Completed
Push — develop ( a2c65d...283ac2 )
by Kristijan
13s
created

Server   F

Complexity

Total Complexity 151

Size/Duplication

Total Lines 787
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 151
dl 0
loc 787
rs 1.263
c 0
b 0
f 0

43 Methods

Rating   Name   Duplication   Size   Complexity  
A handleTokenRequest() 0 6 2
A setResourceController() 0 3 1
A setConfig() 0 3 1
A handleUserInfoRequest() 0 6 2
A addGrantType() 0 11 3
A getGrantType() 0 3 2
A setAuthorizeController() 0 3 1
A getResponseTypes() 0 3 1
A getScopeUtil() 0 8 3
A getUserInfoController() 0 7 2
A getResponseType() 0 6 2
A getAccessTokenResponseType() 0 11 3
A getResourceController() 0 7 2
A setScopeUtil() 0 3 1
A getClientAssertionType() 0 3 1
A getTokenController() 0 7 2
A getAuthorizeController() 0 7 2
B __construct() 0 41 6
A getTokenType() 0 3 1
A getIdTokenResponseType() 0 7 2
A getDefaultTokenType() 0 5 1
A getConfig() 0 3 2
A setTokenController() 0 3 1
A getStorages() 0 3 1
A handleAuthorizeRequest() 0 6 1
A getResponse() 0 3 1
A getStorage() 0 3 2
A setUserInfoController() 0 3 1
A getGrantTypes() 0 3 1
C createDefaultAuthorizeController() 0 22 7
C getDefaultResponseTypes() 0 33 8
A createDefaultIdTokenResponseType() 0 12 3
A createDefaultAccessTokenResponseType() 0 15 4
C createDefaultTokenController() 0 27 7
C getDefaultGrantTypes() 0 34 8
C addStorage() 0 32 15
C addResponseType() 0 22 8
B createDefaultResourceController() 0 18 6
C createDefaultUserInfoController() 0 22 7
A validateAuthorizeRequest() 0 6 2
A getAccessTokenData() 0 6 2
A grantAccessToken() 0 6 2
A verifyResourceRequest() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like Server often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Server, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace OAuth2;
4
5
use OAuth2\Controller\ResourceControllerInterface;
6
use OAuth2\Controller\ResourceController;
7
use OAuth2\OpenID\Controller\UserInfoControllerInterface;
8
use OAuth2\OpenID\Controller\UserInfoController;
9
use OAuth2\OpenID\Controller\AuthorizeController as OpenIDAuthorizeController;
10
use OAuth2\OpenID\ResponseType\AuthorizationCode as OpenIDAuthorizationCodeResponseType;
11
use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface;
12
use OAuth2\OpenID\GrantType\AuthorizationCode as OpenIDAuthorizationCodeGrantType;
13
use OAuth2\Controller\AuthorizeControllerInterface;
14
use OAuth2\Controller\AuthorizeController;
15
use OAuth2\Controller\TokenControllerInterface;
16
use OAuth2\Controller\TokenController;
17
use OAuth2\ClientAssertionType\ClientAssertionTypeInterface;
18
use OAuth2\ClientAssertionType\HttpBasic;
19
use OAuth2\ResponseType\ResponseTypeInterface;
20
use OAuth2\ResponseType\AuthorizationCode as AuthorizationCodeResponseType;
21
use OAuth2\ResponseType\AccessToken;
22
use OAuth2\ResponseType\JwtAccessToken;
23
use OAuth2\OpenID\ResponseType\CodeIdToken;
24
use OAuth2\OpenID\ResponseType\IdToken;
25
use OAuth2\OpenID\ResponseType\IdTokenToken;
26
use OAuth2\TokenType\TokenTypeInterface;
27
use OAuth2\TokenType\Bearer;
28
use OAuth2\GrantType\GrantTypeInterface;
29
use OAuth2\GrantType\UserCredentials;
30
use OAuth2\GrantType\ClientCredentials;
31
use OAuth2\GrantType\RefreshToken;
32
use OAuth2\GrantType\AuthorizationCode;
33
use OAuth2\Storage\JwtAccessToken as JwtAccessTokenStorage;
34
use OAuth2\Storage\JwtAccessTokenInterface;
35
36
/**
37
* Server class for OAuth2
38
* This class serves as a convience class which wraps the other Controller classes
39
*
40
* @see OAuth2\Controller\ResourceController
41
* @see OAuth2\Controller\AuthorizeController
42
* @see OAuth2\Controller\TokenController
43
*/
44
class Server implements ResourceControllerInterface,
45
    AuthorizeControllerInterface,
