Passed
Push — master ( e88490...575397 )
by Alexandre
04:15
created

AuthorizationEndpoint::verifyResourceOwner()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 1
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 0
crap 6
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 string|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
    public function handleRequest(ServerRequestInterface $request): ResponseInterface
87
    {
88
        if ($request->getMethod() === 'GET') {
89
            $requestData = $request->getQueryParams();
90
        } else if ($request->getMethod() === 'POST') {
91 3
            $requestData = is_array($request->getParsedBody()) ? $request->getParsedBody() : [];
92
        } else {
93
            return new Response(404);
94
        }
95 3
96 2
        try {
97 2
            $this->verifyClient($requestData['client_id'] ?? null);
98
            $this->verifyRedirectUri($requestData['redirect_uri'] ?? null);
99
100 1
        } catch (OAuthException $e) {
101
            return new Response(400, ['content-type' => 'application/json'], $e->jsonSerialize());
102
        }
103 3
104
        try {
105
            $this->verifyRequestData($requestData);
106
            $this->responseType->verifyAuthorizationRequest($this, $requestData);
0 ignored issues
show
Bug introduced by
The method verifyAuthorizationRequest() does not exist on null. ( Ignorable by Annotation )

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

106
            $this->responseType->/** @scrutinizer ignore-call */ 
107
                                 verifyAuthorizationRequest($this, $requestData);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
107
108
            // Authorization Server Authenticates End-User
109
            if($response = $this->verifyResourceOwner()) {
110
                return $response;
111
            }
112 4
113
            if($response = $this->verifyConsent()) {
114 4
                return $response;
115
            }
116 4
117
            $responseData = $this->getResponseType()->handleAuthorizationRequest($this, $requestData);
118
        } catch (OAuthException $e) {
119
            /**
120
             * If the Authorization Server encounters any error, it MUST return an error response, per Section 3.1.2.6.
121
             */
122
            $responseData = [
123
                'error' => $e->getError()
124 4
            ];
125
            if ($e->getErrorDescription()) {
126
                $responseData['error_description'] = $e->getErrorDescription();
127
            }
128
            if ($e->getErrorUri()) {
129
                $responseData['error_uri'] = $e->getErrorUri();
130
            }
131
        }
132
133
        if (!empty($this->state)) {
134
            $responseData['state'] = $this->state;
135 4
        }
136
137 4
        return $this->getResponseMode()->buildResponse($this, $requestData, $responseData);
138 4
    }
139
140
    protected function verifyResourceOwner(): ?ResponseInterface {
141
        if (!$this->resourceOwner->isAuthenticated()) {
142
            return $this->resourceOwner->authenticate();
143
        }
144
        return null;
145 4
    }
146
147
    /**
148
     * @return null|ResponseInterface
149
     * @throws OAuthException
150
     */
151
    protected function verifyConsent(): ?ResponseInterface {
152
        $consentGiven = $this->resourceOwner->hasGivenConsent($this->getClient(), $this->getScopes());
153
        if (is_null($consentGiven)) {
154 4
            return $this->resourceOwner->obtainConsent($this->getClient(), $this->getScopes());
155
        }
156
157
        if (empty($consentGiven)) {
158
            throw new OAuthException('access_denied', 'The resource owner denied the request.',
159
                'https://tools.ietf.org/html/rfc6749#section-4.1');
160
        }
161
162
        return null;
163
    }
164 4
165
    /**
166
     * @param null|string $clientId
167
     * @throws OAuthException
168
     */
169
    protected function verifyClient(?string $clientId = null) {
170
        if (empty($clientId)) {
171 4
            throw new OAuthException('invalid_request', 'The request is missing the required parameter client_id.',
172 4
                'https://tools.ietf.org/html/rfc6749#section-4.1');
173
        }
174
175
        if (!($client = $this->clientStorage->get($clientId))) {
176
            throw new OAuthException('invalid_request', 'The request includes the invalid parameter client_id.',
177
                'https://tools.ietf.org/html/rfc6749#section-4.1');
178
        }
179 4
        $this->client = $client;
180 4
    }
181 4
182 4
    /**
183 4
     * @param array $requestData
184 4
     * @throws OAuthException
185 4
     */
186 4
    protected function verifyRequestData(array $requestData)
