Completed
Push — master ( 6e52f0...d9a404 )
by Alexandre
02:29
created

Authorization   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 274
ccs 0
cts 130
cp 0
rs 8.8
c 0
b 0
f 0
wmc 36

3 Methods

Rating   Name   Duplication   Size   Complexity  
D authorize() 0 82 15
C checkClientRedirectUri() 0 64 20
A __construct() 0 6 1
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: Alexandre
5
 * Date: 30/12/2017
6
 * Time: 18:49
7
 */
8
9
namespace OAuth2OLD\Endpoint\Server;
10
11
use GuzzleHttp\Psr7\Response;
12
use GuzzleHttp\Psr7\Uri;
13
use OAuth2OLD\Credential\AuthorizationCode;
14
use OAuth2OLD\Endpoint\Config;
15
use OAuth2OLD\Endpoint\Endpoint;
16
use OAuth2OLD\Endpoint\Server\Messages\Authorization\AuthorizationRequest as AuthorizationRequest;
17
use OAuth2OLD\Endpoint\Server\Messages\Authorization\AuthorizationResponse as AuthorizationResponse;
18
use OAuth2OLD\Endpoint\Server\Messages\Authorization\ErrorResponse;
19
use OAuth2OLD\Exception\MissingResourceOwnerDecision;
20
use OAuth2OLD\Exception\ResourceOwnerNotAuthenticatedException;
0 ignored issues
show
Bug introduced by
The type OAuth2OLD\Exception\Reso...tAuthenticatedException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use OAuth2OLD\ResponseType\ResponseTypeInterface;
22
use OAuth2OLD\Storage\ClientStorage;
23
use OAuth2OLD\Role\Client\RegisteredClient;
24
use OAuth2OLD\Role\Client\Type\ClientPassword;
0 ignored issues
show
Bug introduced by
The type OAuth2OLD\Role\Client\Type\ClientPassword was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use OAuth2OLD\Role\Client\Type\PublicClient;
26
use OAuth2OLD\Role\Client\WebApplication;
27
use OAuth2OLD\Role\ResourceOwner;
28
29
30
/**
31
 * @deprecated
32
 * Class Authorization
33
 * @package OAuth2\endpoints
34
 *
35
 * @see https://tools.ietf.org/html/rfc6749#section-3.1
36
 *
37
 * Authorization Endpoint
38
 *
39
 *     The authorization endpoint is used to interact with the resource
40
 * owner and obtain an authorization grant.  The authorization server
41
 * MUST first verify the identity of the resource owner.  The way in
42
 * which the authorization server authenticates the resource owner
43
 * (e.g., username and password login, session cookies) is beyond the
44
 * scope of this specification.
45
 *
46
 * The means through which the client obtains the location of the
47
 * authorization endpoint are beyond the scope of this specification,
48
 * but the location is typically provided in the service documentation.
49
 *
50
 * The endpoint URI MAY include an "application/x-www-form-urlencoded"
51
 * formatted (per Appendix B) query component ([RFC3986] Section 3.4),
52
 * which MUST be retained when adding additional query parameters.  The
53
 * endpoint URI MUST NOT include a fragment component.
54
 *
55
 * Since requests to the authorization endpoint result in user
56
 * authentication and the transmission of clear-text credentials (in the
57
 * HTTP response), the authorization server MUST require the use of TLS
58
 * as described in Section 1.6 when sending requests to the
59
 * authorization endpoint.
60
 *
61
 * The authorization server MUST support the use of the HTTP "GET"
62
 * method [RFC2616] for the authorization endpoint and MAY support the
63
 * use of the "POST" method as well.
64
 *
65
 * Parameters sent without a value MUST be treated as if they were
66
 * omitted from the request.  The authorization server MUST ignore
67
 * unrecognized request parameters.  Request and response parameters
68
 * MUST NOT be included more than once.
69
 */