46
    TokenControllerInterface,
47
    UserInfoControllerInterface
48
{
49
    // misc properties
50
    protected $response;
51
    protected $config;
52
    protected $storages;
53
54
    // servers
55
    protected $authorizeController;
56
    protected $tokenController;
57
    protected $resourceController;
58
    protected $userInfoController;
59
60
    // config classes
61
    protected $grantTypes;
62
    protected $responseTypes;
63
    protected $tokenType;
64
    protected $scopeUtil;
65
    protected $clientAssertionType;
66
67
    protected $storageMap = array(
68
        'access_token' => 'OAuth2\Storage\AccessTokenInterface',
69
        'authorization_code' => 'OAuth2\Storage\AuthorizationCodeInterface',
70
        'client_credentials' => 'OAuth2\Storage\ClientCredentialsInterface',
71
        'client' => 'OAuth2\Storage\ClientInterface',
72
        'refresh_token' => 'OAuth2\Storage\RefreshTokenInterface',
73
        'user_credentials' => 'OAuth2\Storage\UserCredentialsInterface',
74
        'user_claims' => 'OAuth2\OpenID\Storage\UserClaimsInterface',
75
        'public_key' => 'OAuth2\Storage\PublicKeyInterface',
76
        'jwt_bearer' => 'OAuth2\Storage\JWTBearerInterface',
77
        'scope' => 'OAuth2\Storage\ScopeInterface',
78
    );
79
80
    protected $responseTypeMap = array(
81
        'token' => 'OAuth2\ResponseType\AccessTokenInterface',
82
        'code' => 'OAuth2\ResponseType\AuthorizationCodeInterface',
83
        'id_token' => 'OAuth2\OpenID\ResponseType\IdTokenInterface',
84
        'id_token token' => 'OAuth2\OpenID\ResponseType\IdTokenTokenInterface',
85
        'code id_token' => 'OAuth2\OpenID\ResponseType\CodeIdTokenInterface',
86
    );
87
88
    /**
89
     * @param mixed                                                   $storage             (array or OAuth2\Storage) - single object or array of objects implementing the
90
     *                                                                                     required storage types (ClientCredentialsInterface and AccessTokenInterface as a minimum)
91
     * @param array                                                   $config              specify a different token lifetime, token header name, etc
92
     * @param array                                                   $grantTypes          An array of OAuth2\GrantType\GrantTypeInterface to use for granting access tokens
93
     * @param array                                                   $responseTypes       Response types to use.  array keys should be "code" and and "token" for
94
     *                                                                                     Access Token and Authorization Code response types
95
     * @param OAuth2\TokenType\TokenTypeInterface                     $tokenType           The token type object to use. Valid token types are "bearer" and "mac"
0 ignored issues
show
Bug introduced by
The type OAuth2\OAuth2\TokenType\TokenTypeInterface was not found. Did you mean OAuth2\TokenType\TokenTypeInterface? If so, make sure to prefix the type with \.
Loading history...
96
     * @param OAuth2\ScopeInterface                                   $scopeUtil           The scope utility class to use to validate scope
0 ignored issues
show
Bug introduced by
The type OAuth2\OAuth2\ScopeInterface was not found. Did you mean OAuth2\ScopeInterface? If so, make sure to prefix the type with \.
Loading history...
97
     * @param OAuth2\ClientAssertionType\ClientAssertionTypeInterface $clientAssertionType The method in which to verify the client identity.  Default is HttpBasic
0 ignored issues
show
Bug introduced by
The type OAuth2\OAuth2\ClientAsse...tAssertionTypeInterface was not found. Did you mean OAuth2\ClientAssertionTy...tAssertionTypeInterface? If so, make sure to prefix the type with \.
Loading history...
98
     *
99
     * @ingroup oauth2_section_7
100
     */
101
    public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), TokenTypeInterface $tokenType = null, ScopeInterface $scopeUtil = null, ClientAssertionTypeInterface $clientAssertionType = null)
