Completed
Pull Request — master (#953)
by Andrew
04:06
created

AuthCodeGrant::respondToAccessTokenRequest()   C

Complexity

Conditions 10
Paths 12

Size

Total Lines 91

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 10.0014

Importance

Changes 0
Metric Value
dl 0
loc 91
ccs 40
cts 41
cp 0.9756
rs 6.3296
c 0
b 0
f 0
cc 10
nc 12
nop 3
crap 10.0014

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author      Alex Bilbie <[email protected]>
4
 * @copyright   Copyright (c) Alex Bilbie
5
 * @license     http://mit-license.org/
6
 *
7
 * @link        https://github.com/thephpleague/oauth2-server
8
 */
9
10
namespace League\OAuth2\Server\Grant;
11
12
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
13
use League\OAuth2\Server\Entities\ClientEntityInterface;
14
use League\OAuth2\Server\Entities\ScopeEntityInterface;
15
use League\OAuth2\Server\Entities\UserEntityInterface;
16
use League\OAuth2\Server\Exception\OAuthServerException;
17
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
18
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
19
use League\OAuth2\Server\RequestEvent;
20
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
21
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
22
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
23
use Psr\Http\Message\ServerRequestInterface;
24
25
class AuthCodeGrant extends AbstractAuthorizeGrant
26
{
27
    /**
28
     * @var \DateInterval
29
     */
30
    private $authCodeTTL;
31
32
    /**
33
     * @var bool
34
     */
35
    private $enableCodeExchangeProof = false;
36
37
    /**
38
     * @param AuthCodeRepositoryInterface $authCodeRepository
39
     * @param RefreshTokenRepositoryInterface $refreshTokenRepository
40
     * @param \DateInterval $authCodeTTL
41
     *
42
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
43
     * @throws \Exception
44
     */
45 41
    public function __construct(
46
        AuthCodeRepositoryInterface $authCodeRepository,
47
        RefreshTokenRepositoryInterface $refreshTokenRepository,
48
        \DateInterval $authCodeTTL
49
    ) {
50 41
        $this->setAuthCodeRepository($authCodeRepository);
51 41
        $this->setRefreshTokenRepository($refreshTokenRepository);
52 41
        $this->authCodeTTL = $authCodeTTL;
53 41
        $this->refreshTokenTTL = new \DateInterval('P1M');
54 41
    }
55
56 13
    public function enableCodeExchangeProof()
57
    {
58 13
        $this->enableCodeExchangeProof = true;
59 13
    }
60
61
    /**
62
     * Respond to an access token request.
63
     *
64
     * @param ServerRequestInterface $request
65
     * @param ResponseTypeInterface  $responseType
66
     * @param \DateInterval          $accessTokenTTL
67
     *
68
     * @throws OAuthServerException
69
     *
70
     * @return ResponseTypeInterface
71
     */
72 18
    public function respondToAccessTokenRequest(
73
        ServerRequestInterface $request,
74
        ResponseTypeInterface $responseType,
75
        \DateInterval $accessTokenTTL
76
    ) {
77
        // Validate request
78 18
        $client = $this->validateClient($request);
79 18
        $encryptedAuthCode = $this->getRequestParameter('code', $request, null);
80
81 18
        if ($encryptedAuthCode === null) {
82 1
            throw OAuthServerException::invalidRequest('code');
83
        }
84
85
        try {
86 17
            $authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
87
88 16
            $this->validateAuthorizationCode($authCodePayload, $client, $request);
89
90 11
            $scopes = $this->scopeRepository->finalizeScopes(
91 11
                $this->getScopes($authCodePayload),
92 11
                $this->getIdentifier(),
93 11
                $client,
94 11
                $authCodePayload->user_id
95
            );
96 6
        } catch (\LogicException  $e) {
97 1
            throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
98
        }
99
100
        // Validate code challenge
101 11
        if ($this->enableCodeExchangeProof === true) {
102 7
            $codeVerifier = $this->getRequestParameter('code_verifier', $request, null);
103
104 7
            if ($codeVerifier === null) {
105 1
                throw OAuthServerException::invalidRequest('code_verifier');
106
            }
107
108
            // Validate code_verifier according to RFC-7636
109
            // @see: https://tools.ietf.org/html/rfc7636#section-4.1
110 6
            if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) {
111 3
                throw OAuthServerException::invalidRequest(
112 3
                    'code_verifier',
113 3
                    'Code Verifier must follow the specifications of RFC-7636.'
114
                );
115
            }
116
117 3
            switch ($authCodePayload->code_challenge_method) {
118 3
                case 'plain':
119 2
                    if (hash_equals($codeVerifier, $authCodePayload->code_challenge) === false) {
120 1
                        throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
121
                    }
122
123 1
                    break;
124 1
                case 'S256':
125
                    if (
126 1
                        hash_equals(
127 1
                            strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'),
128 1
                            $authCodePayload->code_challenge
129 1
                        ) === false
130
                    ) {
131
                        throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
132
                    }
133
                    // @codeCoverageIgnoreStart
134
                    break;
135
                default:
136
                    throw OAuthServerException::serverError(
137
                        sprintf(
138
                            'Unsupported code challenge method `%s`',
139
                            $authCodePayload->code_challenge_method
140
                        )
141
                    );
142
                // @codeCoverageIgnoreEnd
143
            }
144
        }
145
146
        // Issue and persist access + refresh tokens
147 6
        $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
148 6
        $refreshToken = $this->issueRefreshToken($accessToken);
0 ignored issues
show
Bug introduced by
It seems like $accessToken defined by $this->issueAccessToken(...load->user_id, $scopes) on line 147 can be null; however, League\OAuth2\Server\Gra...nt::issueRefreshToken() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
149
150
        // Send events to emitter
151 4
        $this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
152 4
        $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
153
154
        // Inject tokens into response type
155 4
        $responseType->setAccessToken($accessToken);
0 ignored issues
show
Bug introduced by
It seems like $accessToken defined by $this->issueAccessToken(...load->user_id, $scopes) on line 147 can be null; however, League\OAuth2\Server\Res...rface::setAccessToken() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
156 4
        $responseType->setRefreshToken($refreshToken);
0 ignored issues
show
Bug introduced by
It seems like $refreshToken defined by $this->issueRefreshToken($accessToken) on line 148 can be null; however, League\OAuth2\Server\Res...face::setRefreshToken() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
157
158
        // Revoke used auth code
159 4
        $this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
160
161 4
        return $responseType;
162
    }
163
164
    /**
165
     * Validate the authorization code.
166
     *
167
     * @param AuthCodeEntityInterface $authCodePayload
168
     * @param ClientEntityInterface   $client
169
     * @param ServerRequestInterface  $request
170
     *
171
     * @return void
172
     */
173 16
    private function validateAuthorizationCode(
174
        $authCodePayload,
175
        ClientEntityInterface $client,
176
        ServerRequestInterface $request
177
    ) {
178 16
        if (time() > $authCodePayload->expire_time) {
0 ignored issues
show
Bug introduced by
Accessing expire_time on the interface League\OAuth2\Server\Ent...AuthCodeEntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
179 1
            throw OAuthServerException::invalidRequest('code', 'Authorization code has expired');
180
        }
181
182 15
        if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
0 ignored issues
show
Bug introduced by
Accessing auth_code_id on the interface League\OAuth2\Server\Ent...AuthCodeEntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
183 1
            throw OAuthServerException::invalidRequest('code', 'Authorization code has been revoked');
184
        }
185
186 14
        if ($authCodePayload->client_id !== $client->getIdentifier()) {
0 ignored issues
show
Bug introduced by
Accessing client_id on the interface League\OAuth2\Server\Ent...AuthCodeEntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
187 1
            throw OAuthServerException::invalidRequest('code', 'Authorization code was not issued to this client');
188
        }
189
190
        // The redirect URI is required in this request
191 13
        $redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
192 13
        if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) {
0 ignored issues
show
Bug introduced by
Accessing redirect_uri on the interface League\OAuth2\Server\Ent...AuthCodeEntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
193 1
            throw OAuthServerException::invalidRequest('redirect_uri');
194
        }