70
class Authorization extends Endpoint
71
{
72
    /**
73
     * @var array
74
     *
75
     * @see https://tools.ietf.org/html/rfc6749#section-3.1.1
76
     *
77
     * Response Type
78
     *
79
     *     The authorization endpoint is used by the authorization code grant
80
     * type and implicit grant type flows.  The client informs the
81
     * authorization server of the desired grant type using the following
82
     * parameter:
83
     *
84
     * response_type
85
     * REQUIRED.  The value MUST be one of "code" for requesting an
86
     * authorization code as described by Section 4.1.1, "token" for
87
     * requesting an access token (implicit grant) as described by
88
     * Section 4.2.1, or a registered extension value as described by
89
     * Section 8.4.
90
     *
91
     * Extension response types MAY contain a space-delimited (%x20) list of
92
     * values, where the order of values does not matter (e.g., response
93
     * type "a b" is the same as "b a").  The meaning of such composite
94
     * response types is defined by their respective specifications.
95
     *
96
     * If an authorization request is missing the "response_type" parameter,
97
     * or if the response type is not understood, the authorization server
98
     * MUST return an error response as described in Section 4.1.2.1.
99
     */
100
    protected $responseTypesRegistry;
101
    /**
102
     * @var Server
103
     */
104
    private $server;
105
106
    /**
107
     * Authorization constructor.
108
     * @param Server $server
109
     */
110
    public function __construct(Server $server)
111
    {
112
        $this->responseTypesRegistry = [
113
            'code' => 'class'
114
        ];
115
        $this->server = $server;
116
    }
117
118
119
    /**
120
     * @see https://tools.ietf.org/html/rfc6749#section-3.3
121
     *
122
     * Access Token Scope
123
     *
124
     *     The authorization and token endpoints allow the client to specify the
125
     * scope of the access request using the "scope" request parameter.  In
126
     * turn, the authorization server uses the "scope" response parameter to
127
     * inform the client of the scope of the access token issued.
128
     *
129
     * The value of the scope parameter is expressed as a list of space-
130
     * delimited, case-sensitive strings.  The strings are defined by the
131
     * authorization server.  If the value contains multiple space-delimited
132
     * strings, their order does not matter, and each string adds an
133
     * additional access range to the requested scope.
134
     *
135
     * scope       = scope-token *( SP scope-token )
136
     * scope-token = 1*( %x21 / %x23-5B / %x5D-7E )
137
     *
138
     * The authorization server MAY fully or partially ignore the scope
139
     * requested by the client, based on the authorization server policy or
140
     * the resource owner's instructions.  If the issued access token scope
141
     * is different from the one requested by the client, the authorization
142
     * server MUST include the "scope" response parameter to inform the
143
     * client of the actual scope granted.
144
     *
145
     * If the client omits the scope parameter when requesting
146
     * authorization, the authorization server MUST either process the
147
     * request using a pre-defined default value or fail the request
148
     * indicating an invalid scope.  The authorization server SHOULD
149
     * document its scope requirements and default value (if defined).
150
     *
151
     *
152
     * @see https://tools.ietf.org/html/rfc6749#section-10.12
153
     *
154
     *  The authorization server MUST implement CSRF protection for its
155
     * authorization endpoint and ensure that a malicious client cannot
156
     * obtain authorization without the awareness and explicit consent of
157
     * the resource owner.
158
     *
159
     * @param AuthorizationRequest $request
160
     * @param ResourceOwner $resourceOwner
161
     * @param bool|null $authorizationDecision
162
     * @return Response
163
     * @throws MissingResourceOwnerDecision
164
     * @throws ResourceOwnerNotAuthenticatedException
165
     * @throws \Exception
166
     */
167
    public function authorize(AuthorizationRequest $request, ResourceOwner $resourceOwner,
168
                              ?bool $authorizationDecision = null)
169
    {
170
        $client = $this->server->getClientStorage()->getByIdentifier($request->getClientId());
171
        $redirectUri = $request->getRedirectUri();
172
173
        try {
174
            $isImplicitResponseType = false;
175
            $responseTypes = [];
176
            foreach (explode(' ', $request->getResponseType()) as $responseTypeRequested) {
177
                $responseType = $this->server->getResponseType($responseTypeRequested);
178
                if(!$responseType) {
179
                    return new ErrorResponse($redirectUri, 'unsupported_response_type', 'Invalid response_type parameter',
180
                        'https://tools.ietf.org/html/rfc6749#section-3.1.1');
181
                }
182
183
                if($responseType->isImplicit()) {
184
                    $isImplicitResponseType = true;
185
                }
186
187
                $responseTypes[] = $responseType;
188
            }
189
190
            $redirectUri = $this->checkClientRedirectUri($client, $redirectUri, $isImplicitResponseType);
191
192
            $redirectUri = Uri::withoutQueryValue($redirectUri, 'response_type');
193
            $redirectUri = Uri::withoutQueryValue($redirectUri, 'client_id');
194
            $redirectUri = Uri::withoutQueryValue($redirectUri, 'redirect_uri');
195
            $redirectUri = Uri::withoutQueryValue($redirectUri, 'scope');
196
            $redirectUri = Uri::withoutQueryValue($redirectUri, 'state');
197
        } catch (\Exception $e) {
198
            return new Response(302, ['Location' => $this->server->getConfig(Config::INVALID_ENDPOINT_URI)]);
199
        }
200
201
        try {
202
            $request->validate();
203
        } catch (\Exception $e) {
204
            return new ErrorResponse($redirectUri, 'invalid_request', $e->getMessage(),
205
                'https://tools.ietf.org/html/rfc6749#section-4.1.1');
206
        }
207
208
209
        if ($this->server->getConfig(Config::ENFORCE_STATE) && !$request->getState()) {
210
            return new ErrorResponse($redirectUri, 'invalid_request', 'Missing state parameter',
211
                'https://tools.ietf.org/html/rfc6749#section-4.1.1');
212
        }
213
214
        if (!$resourceOwner->isAuthenticated()) {
215
            throw new ResourceOwnerNotAuthenticatedException();
216
        }
217
218
        if (is_null($authorizationDecision)) {
219
            throw new MissingResourceOwnerDecision();
220
        }
221
222
        if (!$authorizationDecision) {
223
            return new ErrorResponse($redirectUri, 'access_denied', 'The resource owner server denied the request',
224
                'https://tools.ietf.org/html/rfc6749#section-4.1.1');
225
        }
226
227
        /**
228
         * @var ResponseTypeInterface $responseType
229
         */
230
        $params = array();
231
        foreach ($responseTypes as $responseType) {
232
            $params = array_merge($responseType->getResponse($client, $redirectUri));
233
        }
234
235
        if ($request->getState()) {
236
            $params['state'] = $request->getState();
237
        }
238
239
        if($isImplicitResponseType) {
240
            $redirectUri = $redirectUri->withFragment(http_build_query($params));
241
        }
242
        else {
243
            foreach ($params as $key => $value) {
244
                $redirectUri = Uri::withQueryValue($redirectUri, $key, $value);
245
            }
246
        }
247
248
        return new Response(302, ['Location' => $redirectUri->__toString()]);
249
250
//        $code = AuthorizationCode::generate();
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
251
//        $authorizationCode = new AuthorizationCode($code, $client->getIdentifier(), $redirectUri);
252
//        $this->server->getAuthorizationCodeStorage()->create($authorizationCode);
253
//        return new AuthorizationResponse($redirectUri, $authorizationCode, $request->getState());
254
    }
255
256
    /**
257
     * @see https://tools.ietf.org/html/rfc6749#section-3.1.2.3
258
     *
259
     * Dynamic Configuration
260
     *
261
     *     If multiple redirection URIs have been registered, if only part of
262
     * the redirection URI has been registered, or if no redirection URI has
263
     * been registered, the client MUST include a redirection URI with the
264
     * authorization request using the "redirect_uri" request parameter.
265
     *
266
     * When a redirection URI is included in an authorization request, the
267
     * authorization server MUST compare and match the value received
268
     * against at least one of the registered redirection URIs (or URI
269
     * components) as defined in [RFC3986] Section 6, if any redirection
270
     * URIs were registered.  If the client registration included the full
271
     * redirection URI, the authorization server MUST compare the two URIs
272
     * using simple string comparison as defined in [RFC3986] Section 6.2.1
273
     *
274
     * @param RegisteredClient $client
275
     * @param null|string $redirectUri
276
     * @param bool $isImplicitGrant
277
     * @return Uri|null|string
278
     * @throws \Exception
279
     */
280
    private function checkClientRedirectUri(RegisteredClient $client, ?string $redirectUri, $isImplicitGrant = false)
281
    {
282
        if (!$client instanceof RegisteredClient) {
0 ignored issues
show
introduced by
The condition ! $client instanceof OAu...Client\RegisteredClient can never be true.
Loading history...
283
            throw new \Exception('Only registered clients are supported');
284
        }
285
        if (!$client instanceof PublicClient && !$client instanceof ClientPassword) {
286
            throw new \Exception('Only public and confidential clients are supported');
287
        }
288
289
        if (!$redirectUri && $this->server->getConfig(Config::ENFORCE_REDIRECT_URI)) {
290
            throw new \Exception('Invalid URI');
291
        }
292
293
        if (!$redirectUri) {
294
            if (count($client->getRedirectUris()) !== 1) {
295
                throw new \Exception('Invalid URI');
296
            }
297
            $redirectUri = new Uri($client->getRedirectUris()[0]);
298
        } else {
299
            if (!$redirectUri) {
300
                throw new \Exception('Invalid URI');
301
            }
302
303
            $redirectUri = new Uri($redirectUri);
304
            if ($redirectUri->getFragment()) {
305
                throw new \Exception('Invalid URI');
306
            }
307
308
            if (empty($client->getRedirectUris())) {
309
                if ($client instanceof ClientPassword && !$isImplicitGrant) {
310
                    return $redirectUri;
311
                } else {
312
                    throw new \Exception('Invalid URI');
313
                }
314
            }
315
316
            $redirectUriWithoutQuery = Uri::composeComponents(
317
                $redirectUri->getScheme(), $redirectUri->getAuthority(), $redirectUri->getPath(), '', '');
318
319
            $match = false;
320
            foreach ($client->getRedirectUris() as $registeredUri) {
321
                $registeredUri = new Uri($registeredUri);
322
                if ($registeredUri->getQuery()) {
323
                    if ($registeredUri->__toString() === $redirectUri->__toString()) {
324
                        $match = true;
325
                        break;
326
                    }
327
                } else {
328
                    if ($this->server->getConfig(Config::STRICT_REDIRECT_URI_COMPARISON)) {
329
                        if ($registeredUri->__toString() === $redirectUri->__toString()) {
330
                            $match = true;
331
                            break;
332
                        }
333
                    } else if ($registeredUri->__toString() === $redirectUriWithoutQuery) {
334
                        $match = true;
335
                        break;
336
                    }
337
                }
338
            }
339
            if (!$match) {
340
                throw new \Exception('Invalid URI');
341
            }
342
        }
343
        return $redirectUri;
344
    }
345
}