102
    {
103
        $storage = is_array($storage) ? $storage : array($storage);
104
        $this->storages = array();
105
        foreach ($storage as $key => $service) {
106
            $this->addStorage($service, $key);
107
        }
108
109
        // merge all config values.  These get passed to our controller objects
110
        $this->config = array_merge(array(
111
            'use_jwt_access_tokens'        => false,
112
            'store_encrypted_token_string' => true,
113
            'use_openid_connect'       => false,
114
            'id_lifetime'              => 3600,
115
            'access_lifetime'          => 3600,
116
            'www_realm'                => 'Service',
117
            'token_param_name'         => 'access_token',
118
            'token_bearer_header_name' => 'Bearer',
119
            'enforce_state'            => true,
120
            'require_exact_redirect_uri' => true,
121
            'allow_implicit'           => false,
122
            'allow_credentials_in_request_body' => true,
123
            'allow_public_clients'     => true,
124
            'always_issue_new_refresh_token' => false,
125
            'unset_refresh_token_after_use' => true,
126
        ), $config);
127
128
        foreach ($grantTypes as $key => $grantType) {
129
            $this->addGrantType($grantType, $key);
130
        }
131
132
        foreach ($responseTypes as $key => $responseType) {
133
            $this->addResponseType($responseType, $key);
134
        }
135
136
        $this->tokenType = $tokenType;
137
        $this->scopeUtil = $scopeUtil;
138
        $this->clientAssertionType = $clientAssertionType;
139
140
        if ($this->config['use_openid_connect']) {
141
            $this->validateOpenIdConnect();
142
        }
143
    }
144
145
    public function getAuthorizeController()
146
    {
147
        if (is_null($this->authorizeController)) {
148
            $this->authorizeController = $this->createDefaultAuthorizeController();
149
        }
150
151
        return $this->authorizeController;
152
    }
153
154
    public function getTokenController()
155
    {
156
        if (is_null($this->tokenController)) {
157
            $this->tokenController = $this->createDefaultTokenController();
158
        }
159
160
        return $this->tokenController;
161
    }
162
163
    public function getResourceController()
164
    {
165
        if (is_null($this->resourceController)) {
166
            $this->resourceController = $this->createDefaultResourceController();
167
        }
168
169
        return $this->resourceController;
170
    }
171
172
    public function getUserInfoController()
173
    {
174
        if (is_null($this->userInfoController)) {
175
            $this->userInfoController = $this->createDefaultUserInfoController();
176
        }
177
178
        return $this->userInfoController;
179
    }
180
181
    /**
182
     * every getter deserves a setter
183
     */
184
    public function setAuthorizeController(AuthorizeControllerInterface $authorizeController)
185
    {
186
        $this->authorizeController = $authorizeController;
187
    }
188
189
    /**
190
     * every getter deserves a setter
191
     */
192
    public function setTokenController(TokenControllerInterface $tokenController)
193
    {
194
        $this->tokenController = $tokenController;
195
    }
196
197
    /**
198
     * every getter deserves a setter
199
     */
200
    public function setResourceController(ResourceControllerInterface $resourceController)
201
    {
202
        $this->resourceController = $resourceController;
203
    }
204
205
    /**
206
     * every getter deserves a setter
207
     */
208
    public function setUserInfoController(UserInfoControllerInterface $userInfoController)
209
    {
210
        $this->userInfoController = $userInfoController;
211
    }
