Completed
Push — master ( 64cd9f...411f65 )
by Alexandre
02:13
created

AuthorizationEndpoint::getResourceOwner()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 3
cp 0
crap 2
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: Alexandre
5
 * Date: 18/02/2018
6
 * Time: 18:14
7
 */
8
9
namespace OAuth2\Endpoints;
10
11
12
use GuzzleHttp\Psr7\Response;
13
use GuzzleHttp\Psr7\Uri;
14
use OAuth2\Exceptions\OAuthException;
15
use OAuth2\ResponseModes\ResponseModeInterface;
16
use OAuth2\ResponseModes\ResponseModeManager;
17
use OAuth2\ResponseTypes\ResponseTypeInterface;
18
use OAuth2\ResponseTypes\ResponseTypeManager;
19
use OAuth2\Roles\Clients\RegisteredClient;
20
use OAuth2\Roles\ResourceOwnerInterface;
21
use OAuth2\ScopePolicy\ScopePolicyManager;
22
use OAuth2\Storages\ClientStorageInterface;
23
use Psr\Http\Message\ResponseInterface;
24
use Psr\Http\Message\ServerRequestInterface;
25
26
class AuthorizationEndpoint implements EndpointInterface
27
{
28
    /**
29
     * @var ResponseTypeManager
30
     */
31
    protected $responseTypeManager;
32
    /**
33
     * @var ResponseTypeInterface|null
34
     */
35
    private $responseType;
36
    /**
37
     * @var ResponseModeManager
38
     */
39
    protected $responseModeManager;
40
    /**
41 4
     * @var ScopePolicyManager
42
     */
43
    private $scopePolicyManager;
44
    /**
45
     * @var ResponseModeInterface|null
46
     */
47 4
    private $responseMode;
48 4
    /**
49 1
     * @var ResourceOwnerInterface
50
     */
51
    private $resourceOwner;
52
    /**
53
     * @var ClientStorageInterface
54
     */
55
    private $clientStorage;
56
    /**
57
     * @var RegisteredClient|null
58
     */
59
    private $client;
60 3
    /**
61
     * @var Uri|null
62 3
     */
63
    private $redirectUri;
64
    /**
65
     * @var mixed|null
66 3
     */
67
    private $state;
68
    /**
69
     * @var array|null
70
     */
71
    private $scopes;
72 3
73 3
    public function __construct(ResponseTypeManager $responseTypeManager,
74
                                ResponseModeManager $responseModeManager,
75
                                ScopePolicyManager $scopePolicyManager,
76
                                ResourceOwnerInterface $resourceOwner,
77
                                ClientStorageInterface $clientStorage)
78
    {
79 3
        $this->responseTypeManager = $responseTypeManager;
80 3
        $this->responseModeManager = $responseModeManager;
81
        $this->scopePolicyManager = $scopePolicyManager;
82 3
        $this->resourceOwner = $resourceOwner;
83
        $this->clientStorage = $clientStorage;
84
    }
85
86
    function handleRequest(ServerRequestInterface $request): ResponseInterface
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
87
    {
88
        $requestData = $request->getMethod() == 'GET' ? $request->getQueryParams() : $request->getParsedBody();
89
90
        try {
91 3
            // Authentication Request Validation
92
            // The Authorization Server MUST validate all the OAuth 2.0 parameters according to the OAuth 2.0 specification.
93
            $this->verifyRequestData($requestData);
0 ignored issues
show
Bug introduced by
It seems like $requestData can also be of type object and null; however, parameter $requestData of OAuth2\Endpoints\Authori...nt::verifyRequestData() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
            $this->verifyRequestData(/** @scrutinizer ignore-type */ $requestData);
Loading history...
94
95 3
        } catch (OAuthException $e) {
96 2
            /**
97 2
             * If the Authorization Server encounters any error, it MUST return an error response, per Section 3.1.2.6.
98
             */
99
100 1
            return new Response(400, ['content-type' => 'application/json'], $e->jsonSerialize());
101
        }
102
103 3
        // Authorization Server Authenticates End-User
104
        if (!$this->resourceOwner->isAuthenticated()) {
105
            return $this->resourceOwner->authenticate();
106
        }
107
108
        try {
109
            $consentGiven = $this->resourceOwner->hasGivenConsent($this->getClient(), $this->getScopes());
110
            if (is_null($consentGiven)) {
111
                return $this->resourceOwner->obtainConsent($this->getClient(), $this->getScopes());
112 4
            }
113
114 4
            if (empty($consentGiven)) {
115
                throw new OAuthException('access_denied', 'The resource owner denied the request.',
116 4
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
117
            }
118
119
            $responseData = $this->getResponseType()->handleAuthorizationRequest($this, $requestData);
120
        } catch (OAuthException $e) {
121
            /**
122
             * If the Authorization Server encounters any error, it MUST return an error response, per Section 3.1.2.6.
123
             */
124 4
            $responseData = [
125
                'error' => $e->getError()
126
            ];
127
            if($e->getErrorDescription()) {
128
                $responseData['error_description'] = $e->getErrorDescription();
129
            }
130
            if($e->getErrorUri()) {
131
                $responseData['error_uri'] = $e->getErrorUri();
132
            }
133
        }
134
135 4
        if(!empty($this->state)) {
136
            $responseData['state'] = $this->state;
137 4
        }
138 4
139
        return $this->getResponseMode()->buildResponse($this, $requestData, $responseData);
140
    }
141
142
143
    /**
144
     * @param array $requestData
145 4
     * @throws OAuthException
146
     */
147
    protected function verifyRequestData(array $requestData)
148
    {
149
        // response_type required
150
        if (empty($requestData['response_type'])) {
151
            throw new OAuthException('invalid_request', 'The request is missing the required parameter response_type.',
152
                'https://tools.ietf.org/html/rfc6749#section-4.1');
153
        }
154 4
155
        if (!($responseType = $this->responseTypeManager->getResponseType($requestData['response_type']))) {
156
            throw new OAuthException('invalid_request', 'The request includes the invalid parameter response_type.',
157
                'https://tools.ietf.org/html/rfc6749#section-4.1');
158
        }
159
        $this->responseType = $responseType;
160
161
        if (empty($requestData['client_id'])) {
162
            throw new OAuthException('invalid_request', 'The request is missing the required parameter client_id.',
163
                'https://tools.ietf.org/html/rfc6749#section-4.1');
164 4
        }
165
166
        if (!($client = $this->clientStorage->get($requestData['client_id']))) {
167
            throw new OAuthException('invalid_request', 'The request includes the invalid parameter client_id.',
168
                'https://tools.ietf.org/html/rfc6749#section-4.1');
169
        }
170
        $this->client = $client;
171 4
172 4
        $supportedResponseTypes = $this->client->getMetadata()->getResponseTypes() ?: ['code'];
173
        foreach (explode(' ', $requestData['response_type']) as $responseType) {
174
            if(!in_array($responseType, $supportedResponseTypes)) {
175
            throw new OAuthException('unsupported_response_type',
176
                'The authorization server does not support obtaining an authorization code using this method.',
177
                'https://tools.ietf.org/html/rfc6749#section-4.1');
178
            }
179 4
        }
180 4
181 4
        $this->verifyRedirectUri($requestData['redirect_uri'] ?? null);
182 4
183 4
        $this->verifyScope($requestData['scope'] ?? null);
184 4
185 4
        $this->state = $requestData['state'] ?? null;
186 4
187 1
        $responseModeIdentifier = $requestData['response_mode'] ?? $this->getResponseType()->getDefaultResponseMode();
188 1
        if (!($responseMode = $this->responseModeManager->getResponseMode($responseModeIdentifier))) {
189 1
            throw new OAuthException('invalid_request', 'response_mode invalid');
190
        }
191
192 3
        if (in_array($responseModeIdentifier, $this->getResponseType()->getUnsupportedResponseModes())) {
193 3
            throw new OAuthException('invalid_request', 'response_mode unsupported');
194 3
        }
195 3
        $this->responseMode = $responseMode;
196
    }
197
198
    /**
199
     * @param null|string $redirectUri
200
     * @throws OAuthException
201
     */
202
    protected function verifyRedirectUri(?string $redirectUri = null)
203 3
    {
204 3
        $redirectUris = $this->getClient()->getMetadata()->getRedirectUris();
205
        if ($redirectUri) {
206 3
            if (!in_array($redirectUri, $redirectUris)) {
207 1
                throw new OAuthException('invalid_request', 'The request includes the invalid parameter redirect_uri.',
208
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
209
            }
210 3
        }
211 3
        else {
212
            if (count($redirectUris) == 1) {
213
               $redirectUri = $redirectUris[0];
214
            }
215 3
            else {
216
                throw new OAuthException('invalid_request', 'The request is missing the required parameter redirect_uri.',
217
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
218
            }
219
        }
220
        try {
221 3
            $redirectUri = new Uri($redirectUri);
222
            if($redirectUri->getFragment()) {
223 3
                throw new \InvalidArgumentException('The endpoint URI must not include a fragment component.');
224 3
            }
225
            $this->redirectUri = $redirectUri;
226
        }
227
        catch (\InvalidArgumentException $e) {
228 3
            throw new OAuthException('invalid_request', 'The request includes the malformed parameter redirect_uri. '.$e->getMessage(),
229
                'https://tools.ietf.org/html/rfc6749#section-4.1');
230
        }
231 1
    }
232 1
233 1
    /**
234 1
     * @param null|string $scope
235
     * @throws OAuthException
236
     */
237 3
    protected function verifyScope(?string $scope = null) {
238
        $scopes = $this->scopePolicyManager->getScopes($this->getClient(), $scope);
239
        $this->scopePolicyManager->verifyScopes($this->getClient(), $scopes);
240
        $this->scopes = $scopes;
241
    }
242
243
    /**
244
     * @return null|ResponseTypeInterface
245
     */
246
    public function getResponseType(): ?ResponseTypeInterface
247
    {
248
        return $this->responseType;
249
    }
250
251
    /**
252
     * @return null|ResponseModeInterface
253
     */
254
    public function getResponseMode(): ?ResponseModeInterface
255
    {
256
        return $this->responseMode;
257
    }
258
259
    /**
260
     * @return null|RegisteredClient
261
     */
262
    public function getClient(): ?RegisteredClient
263 4
    {
264
        return $this->client;
265 4
    }
266
267
    /**
268
     * @return Uri|null
269
     */
270
    public function getRedirectUri(): ?Uri
271
    {
272
        return $this->redirectUri;
273
    }
274
275
    /**
276
     * @return null|mixed
277 4
     */
278 4
    public function getState()
279
    {
280
        return $this->state;
281 4
    }
282
283
    /**
284
     * @return null|array
285
     */
286
    public function getScopes(): ?array
287
    {
288 4
        return $this->scopes;
289 4
    }
290 4
291 4
    /**
292 4
     * @return ResourceOwnerInterface
293 4
     */
294
    public function getResourceOwner(): ResourceOwnerInterface
295
    {
296
        return $this->resourceOwner;
297
    }
298
}