Passed
Push — master ( cd0cec...4b3abd )
by Alexandre
06:20
created

AuthorizationEndpoint::getState()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.037

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 2
cts 3
cp 0.6667
crap 1.037
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
        // Authentication Request Validation
97 2
        // The Authorization Server MUST validate all the OAuth 2.0 parameters according to the OAuth 2.0 specification.
98
99
        try {
100 1
            $this->verifyClient($requestData['client_id'] ?? null);
101
            $this->verifyRedirectUri($requestData['redirect_uri'] ?? null);
102
103 3
        } catch (OAuthException $e) {
104
            /**
105
             * If the Authorization Server encounters any error, it MUST return an error response, per Section 3.1.2.6.
106
             */
107
108
            return new Response(400, ['content-type' => 'application/json'], $e->jsonSerialize());
109
        }
110
111
        try {
112 4
            $this->verifyRequestData($requestData);
113
            $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

113
            $this->responseType->/** @scrutinizer ignore-call */ 
114
                                 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...
114 4
115
            // Authorization Server Authenticates End-User
116 4
            if (!$this->resourceOwner->isAuthenticated()) {
117
                return $this->resourceOwner->authenticate();
118
            }
119
120
            $consentGiven = $this->resourceOwner->hasGivenConsent($this->getClient(), $this->getScopes());
121
            if (is_null($consentGiven)) {
122
                return $this->resourceOwner->obtainConsent($this->getClient(), $this->getScopes());
123
            }
124 4
125
            if (empty($consentGiven)) {
126
                throw new OAuthException('access_denied', 'The resource owner denied the request.',
127
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
128
            }
129
130
            $responseData = $this->getResponseType()->handleAuthorizationRequest($this, $requestData);
131
        } catch (OAuthException $e) {
132
            /**
133
             * If the Authorization Server encounters any error, it MUST return an error response, per Section 3.1.2.6.
134
             */
135 4
            $responseData = [
136
                'error' => $e->getError()
137 4
            ];
138 4
            if ($e->getErrorDescription()) {
139
                $responseData['error_description'] = $e->getErrorDescription();
140
            }
141
            if ($e->getErrorUri()) {
142
                $responseData['error_uri'] = $e->getErrorUri();
143
            }
144
        }
145 4
146
        if (!empty($this->state)) {
147
            $responseData['state'] = $this->state;
148
        }
149
150
        return $this->getResponseMode()->buildResponse($this, $requestData, $responseData);
151
    }
152
153
    /**
154 4
     * @param null|string $clientId
155
     * @throws OAuthException
156
     */
157
    protected function verifyClient(?string $clientId = null) {
158
        if (empty($clientId)) {
159
            throw new OAuthException('invalid_request', 'The request is missing the required parameter client_id.',
160
                'https://tools.ietf.org/html/rfc6749#section-4.1');
161
        }
162
163
        if (!($client = $this->clientStorage->get($clientId))) {
164 4
            throw new OAuthException('invalid_request', 'The request includes the invalid parameter client_id.',
165
                'https://tools.ietf.org/html/rfc6749#section-4.1');
166
        }
167
        $this->client = $client;
168
    }
169
170
    /**
171 4
     * @param array $requestData
172 4
     * @throws OAuthException
173
     */
174
    protected function verifyRequestData(array $requestData)
175
    {
176
        // response_type required
177
        if (empty($requestData['response_type'])) {
178
            throw new OAuthException('invalid_request', 'The request is missing the required parameter response_type.',
179 4
                'https://tools.ietf.org/html/rfc6749#section-4.1');
180 4
        }
181 4
182 4
        if (!($responseType = $this->responseTypeManager->getResponseType($requestData['response_type']))) {
183 4
            throw new OAuthException('invalid_request', 'The request includes the invalid parameter response_type.',
184 4
                'https://tools.ietf.org/html/rfc6749#section-4.1');
185 4
        }
186 4
        $this->responseType = $responseType;
187 1
188 1
        $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

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