212
213
    /**
214
     * Return claims about the authenticated end-user.
215
     * This would be called from the "/UserInfo" endpoint as defined in the spec.
216
     *
217
     * @param $request - OAuth2\RequestInterface
218
     * Request object to grant access token
219
     *
220
     * @param $response - OAuth2\ResponseInterface
221
     * Response object containing error messages (failure) or user claims (success)
222
     *
223
     * @throws InvalidArgumentException
224
     * @throws LogicException
225
     *
226
     * @see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo
227
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
228
    public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response = null)
229
    {
230
        $this->response = is_null($response) ? new Response() : $response;
231
        $this->getUserInfoController()->handleUserInfoRequest($request, $this->response);
232
233
        return $this->response;
234
    }
235
236
    /**
237
     * Grant or deny a requested access token.
238
     * This would be called from the "/token" endpoint as defined in the spec.
239
     * Obviously, you can call your endpoint whatever you want.
240
     *
241
     * @param $request - OAuth2\RequestInterface
242
     * Request object to grant access token
243
     *
244
     * @param $response - OAuth2\ResponseInterface
245
     * Response object containing error messages (failure) or access token (success)
246
     *
247
     * @throws InvalidArgumentException
248
     * @throws LogicException
249
     *
250
     * @see http://tools.ietf.org/html/rfc6749#section-4
251
     * @see http://tools.ietf.org/html/rfc6749#section-10.6
252
     * @see http://tools.ietf.org/html/rfc6749#section-4.1.3
253
     *
254
     * @ingroup oauth2_section_4
255
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment - at position 0 could not be parsed: Unknown type name '-' at position 0 in -.
Loading history...
256
    public function handleTokenRequest(RequestInterface $request, ResponseInterface $response = null)
257
    {
258
        $this->response = is_null($response) ? new Response() : $response;
259
        $this->getTokenController()->handleTokenRequest($request, $this->response);
260
261
        return $this->response;
262
    }
263
264
    public function grantAccessToken(RequestInterface $request, ResponseInterface $response = null)
265
    {
266
        $this->response = is_null($response) ? new Response() : $response;
267
        $value = $this->getTokenController()->grantAccessToken($request, $this->response);
268
269
        return $value;
270
    }
271
272
    /**
273
     * Handle a revoke token request
274
     * This would be called from the "/revoke" endpoint as defined in the draft Token Revocation spec
275
     *
276
     * @see https://tools.ietf.org/html/rfc7009#section-2
277
     *
278
     * @param RequestInterface $request
279
     * @param ResponseInterface $response
280
     * @return Response|ResponseInterface
281
     */
282
    public function handleRevokeRequest(RequestInterface $request, ResponseInterface $response = null)
283
    {
284
        $this->response = is_null($response) ? new Response() : $response;
285
        $this->getTokenController()->handleRevokeRequest($request, $this->response);
286
287
        return $this->response;
288
    }
289
290
    /**
291
     * Redirect the user appropriately after approval.
292
     *
293
     * After the user has approved or denied the resource request the
294
     * authorization server should call this function to redirect the user
295
     * appropriately.
296
     *
297
     * @param $request
298
     * The request should have the follow parameters set in the querystring:
299
     * - response_type: The requested response: an access token, an
300
     * authorization code, or both.
301
     * - client_id: The client identifier as described in Section 2.
302
     * - redirect_uri: An absolute URI to which the authorization server
303
     * will redirect the user-agent to when the end-user authorization
304
     * step is completed.
305
     * - scope: (optional) The scope of the resource request expressed as a
306
     * list of space-delimited strings.
307
     * - state: (optional) An opaque value used by the client to maintain
308
     * state between the request and callback.
309
     * @param $is_authorized
310
     * TRUE or FALSE depending on whether the user authorized the access.
311
     * @param $user_id
312
     * Identifier of user who authorized the client
313
     *
314
     * @see http://tools.ietf.org/html/rfc6749#section-4
315
     *
316
     * @ingroup oauth2_section_4
317
     */
318
    public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null)
319
    {
320
        $this->response = $response;
321
        $this->getAuthorizeController()->handleAuthorizeRequest($request, $this->response, $is_authorized, $user_id);
322
323
        return $this->response;
324
    }
325
326
    /**
327
     * Pull the authorization request data out of the HTTP request.
328
     * - The redirect_uri is OPTIONAL as per draft 20. But your implementation can enforce it
329
     * by setting $config['enforce_redirect'] to true.
330
     * - The state is OPTIONAL but recommended to enforce CSRF. Draft 21 states, however, that
331
     * CSRF protection is MANDATORY. You can enforce this by setting the $config['enforce_state'] to true.
332
     *
333
     * The draft specifies that the parameters should be retrieved from GET, override the Response
334
     * object to change this
335
     *
336
     * @return
337
     * The authorization parameters so the authorization server can prompt
338
     * the user for approval if valid.
339
     *
340
     * @see http://tools.ietf.org/html/rfc6749#section-4.1.1
341
     * @see http://tools.ietf.org/html/rfc6749#section-10.12
342
     *
343
     * @ingroup oauth2_section_3
344
     */
345
    public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response = null)
346
    {
347
        $this->response = is_null($response) ? new Response() : $response;
348
        $value = $this->getAuthorizeController()->validateAuthorizeRequest($request, $this->response);
349
350
        return $value;
351
    }
352
353
    public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response = null, $scope = null)
354
    {
355
        $this->response = is_null($response) ? new Response() : $response;
356
        $value = $this->getResourceController()->verifyResourceRequest($request, $this->response, $scope);
357
358
        return $value;
359
    }