187 1
    {
188 1
        // set the default response in case of invalid response type
189 1
190
        $this->responseMode =  $this->responseModeManager->getDefaultResponseMode();
191
192 3
        // response_type required
193 3
        if (empty($requestData['response_type'])) {
194 3
            throw new OAuthException('invalid_request', 'The request is missing the required parameter response_type.',
195 3
                'https://tools.ietf.org/html/rfc6749#section-4.1');
196
        }
197
198
        if (!($responseType = $this->responseTypeManager->getResponseType($requestData['response_type']))) {
199
            throw new OAuthException('invalid_request', 'The request includes the invalid parameter response_type.',
200
                'https://tools.ietf.org/html/rfc6749#section-4.1');
201
        }
202
        $this->responseType = $responseType;
203 3
204 3
        $supportedResponseTypes = $this->client->getMetadata()->getResponseTypes() ?: ['code'];
0 ignored issues
show
Bug introduced by
The method getMetadata() does not exist on null. ( Ignorable by Annotation )

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

204
        $supportedResponseTypes = $this->client->/** @scrutinizer ignore-call */ getMetadata()->getResponseTypes() ?: ['code'];

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
205
        foreach (explode(' ', $requestData['response_type']) as $responseType) {
206 3
            if (!in_array($responseType, $supportedResponseTypes)) {
207 1
                throw new OAuthException('unsupported_response_type',
208
                    'The authorization server does not support obtaining an authorization code using this method.',
209
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
210 3
            }
211 3
        }
212
213
        $this->verifyScope($requestData['scope'] ?? null);
214
215 3
        $this->state = $requestData['state'] ?? null;
216
217
        $responseModeIdentifier = $requestData['response_mode'] ?? $this->getResponseType()->getDefaultResponseMode();
218
        if (!($responseMode = $this->responseModeManager->getResponseMode($responseModeIdentifier))) {
219
            throw new OAuthException('invalid_request', 'response_mode invalid');
220
        }
221 3
222
        if (in_array($responseModeIdentifier, $this->getResponseType()->getUnsupportedResponseModes())) {
223 3
            throw new OAuthException('invalid_request', 'response_mode unsupported');
224 3
        }
225
226
        $this->responseMode = $responseMode;
227
    }
228 3
229
    /**
230
     * @param null|string $redirectUri
231 1
     * @throws OAuthException
232 1
     */
233 1
    protected function verifyRedirectUri(?string $redirectUri = null)
234 1
    {
235
        $redirectUris = $this->getClient()->getMetadata()->getRedirectUris();
236
        if ($redirectUri) {
237 3
            if (!in_array($redirectUri, $redirectUris)) {
238
                throw new OAuthException('invalid_request', 'The request includes the invalid parameter redirect_uri.',
239
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
240
            }
241
        } else {
242
            if (count($redirectUris) == 1) {
243
                $redirectUri = $redirectUris[0];
244
            } else {
245
                throw new OAuthException('invalid_request', 'The request is missing the required parameter redirect_uri.',
246
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
247
            }
248
        }
249
250
        try {
251
            $redirectUri = new Uri($redirectUri);
252
            if ($redirectUri->getFragment()) {
253
                throw new \InvalidArgumentException('The endpoint URI must not include a fragment component.');
254
            }
255
256
            $this->redirectUri = $redirectUri;
257
        } catch (\InvalidArgumentException $e) {
258
            throw new OAuthException('invalid_request', 'The request includes the malformed parameter redirect_uri. ' . $e->getMessage(),
259
                'https://tools.ietf.org/html/rfc6749#section-4.1');
260
        }
261
    }
262
263 4
    /**
264
     * @param null|string $scope
265 4
     * @throws OAuthException
266
     */
267
    protected function verifyScope(?string $scope = null)
268
    {
269
        $scopes = $this->scopePolicyManager->getScopes($this->getClient(), $scope);
270
        $this->scopePolicyManager->verifyScopes($this->getClient(), $scopes);
271
        $this->scopes = $scopes;
272
    }
273
274
    /**
275
     * @return null|ResponseTypeInterface
276
     */
277 4
    public function getResponseType(): ?ResponseTypeInterface
278 4
    {
279
        return $this->responseType;
280
    }
281 4
282
    /**
283
     * @return null|ResponseModeInterface
284
     */
285
    public function getResponseMode(): ?ResponseModeInterface
286
    {
287
        return $this->responseMode;
288 4
    }
289 4
290 4
    /**
291 4
     * @return null|RegisteredClient
292 4
     */
293 4
    public function getClient(): ?RegisteredClient
294
    {
295
        return $this->client;
296
    }
297
298
    /**
299 4
     * @return Uri|null
300 4
     */
301 4
    public function getRedirectUri(): ?Uri
302 4
    {
303
        return $this->redirectUri;
304
    }
305
306
    /**
307
     * @return null|string
308
     */
309
    public function getState(): ?string
310 4
    {
311
        return $this->state;
312
    }
313
314 4
    /**
315
     * @return null|array
316
     */
317
    public function getScopes(): ?array
318
    {
319
        return $this->scopes;
320
    }
321
322
    /**
323
     * @return ResourceOwnerInterface
324
     */
325
    public function getResourceOwner(): ResourceOwnerInterface
326
    {
327
        return $this->resourceOwner;
328
    }
329
}