195
196 12
        if ($authCodePayload->redirect_uri !== $redirectUri) {
0 ignored issues
show
Bug introduced by
Accessing redirect_uri on the interface League\OAuth2\Server\Ent...AuthCodeEntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
197 1
            throw OAuthServerException::invalidRequest('redirect_uri', 'Invalid redirect URI');
198
        }
199 11
    }
200
201
    /**
202
     * Get scopes from the auth code payload.
203
     *
204
     * @param AuthCodeEntityInterface $authCodePayload
205
     *
206
     * @return array
207
     */
208 11
    private function getScopes($authCodePayload)
209
    {
210 11
        $scopes = [];
211
212 11
        foreach ($authCodePayload->scopes as $scopeId) {
0 ignored issues
show
Bug introduced by
Accessing scopes on the interface League\OAuth2\Server\Ent...AuthCodeEntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
213 11
            $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
214
215 11
            if ($scope instanceof ScopeEntityInterface === false) {
216
                // @codeCoverageIgnoreStart
217
                throw OAuthServerException::invalidScope($scopeId);
218
                // @codeCoverageIgnoreEnd
219
            }
220
221 11
            $scopes[] = $scope;
222
        }
223
224 11
        return $scopes;
225
    }
226
227
    /**
228
     * Return the grant identifier that can be used in matching up requests.
229
     *
230
     * @return string
231
     */