360
361
    public function getAccessTokenData(RequestInterface $request, ResponseInterface $response = null)
362
    {
363
        $this->response = is_null($response) ? new Response() : $response;
364
        $value = $this->getResourceController()->getAccessTokenData($request, $this->response);
365
366
        return $value;
367
    }
368
369
    public function addGrantType(GrantTypeInterface $grantType, $identifier = null)
370
    {
371
        if (!is_string($identifier)) {
372
            $identifier = $grantType->getQuerystringIdentifier();
373
        }
374
375
        $this->grantTypes[$identifier] = $grantType;
376
377
        // persist added grant type down to TokenController
378
        if (!is_null($this->tokenController)) {
379
            $this->getTokenController()->addGrantType($grantType, $identifier);
380
        }
381
    }
382
383
    /**
384
     * Set a storage object for the server
385
     *
386
     * @param $storage
387
     * An object implementing one of the Storage interfaces
388
     * @param $key
389
     * If null, the storage is set to the key of each storage interface it implements
390
     *
391
     * @see storageMap
392
     */
393
    public function addStorage($storage, $key = null)
394
    {
395
        // if explicitly set to a valid key, do not "magically" set below
396
        if (isset($this->storageMap[$key])) {
397
            if (!is_null($storage) && !$storage instanceof $this->storageMap[$key]) {
398
                throw new \InvalidArgumentException(sprintf('storage of type "%s" must implement interface "%s"', $key, $this->storageMap[$key]));
399
            }
400
            $this->storages[$key] = $storage;
401
402
            // special logic to handle "client" and "client_credentials" strangeness
403
            if ($key === 'client' && !isset($this->storages['client_credentials'])) {
404
                if ($storage instanceof \OAuth2\Storage\ClientCredentialsInterface) {
405
                    $this->storages['client_credentials'] = $storage;
406
                }
407
            } elseif ($key === 'client_credentials' && !isset($this->storages['client'])) {
408
                if ($storage instanceof \OAuth2\Storage\ClientInterface) {
409
                    $this->storages['client'] = $storage;
410
                }
411
            }
412
        } elseif (!is_null($key) && !is_numeric($key)) {
413
            throw new \InvalidArgumentException(sprintf('unknown storage key "%s", must be one of [%s]', $key, implode(', ', array_keys($this->storageMap))));
414
        } else {
415
            $set = false;
416
            foreach ($this->storageMap as $type => $interface) {
417
                if ($storage instanceof $interface) {
418
                    $this->storages[$type] = $storage;
419
                    $set = true;
420
                }
421
            }
422
423
            if (!$set) {
424
                throw new \InvalidArgumentException(sprintf('storage of class "%s" must implement one of [%s]', get_class($storage), implode(', ', $this->storageMap)));
425
            }
426
        }
427
    }
428
429
    public function addResponseType(ResponseTypeInterface $responseType, $key = null)
430
    {
431
        $key = $this->normalizeResponseType($key);
432
433
        if (isset($this->responseTypeMap[$key])) {
434
            if (!$responseType instanceof $this->responseTypeMap[$key]) {
435
                throw new \InvalidArgumentException(sprintf('responseType of type "%s" must implement interface "%s"', $key, $this->responseTypeMap[$key]));
436
            }
437
            $this->responseTypes[$key] = $responseType;
438
        } elseif (!is_null($key) && !is_numeric($key)) {
439
            throw new \InvalidArgumentException(sprintf('unknown responseType key "%s", must be one of [%s]', $key, implode(', ', array_keys($this->responseTypeMap))));
440
        } else {
441
            $set = false;
442
            foreach ($this->responseTypeMap as $type => $interface) {
443
                if ($responseType instanceof $interface) {
444
                    $this->responseTypes[$type] = $responseType;
445
                    $set = true;
446
                }
447
            }
448
449
            if (!$set) {
450
                throw new \InvalidArgumentException(sprintf('Unknown response type %s.  Please implement one of [%s]', get_class($responseType), implode(', ', $this->responseTypeMap)));
451
            }
452
        }
453
    }
454
455
    public function getScopeUtil()
456
    {
457
        if (!$this->scopeUtil) {
458
            $storage = isset($this->storages['scope']) ? $this->storages['scope'] : null;
459
            $this->scopeUtil = new Scope($storage);
460
        }
461
462
        return $this->scopeUtil;
463
    }
464
465
    /**
466
     * every getter deserves a setter
467
     */
468
    public function setScopeUtil($scopeUtil)
469
    {
470
        $this->scopeUtil = $scopeUtil;
471
    }
472
473
    protected function createDefaultAuthorizeController()
474
    {
475
        if (!isset($this->storages['client'])) {
476
            throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\ClientInterface to use the authorize server");
477
        }
478
        if (0 == count($this->responseTypes)) {
479
            $this->responseTypes = $this->getDefaultResponseTypes();
480
        }
481
        if ($this->config['use_openid_connect'] && !isset($this->responseTypes['id_token'])) {
482
            $this->responseTypes['id_token'] = $this->createDefaultIdTokenResponseType();
483
            if ($this->config['allow_implicit']) {
484
                $this->responseTypes['id_token token'] = $this->createDefaultIdTokenTokenResponseType();
485
            }
486
        }
487
488
        $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state require_exact_redirect_uri')));
489
490
        if ($this->config['use_openid_connect']) {
491
            return new OpenIDAuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil());
492
        }
493
494
        return new AuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil());
495
    }
496
497
    protected function createDefaultTokenController()
498
    {
499
        if (0 == count($this->grantTypes)) {
500
            $this->grantTypes = $this->getDefaultGrantTypes();
501
        }
502
503
        if (is_null($this->clientAssertionType)) {
504
            // see if HttpBasic assertion type is requred.  If so, then create it from storage classes.
505
            foreach ($this->grantTypes as $grantType) {
506
                if (!$grantType instanceof ClientAssertionTypeInterface) {
507
                    if (!isset($this->storages['client_credentials'])) {
508
                        throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\ClientCredentialsInterface to use the token server");
509
                    }
510
                    $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_credentials_in_request_body allow_public_clients')));
511
                    $this->clientAssertionType = new HttpBasic($this->storages['client_credentials'], $config);
512
                    break;
513
                }
514
            }
515
        }
516
517
        if (!isset($this->storages['client'])) {
518
            throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\ClientInterface to use the token server");
519
        }
520
521
        $accessTokenResponseType = $this->getAccessTokenResponseType();
522
523
        return new TokenController($accessTokenResponseType, $this->storages['client'], $this->grantTypes, $this->clientAssertionType, $this->getScopeUtil());
524
    }
525
526
    protected function createDefaultResourceController()
527
    {
528
        if ($this->config['use_jwt_access_tokens']) {
529
            // overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set
530
            if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof JwtAccessTokenInterface) {
531
                $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage();
532
            }
533
        } elseif (!isset($this->storages['access_token'])) {
534
            throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the resource server");
535
        }
536
537
        if (!$this->tokenType) {
538
            $this->tokenType = $this->getDefaultTokenType();
539
        }
540
541
        $config = array_intersect_key($this->config, array('www_realm' => ''));
542
543
        return new ResourceController($this->tokenType, $this->storages['access_token'], $config, $this->getScopeUtil());
544
    }
545
546
    protected function createDefaultUserInfoController()
547
    {
548
        if ($this->config['use_jwt_access_tokens']) {
549
            // overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set
550
            if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof JwtAccessTokenInterface) {
551
                $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage();
552
            }
553
        } elseif (!isset($this->storages['access_token'])) {
554
            throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the UserInfo server");
555
        }
556
557
        if (!isset($this->storages['user_claims'])) {
558
            throw new \LogicException("You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use the UserInfo server");
559
        }
560
561
        if (!$this->tokenType) {
562
            $this->tokenType = $this->getDefaultTokenType();
563
        }
564
565
        $config = array_intersect_key($this->config, array('www_realm' => ''));
566
567
        return new UserInfoController($this->tokenType, $this->storages['access_token'], $this->storages['user_claims'], $config, $this->getScopeUtil());
568
    }
569
570
    protected function getDefaultTokenType()
571
    {
572
        $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_param_name token_bearer_header_name')));
573
574
        return new Bearer($config);
575
    }
576
577
    protected function getDefaultResponseTypes()