232 33
    public function getIdentifier()
233
    {
234 33
        return 'authorization_code';
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240 3
    public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
241
    {
242
        return (
243 3
            array_key_exists('response_type', $request->getQueryParams())
244 3
            && $request->getQueryParams()['response_type'] === 'code'
245 3
            && isset($request->getQueryParams()['client_id'])
246
        );
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252 14
    public function validateAuthorizationRequest(ServerRequestInterface $request)
253
    {
254 14
        $clientId = $this->getQueryStringParameter(
255 14
            'client_id',
256 14
            $request,
257 14
            $this->getServerParameter('PHP_AUTH_USER', $request)
258
        );
259
260 14
        if ($clientId === null) {
261 1
            throw OAuthServerException::invalidRequest('client_id');
262
        }
263
264 13
        $client = $this->clientRepository->getClientEntity(
265 13
            $clientId,
266 13
            $this->getIdentifier(),
267 13
            null,
268 13
            false
269
        );
270
271 13
        if ($client instanceof ClientEntityInterface === false) {
272 1
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
273 1
            throw OAuthServerException::invalidClient();
274
        }
275
276 12
        $redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
277
278 12
        if ($redirectUri !== null) {
279 10
            $this->validateRedirectUri($redirectUri, $client, $request);
280 2
        } elseif (empty($client->getRedirectUri()) ||
281 2
            (\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1)) {
282 1
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
283 1
            throw OAuthServerException::invalidClient();
284
        } else {
285 1
            $redirectUri = \is_array($client->getRedirectUri())
286
                ? $client->getRedirectUri()[0]
287 1
                : $client->getRedirectUri();
288
        }
289
290 9
        $scopes = $this->validateScopes(
291 9
            $this->getQueryStringParameter('scope', $request, $this->defaultScope),
292 9
            $redirectUri
293
        );
294
295 9
        $stateParameter = $this->getQueryStringParameter('state', $request);
296
297 9
        $authorizationRequest = new AuthorizationRequest();
298 9
        $authorizationRequest->setGrantTypeId($this->getIdentifier());
299 9
        $authorizationRequest->setClient($client);
300 9
        $authorizationRequest->setRedirectUri($redirectUri);
301
302 9
        if ($stateParameter !== null) {
303
            $authorizationRequest->setState($stateParameter);
304
        }
305
306 9
        $authorizationRequest->setScopes($scopes);
307
308 9
        if ($this->enableCodeExchangeProof === true) {
309 6
            $codeChallenge = $this->getQueryStringParameter('code_challenge', $request);
310 6
            if ($codeChallenge === null) {
311 1
                throw OAuthServerException::invalidRequest('code_challenge');
312
            }
313
314 5
            $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
315
316 5
            if (\in_array($codeChallengeMethod, ['plain', 'S256'], true) === false) {
317 1
                throw OAuthServerException::invalidRequest(
318 1
                    'code_challenge_method',
319 1
                    'Code challenge method must be `plain` or `S256`'
320
                );
321
            }
322
323
            // Validate code_challenge according to RFC-7636
324
            // @see: https://tools.ietf.org/html/rfc7636#section-4.2
325 4
            if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
326 3
                throw OAuthServerException::invalidRequest(
327 3
                    'code_challenged',
328 3
                    'Code challenge must follow the specifications of RFC-7636.'
329
                );
330
            }
331
332 1
            $authorizationRequest->setCodeChallenge($codeChallenge);
333 1
            $authorizationRequest->setCodeChallengeMethod($codeChallengeMethod);
334
        }
335
336 4
        return $authorizationRequest;
337
    }
338
339
    /**
340
     * {@inheritdoc}
341
     */
342 7
    public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
343
    {
344 7
        if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
345 1
            throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
346
        }
347
348 6
        $finalRedirectUri = $authorizationRequest->getRedirectUri()
349 6
                          ?? $this->getClientRedirectUri($authorizationRequest);
350
351
        // The user approved the client, redirect them back with an auth code
352 6
        if ($authorizationRequest->isAuthorizationApproved() === true) {
353 5
            $authCode = $this->issueAuthCode(
354 5
                $this->authCodeTTL,
355 5
                $authorizationRequest->getClient(),
356 5
                $authorizationRequest->getUser()->getIdentifier(),
357 5
                $authorizationRequest->getRedirectUri(),
358 5
                $authorizationRequest->getScopes()
359
            );
360
361
            $payload = [
362 3
                'client_id'             => $authCode->getClient()->getIdentifier(),
363 3
                'redirect_uri'          => $authCode->getRedirectUri(),
364 3
                'auth_code_id'          => $authCode->getIdentifier(),
365 3
                'scopes'                => $authCode->getScopes(),
366 3
                'user_id'               => $authCode->getUserIdentifier(),
367 3
                'expire_time'           => (new \DateTime())->add($this->authCodeTTL)->format('U'),
368 3
                'code_challenge'        => $authorizationRequest->getCodeChallenge(),
369 3
                'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(),
370
            ];
371
372 3
            $response = new RedirectResponse();
373 3
            $response->setRedirectUri(
374 3
                $this->makeRedirectUri(
375 3
                    $finalRedirectUri,
376
                    [
377 3
                        'code'  => $this->encrypt(
378 3
                            json_encode(
379 3
                                $payload
380
                            )
381
                        ),
382 3
                        'state' => $authorizationRequest->getState(),
383
                    ]
384
                )
385
            );
386
387 3
            return $response;
388
        }
389
390
        // The user denied the client, redirect them back with an error
391 1
        throw OAuthServerException::accessDenied(
392 1
            'The user denied the request',
393 1
            $this->makeRedirectUri(
394 1
                $finalRedirectUri,
395
                [
396 1
                    'state' => $authorizationRequest->getState(),
397
                ]
398
            )
399
        );
400
    }
401
402
    /**
403
     * Get the client redirect URI if not set in the request.
404
     *
405
     * @param AuthorizationRequest $authorizationRequest
406
     *
407
     * @return string
408
     */
409 6
    private function getClientRedirectUri(AuthorizationRequest $authorizationRequest)
410
    {
411 6
        return \is_array($authorizationRequest->getClient()->getRedirectUri())
412
                ? $authorizationRequest->getClient()->getRedirectUri()[0]
413 6
                : $authorizationRequest->getClient()->getRedirectUri();
414
    }
415
}
416