578
    {
579
        $responseTypes = array();
580
581
        if ($this->config['allow_implicit']) {
582
            $responseTypes['token'] = $this->getAccessTokenResponseType();
583
        }
584
585
        if ($this->config['use_openid_connect']) {
586
            $responseTypes['id_token'] = $this->getIdTokenResponseType();
587
            if ($this->config['allow_implicit']) {
588
                $responseTypes['id_token token'] = $this->getIdTokenTokenResponseType();
589
            }
590
        }
591
592
        if (isset($this->storages['authorization_code'])) {
593
            $config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime')));
594
            if ($this->config['use_openid_connect']) {
595
                if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) {
596
                    throw new \LogicException("Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when 'use_openid_connect' is true");
597
                }
598
                $responseTypes['code'] = new OpenIDAuthorizationCodeResponseType($this->storages['authorization_code'], $config);
599
                $responseTypes['code id_token'] = new CodeIdToken($responseTypes['code'], $responseTypes['id_token']);
600
            } else {
601
                $responseTypes['code'] = new AuthorizationCodeResponseType($this->storages['authorization_code'], $config);
602
            }
603
        }
604
605
        if (count($responseTypes) == 0) {
606
            throw new \LogicException("You must supply an array of response_types in the constructor or implement a OAuth2\Storage\AuthorizationCodeInterface storage object or set 'allow_implicit' to true and implement a OAuth2\Storage\AccessTokenInterface storage object");
607
        }
608
609
        return $responseTypes;
610
    }
611
612
    protected function getDefaultGrantTypes()
613
    {
614
        $grantTypes = array();
615
616
        if (isset($this->storages['user_credentials'])) {
617
            $grantTypes['password'] = new UserCredentials($this->storages['user_credentials']);
618
        }
619
620
        if (isset($this->storages['client_credentials'])) {
621
            $config = array_intersect_key($this->config, array('allow_credentials_in_request_body' => ''));
622
            $grantTypes['client_credentials'] = new ClientCredentials($this->storages['client_credentials'], $config);
623
        }
624
625
        if (isset($this->storages['refresh_token'])) {
626
            $config = array_intersect_key($this->config, array_flip(explode(' ', 'always_issue_new_refresh_token unset_refresh_token_after_use')));
627
            $grantTypes['refresh_token'] = new RefreshToken($this->storages['refresh_token'], $config);
628
        }
629
630
        if (isset($this->storages['authorization_code'])) {
631
            if ($this->config['use_openid_connect']) {
632
                if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) {
633
                    throw new \LogicException("Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when 'use_openid_connect' is true");
634
                }
635
                $grantTypes['authorization_code'] = new OpenIDAuthorizationCodeGrantType($this->storages['authorization_code']);
636
            } else {
637
                $grantTypes['authorization_code'] = new AuthorizationCode($this->storages['authorization_code']);
638
            }
639
        }
640
641
        if (count($grantTypes) == 0) {
642
            throw new \LogicException("Unable to build default grant types - You must supply an array of grant_types in the constructor");
643
        }
644
645
        return $grantTypes;
646
    }
647
648
    protected function getAccessTokenResponseType()
649
    {
650
        if (isset($this->responseTypes['token'])) {
651
            return $this->responseTypes['token'];
652
        }
653
654
        if ($this->config['use_jwt_access_tokens']) {
655
            return $this->createDefaultJwtAccessTokenResponseType();
656
        }
657
658
        return $this->createDefaultAccessTokenResponseType();
659
    }
660
661
    protected function getIdTokenResponseType()
662
    {
663
        if (isset($this->responseTypes['id_token'])) {
664
            return $this->responseTypes['id_token'];
665
        }
666
667
        return $this->createDefaultIdTokenResponseType();
668
    }
669
670
    protected function getIdTokenTokenResponseType()
671
    {
672
        if (isset($this->responseTypes['id_token token'])) {
673
            return $this->responseTypes['id_token token'];
674
        }
675
676
        return $this->createDefaultIdTokenTokenResponseType();
677
    }
678
679
    /**
680
     * For Resource Controller
681
     */
682
    protected function createDefaultJwtAccessTokenStorage()
683
    {
684
        if (!isset($this->storages['public_key'])) {
685
            throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens");
686
        }
687
        $tokenStorage = null;
688
        if (!empty($this->config['store_encrypted_token_string']) && isset($this->storages['access_token'])) {
689
            $tokenStorage = $this->storages['access_token'];
690
        }
691
        // wrap the access token storage as required.
692
        return new JwtAccessTokenStorage($this->storages['public_key'], $tokenStorage);
693
    }
694
695
    /**
696
     * For Authorize and Token Controllers
697
     */
698
    protected function createDefaultJwtAccessTokenResponseType()
699
    {
700
        if (!isset($this->storages['public_key'])) {
701
            throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens");
702
        }
703
704
        $tokenStorage = null;
705
        if (isset($this->storages['access_token'])) {
706
            $tokenStorage = $this->storages['access_token'];
707
        }
708
709
        $refreshStorage = null;
710
        if (isset($this->storages['refresh_token'])) {
711
            $refreshStorage = $this->storages['refresh_token'];
712
        }
713
714
        $config = array_intersect_key($this->config, array_flip(explode(' ', 'store_encrypted_token_string issuer access_lifetime refresh_token_lifetime')));
715
716
        return new JwtAccessToken($this->storages['public_key'], $tokenStorage, $refreshStorage, $config);
717
    }
718
719
    protected function createDefaultAccessTokenResponseType()
720
    {
721
        if (!isset($this->storages['access_token'])) {
722
            throw new \LogicException("You must supply a response type implementing OAuth2\ResponseType\AccessTokenInterface, or a storage object implementing OAuth2\Storage\AccessTokenInterface to use the token server");
723
        }
724
725
        $refreshStorage = null;
726
        if (isset($this->storages['refresh_token'])) {
727
            $refreshStorage = $this->storages['refresh_token'];
728
        }
729
730
        $config = array_intersect_key($this->config, array_flip(explode(' ', 'access_lifetime refresh_token_lifetime')));
731
        $config['token_type'] = $this->tokenType ? $this->tokenType->getTokenType() :  $this->getDefaultTokenType()->getTokenType();
732
733
        return new AccessToken($this->storages['access_token'], $refreshStorage, $config);
734
    }
735
736
    protected function createDefaultIdTokenResponseType()
737
    {
738
        if (!isset($this->storages['user_claims'])) {
739
            throw new \LogicException("You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use openid connect");
740
        }
741
        if (!isset($this->storages['public_key'])) {
742
            throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use openid connect");
743
        }
744
745
        $config = array_intersect_key($this->config, array_flip(explode(' ', 'issuer id_lifetime')));
746
747
        return new IdToken($this->storages['user_claims'], $this->storages['public_key'], $config);
748
    }
749
750
    protected function createDefaultIdTokenTokenResponseType()
751
    {
752
        return new IdTokenToken($this->getAccessTokenResponseType(), $this->getIdTokenResponseType());
753
    }
754
755
    protected function validateOpenIdConnect()
756
    {
757
        $authCodeGrant = $this->getGrantType('authorization_code');
758
        if (!empty($authCodeGrant) && !$authCodeGrant instanceof OpenIDAuthorizationCodeGrantType) {
759
            throw new \InvalidArgumentException('You have enabled OpenID Connect, but supplied a grant type that does not support it.');
760
        }
761
    }
762
763
    protected function normalizeResponseType($name)
764
    {
765
        // for multiple-valued response types - make them alphabetical
766
        if (!empty($name) && false !== strpos($name, ' ')) {
767
            $types = explode(' ', $name);
768
            sort($types);
769
            $name = implode(' ', $types);
770
        }
771
772
        return $name;
773
    }
774
775
    public function getResponse()
776
    {
777
        return $this->response;
778
    }
779
780
    public function getStorages()
781
    {
782
        return $this->storages;
783
    }
784
785
    public function getStorage($name)
786
    {
787
        return isset($this->storages[$name]) ? $this->storages[$name] : null;
788
    }
789
790
    public function getGrantTypes()
791
    {
792
        return $this->grantTypes;
793
    }
794
795
    public function getGrantType($name)
796
    {
797
        return isset($this->grantTypes[$name]) ? $this->grantTypes[$name] : null;
798
    }
799
800
    public function getResponseTypes()
801
    {
802
        return $this->responseTypes;
803
    }
804
805
    public function getResponseType($name)
806
    {
807
        // for multiple-valued response types - make them alphabetical
808
        $name = $this->normalizeResponseType($name);
809
810
        return isset($this->responseTypes[$name]) ? $this->responseTypes[$name] : null;
811
    }
812
813
    public function getTokenType()
814
    {
815
        return $this->tokenType;
816
    }
817
818
    public function getClientAssertionType()
819
    {
820
        return $this->clientAssertionType;
821
    }
822
823
    public function setConfig($name, $value)
824
    {
825
        $this->config[$name] = $value;
826
    }
827
828
    public function getConfig($name, $default = null)
829
    {
830
        return isset($this->config[$name]) ? $this->config[$name] : $default;
831
    }
832
}
833