Completed
Pull Request — master (#1074)
by Luca
01:57 queued 28s
created

AbstractGrant::setScopeRepository()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * OAuth 2.0 Abstract grant.
4
 *
5
 * @author      Alex Bilbie <[email protected]>
6
 * @copyright   Copyright (c) Alex Bilbie
7
 * @license     http://mit-license.org/
8
 *
9
 * @link        https://github.com/thephpleague/oauth2-server
10
 */
11
namespace League\OAuth2\Server\Grant;
12
13
use DateInterval;
14
use DateTimeImmutable;
15
use Error;
16
use Exception;
17
use League\Event\EmitterAwareTrait;
18
use League\OAuth2\Server\CryptKey;
19
use League\OAuth2\Server\CryptTrait;
20
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
21
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
22
use League\OAuth2\Server\Entities\ClientEntityInterface;
23
use League\OAuth2\Server\Entities\DeviceCodeEntityInterface;
24
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
25
use League\OAuth2\Server\Entities\ScopeEntityInterface;
26
use League\OAuth2\Server\Exception\OAuthServerException;
27
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
28
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
29
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
30
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
31
use League\OAuth2\Server\Repositories\DeviceCodeRepositoryInterface;
32
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
33
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
34
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
35
use League\OAuth2\Server\RequestEvent;
36
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
37
use League\OAuth2\Server\RequestTypes\DeviceAuthorizationRequest;
38
use LogicException;
39
use Psr\Http\Message\ServerRequestInterface;
40
use TypeError;
41
42
/**
43
 * Abstract grant class.
44
 */
45
abstract class AbstractGrant implements GrantTypeInterface
46
{
47
    use EmitterAwareTrait, CryptTrait;
48
49
    const SCOPE_DELIMITER_STRING = ' ';
50
51
    const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
52
53
    /**
54
     * @var ClientRepositoryInterface
55
     */
56
    protected $clientRepository;
57
58
    /**
59
     * @var AccessTokenRepositoryInterface
60
     */
61
    protected $accessTokenRepository;
62
63
    /**
64
     * @var ScopeRepositoryInterface
65
     */
66
    protected $scopeRepository;
67
68
    /**
69
     * @var AuthCodeRepositoryInterface
70
     */
71
    protected $authCodeRepository;
72
73
    /**
74
     * @var DeviceCodeRepositoryInterface
75
     */
76
    protected $deviceCodeRepository;
77
78
    /**
79
     * @var RefreshTokenRepositoryInterface
80
     */
81
    protected $refreshTokenRepository;
82
83
    /**
84
     * @var UserRepositoryInterface
85
     */
86
    protected $userRepository;
87
88
    /**
89
     * @var DateInterval
90
     */
91
    protected $refreshTokenTTL;
92
93
    /**
94
     * @var CryptKey
95
     */
96
    protected $privateKey;
97
98
    /**
99
     * @string
100
     */
101
    protected $defaultScope;
102
103
    /**
104
     * @param ClientRepositoryInterface $clientRepository
105
     */
106 78
    public function setClientRepository(ClientRepositoryInterface $clientRepository)
107
    {
108 78
        $this->clientRepository = $clientRepository;
109 78
    }
110
111
    /**
112
     * @param AccessTokenRepositoryInterface $accessTokenRepository
113
     */
114 48
    public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
115
    {
116 48
        $this->accessTokenRepository = $accessTokenRepository;
117 48
    }
118
119
    /**
120
     * @param ScopeRepositoryInterface $scopeRepository
121
     */
122 47
    public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
123
    {
124 47
        $this->scopeRepository = $scopeRepository;
125 47
    }
126
127
    /**
128
     * @param RefreshTokenRepositoryInterface $refreshTokenRepository
129
     */
130 72
    public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
131
    {
132 72
        $this->refreshTokenRepository = $refreshTokenRepository;
133 72
    }
134
135
    /**
136
     * @param AuthCodeRepositoryInterface $authCodeRepository
137
     */
138 46
    public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
139
    {
140 46
        $this->authCodeRepository = $authCodeRepository;
141 46
    }
142
143
    /**
144
     * @param DeviceCodeRepositoryInterface $deviceCodeRepository
145
     */
146 9
    public function setDeviceCodeRepository(DeviceCodeRepositoryInterface $deviceCodeRepository)
147
    {
148 9
        $this->deviceCodeRepository = $deviceCodeRepository;
149 9
    }
150
151
    /**
152
     * @param UserRepositoryInterface $userRepository
153
     */
154 6
    public function setUserRepository(UserRepositoryInterface $userRepository)
155
    {
156 6
        $this->userRepository = $userRepository;
157 6
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 2
    public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
163
    {
164 2
        $this->refreshTokenTTL = $refreshTokenTTL;
165 2
    }
166
167
    /**
168
     * Set the private key
169
     *
170
     * @param CryptKey $key
171
     */
172 37
    public function setPrivateKey(CryptKey $key)
173
    {
174 37
        $this->privateKey = $key;
175 37
    }
176
177
    /**
178
     * @param string $scope
179
     */
180 22
    public function setDefaultScope($scope)
181
    {
182 22
        $this->defaultScope = $scope;
183 22
    }
184
185
    /**
186
     * Validate the client.
187
     *
188
     * @param ServerRequestInterface $request
189
     *
190
     * @throws OAuthServerException
191
     *
192
     * @return ClientEntityInterface
193
     */
194 44
    protected function validateClient(ServerRequestInterface $request)
195
    {
196 44
        list($clientId, $clientSecret) = $this->getClientCredentials($request);
197
198 41
        if ($this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier()) === false) {
199 5
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
200
201 5
            throw OAuthServerException::invalidClient($request);
202
        }
203
204 36
        $client = $this->getClientEntityOrFail($clientId, $request);
205
206
        // If a redirect URI is provided ensure it matches what is pre-registered
207 36
        $redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
208
209 36
        if ($redirectUri !== null) {
210 16
            $this->validateRedirectUri($redirectUri, $client, $request);
211
        }
212
213 34
        return $client;
214
    }
215
216
    /**
217
     * Wrapper around ClientRepository::getClientEntity() that ensures we emit
218
     * an event and throw an exception if the repo doesn't return a client
219
     * entity.
220
     *
221
     * This is a bit of defensive coding because the interface contract
222
     * doesn't actually enforce non-null returns/exception-on-no-client so
223
     * getClientEntity might return null. By contrast, this method will
224
     * always either return a ClientEntityInterface or throw.
225
     *
226
     * @param string                 $clientId
227
     * @param ServerRequestInterface $request
228
     *
229
     * @return ClientEntityInterface
230
     */
231 65
    protected function getClientEntityOrFail($clientId, ServerRequestInterface $request)
232
    {
233 65
        $client = $this->clientRepository->getClientEntity($clientId);
234
235 65
        if ($client instanceof ClientEntityInterface === false) {
236 4
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
237 4
            throw OAuthServerException::invalidClient($request);
238
        }
239
240 61
        return $client;
241
    }
242
243
    /**
244
     * Gets the client credentials from the request from the request body or
245
     * the Http Basic Authorization header
246
     *
247
     * @param ServerRequestInterface $request
248
     *
249
     * @return array
250
     */
251 53
    protected function getClientCredentials(ServerRequestInterface $request)
252
    {
253 53
        list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
254
255 53
        $clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
256
257 53
        if (\is_null($clientId)) {
258 3
            throw OAuthServerException::invalidRequest('client_id');
259
        }
260
261 50
        $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
262
263 50
        return [$clientId, $clientSecret];
264
    }
265
266
    /**
267
     * Validate redirectUri from the request.
268
     * If a redirect URI is provided ensure it matches what is pre-registered
269
     *
270
     * @param string                 $redirectUri
271
     * @param ClientEntityInterface  $client
272
     * @param ServerRequestInterface $request
273
     *
274
     * @throws OAuthServerException
275
     */
276 30
    protected function validateRedirectUri(
277
        string $redirectUri,
278
        ClientEntityInterface $client,
279
        ServerRequestInterface $request
280
    ) {
281 30
        if (\is_string($client->getRedirectUri())
282 30
            && (\strcmp($client->getRedirectUri(), $redirectUri) !== 0)
283
        ) {
284 3
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
285 3
            throw OAuthServerException::invalidClient($request);
286 27
        } elseif (\is_array($client->getRedirectUri())
287 27
            && \in_array($redirectUri, $client->getRedirectUri(), true) === false
288
        ) {
289 3
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
290 3
            throw OAuthServerException::invalidClient($request);
291
        }
292 24
    }
293
294
    /**
295
     * Validate scopes in the request.
296
     *
297
     * @param string|array $scopes
298
     * @param string       $redirectUri
299
     *
300
     * @throws OAuthServerException
301
     *
302
     * @return ScopeEntityInterface[]
303
     */
304 41
    public function validateScopes($scopes, $redirectUri = null)
305
    {
306 41
        if (!\is_array($scopes)) {
307 27
            $scopes = $this->convertScopesQueryStringToArray($scopes);
308
        }
309
310 41
        $validScopes = [];
311
312 41
        foreach ($scopes as $scopeItem) {
313 36
            $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
314
315 36
            if ($scope instanceof ScopeEntityInterface === false) {
316 1
                throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
317
            }
318
319 35
            $validScopes[] = $scope;
320
        }
321
322 40
        return $validScopes;
323
    }
324
325
    /**
326
     * Converts a scopes query string to an array to easily iterate for validation.
327
     *
328
     * @param string $scopes
329
     *
330
     * @return array
331
     */
332 27
    private function convertScopesQueryStringToArray($scopes)
333
    {
334
        return \array_filter(\explode(self::SCOPE_DELIMITER_STRING, \trim($scopes)), function ($scope) {
335 27
            return !empty($scope);
336 27
        });
337
    }
338
339
    /**
340
     * Retrieve request parameter.
341
     *
342
     * @param string                 $parameter
343
     * @param ServerRequestInterface $request
344
     * @param mixed                  $default
345
     *
346
     * @return null|string
347
     */
348 56
    protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
349
    {
350 56
        $requestParameters = (array) $request->getParsedBody();
351
352 56
        return $requestParameters[$parameter] ?? $default;
353
    }
354
355
    /**
356
     * Retrieve HTTP Basic Auth credentials with the Authorization header
357
     * of a request. First index of the returned array is the username,
358
     * second is the password (so list() will work). If the header does
359
     * not exist, or is otherwise an invalid HTTP Basic header, return
360
     * [null, null].
361
     *
362
     * @param ServerRequestInterface $request
363
     *
364
     * @return string[]|null[]
365
     */
366 58
    protected function getBasicAuthCredentials(ServerRequestInterface $request)
367
    {
368 58
        if (!$request->hasHeader('Authorization')) {
369 51
            return [null, null];
370
        }
371
372 7
        $header = $request->getHeader('Authorization')[0];
373 7
        if (\strpos($header, 'Basic ') !== 0) {
374 1
            return [null, null];
375
        }
376
377 6
        if (!($decoded = \base64_decode(\substr($header, 6)))) {
378 1
            return [null, null];
379
        }
380
381 5
        if (\strpos($decoded, ':') === false) {
382 2
            return [null, null]; // HTTP Basic header without colon isn't valid
383
        }
384
385 3
        return \explode(':', $decoded, 2);
386
    }
387
388
    /**
389
     * Retrieve query string parameter.
390
     *
391
     * @param string                 $parameter
392
     * @param ServerRequestInterface $request
393
     * @param mixed                  $default
394
     *
395
     * @return null|string
396
     */
397 21
    protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
398
    {
399 21
        return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
400
    }
401
402
    /**
403
     * Retrieve cookie parameter.
404
     *
405
     * @param string                 $parameter
406
     * @param ServerRequestInterface $request
407
     * @param mixed                  $default
408
     *
409
     * @return null|string
410
     */
411 1
    protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
412
    {
413 1
        return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
414
    }
415
416
    /**
417
     * Retrieve server parameter.
418
     *
419
     * @param string                 $parameter
420
     * @param ServerRequestInterface $request
421
     * @param mixed                  $default
422
     *
423
     * @return null|string
424
     */
425 23
    protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
426
    {
427 23
        return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
428
    }
429
430
    /**
431
     * Issue an access token.
432
     *
433
     * @param DateInterval           $accessTokenTTL
434
     * @param ClientEntityInterface  $client
435
     * @param string|null            $userIdentifier
436
     * @param ScopeEntityInterface[] $scopes
437
     *
438
     * @throws OAuthServerException
439
     * @throws UniqueTokenIdentifierConstraintViolationException
440
     *
441
     * @return AccessTokenEntityInterface
442
     */
443 23
    protected function issueAccessToken(
444
        DateInterval $accessTokenTTL,
445
        ClientEntityInterface $client,
446
        $userIdentifier,
447
        array $scopes = []
448
    ) {
449 23
        $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
450
451 23
        $accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
452 23
        $accessToken->setExpiryDateTime((new DateTimeImmutable())->add($accessTokenTTL));
453 23
        $accessToken->setPrivateKey($this->privateKey);
454
455 23
        while ($maxGenerationAttempts-- > 0) {
456 23
            $accessToken->setIdentifier($this->generateUniqueIdentifier());
457
            try {
458 23
                $this->accessTokenRepository->persistNewAccessToken($accessToken);
459
460 21
                return $accessToken;
461 2
            } catch (UniqueTokenIdentifierConstraintViolationException $e) {
462 1
                if ($maxGenerationAttempts === 0) {
463 1
                    throw $e;
464
                }
465
            }
466
        }
467
    }
468
469
    /**
470
     * Issue an auth code.
471
     *
472
     * @param DateInterval           $authCodeTTL
473
     * @param ClientEntityInterface  $client
474
     * @param string                 $userIdentifier
475
     * @param string|null            $redirectUri
476
     * @param ScopeEntityInterface[] $scopes
477
     *
478
     * @throws OAuthServerException
479
     * @throws UniqueTokenIdentifierConstraintViolationException
480
     *
481
     * @return AuthCodeEntityInterface
482
     */
483 6
    protected function issueAuthCode(
484
        DateInterval $authCodeTTL,
485
        ClientEntityInterface $client,
486
        $userIdentifier,
487
        $redirectUri,
488
        array $scopes = []
489
    ) {
490 6
        $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
491
492 6
        $authCode = $this->authCodeRepository->getNewAuthCode();
493 6
        $authCode->setExpiryDateTime((new DateTimeImmutable())->add($authCodeTTL));
494 6
        $authCode->setClient($client);
495 6
        $authCode->setUserIdentifier($userIdentifier);
496
497 6
        if ($redirectUri !== null) {
498 1
            $authCode->setRedirectUri($redirectUri);
499
        }
500
501 6
        foreach ($scopes as $scope) {
502 1
            $authCode->addScope($scope);
503
        }
504
505 6
        while ($maxGenerationAttempts-- > 0) {
506 6
            $authCode->setIdentifier($this->generateUniqueIdentifier());
507
            try {
508 6
                $this->authCodeRepository->persistNewAuthCode($authCode);
509
510 4
                return $authCode;
511 2
            } catch (UniqueTokenIdentifierConstraintViolationException $e) {
512 1
                if ($maxGenerationAttempts === 0) {
513 1
                    throw $e;
514
                }
515
            }
516
        }
517
    }
518
519
    /**
520
     * Issue a device code.
521
     *
522
     * @param DateInterval           $deviceCodeTTL
523
     * @param ClientEntityInterface  $client
524
     * @param string                 $verificationUri
525
     * @param ScopeEntityInterface[] $scopes
526
     *
527
     * @return DeviceCodeEntityInterface
528
     *
529
     * @throws OAuthServerException
530
     * @throws UniqueTokenIdentifierConstraintViolationException
531
     */
532 1
    protected function issueDeviceCode(
533
        DateInterval $deviceCodeTTL,
534
        ClientEntityInterface $client,
535
        $verificationUri,
536
        array $scopes = []
537
    ) {
538 1
        $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
539
540 1
        $deviceCode = $this->deviceCodeRepository->getNewDeviceCode();
541 1
        $deviceCode->setExpiryDateTime((new DateTimeImmutable())->add($deviceCodeTTL));
542 1
        $deviceCode->setClient($client);
543 1
        $deviceCode->setVerificationUri($verificationUri);
544
545 1
        foreach ($scopes as $scope) {
546
            $deviceCode->addScope($scope);
547
        }
548
549 1
        while ($maxGenerationAttempts-- > 0) {
550 1
            $deviceCode->setIdentifier($this->generateUniqueIdentifier());
551 1
            $deviceCode->setUserCode($this->generateUniqueUserCode());
552
            try {
553 1
                $this->deviceCodeRepository->persistNewDeviceCode($deviceCode);
554
555 1
                return $deviceCode;
556
            } catch (UniqueTokenIdentifierConstraintViolationException $e) {
557
                if ($maxGenerationAttempts === 0) {
558
                    throw $e;
559
                }
560
            }
561
        }
562
    }
563
564
    /**
565
     * @param AccessTokenEntityInterface $accessToken
566
     *
567
     * @throws OAuthServerException
568
     * @throws UniqueTokenIdentifierConstraintViolationException
569
     *
570
     * @return RefreshTokenEntityInterface|null
571
     */
572 17
    protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
573
    {
574 17
        $refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
575
576 17
        if ($refreshToken === null) {
577 4
            return null;
578
        }
579
580 13
        $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL));
581 13
        $refreshToken->setAccessToken($accessToken);
582
583 13
        $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
584
585 13
        while ($maxGenerationAttempts-- > 0) {
586 13
            $refreshToken->setIdentifier($this->generateUniqueIdentifier());
587
            try {
588 13
                $this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
589
590 11
                return $refreshToken;
591 2
            } catch (UniqueTokenIdentifierConstraintViolationException $e) {
592 1
                if ($maxGenerationAttempts === 0) {
593 1
                    throw $e;
594
                }
595
            }
596
        }
597
    }
598
599
    /**
600
     * Generate a new unique identifier.
601
     *
602
     * @param int $length
603
     *
604
     * @throws OAuthServerException
605
     *
606
     * @return string
607
     */
608 32
    protected function generateUniqueIdentifier($length = 40)
609
    {
610
        try {
611 32
            return \bin2hex(\random_bytes($length));
612
            // @codeCoverageIgnoreStart
613
        } catch (TypeError $e) {
614
            throw OAuthServerException::serverError('An unexpected error has occurred', $e);
615
        } catch (Error $e) {
616
            throw OAuthServerException::serverError('An unexpected error has occurred', $e);
617
        } catch (Exception $e) {
618
            // If you get this message, the CSPRNG failed hard.
619
            throw OAuthServerException::serverError('Could not generate a random string', $e);
620
        }
621
        // @codeCoverageIgnoreEnd
622
    }
623
624
    /**
625
     * Generate a new unique user code.
626
     *
627
     * @param int $length
628
     *
629
     * @return string
630
     *
631
     * @throws OAuthServerException
632
     */
633 1
    protected function generateUniqueUserCode($length = 8)
634
    {
635
        try {
636 1
            $userCode = '';
637 1
            while (\strlen($userCode) < $length) {
638 1
                $userCode .= (string) \random_int(0, 9);
639
            }
640
641 1
            return $userCode;
642
            // @codeCoverageIgnoreStart
643
        } catch (TypeError $e) {
644
            throw OAuthServerException::serverError('An unexpected error has occurred', $e);
645
        } catch (Error $e) {
646
            throw OAuthServerException::serverError('An unexpected error has occurred', $e);
647
        } catch (Exception $e) {
648
            // If you get this message, the CSPRNG failed hard.
649
            throw OAuthServerException::serverError('Could not generate a random string', $e);
650
        }
651
        // @codeCoverageIgnoreEnd
652
    }
653
654
    /**
655
     * {@inheritdoc}
656
     */
657 5
    public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
658
    {
659 5
        $requestParameters = (array) $request->getParsedBody();
660
661
        return (
662 5
            \array_key_exists('grant_type', $requestParameters)
663 5
            && $requestParameters['grant_type'] === $this->getIdentifier()
664
        );
665
    }
666
667
    /**
668
     * {@inheritdoc}
669
     */
670 1
    public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
671
    {
672 1
        return false;
673
    }
674
675
    /**
676
     * {@inheritdoc}
677
     */
678 1
    public function validateAuthorizationRequest(ServerRequestInterface $request)
679
    {
680 1
        throw new LogicException('This grant cannot validate an authorization request');
681
    }
682
683
    /**
684
     * {@inheritdoc}
685
     */
686 1
    public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
687
    {
688 1
        throw new LogicException('This grant cannot complete an authorization request');
689
    }
690
691
    /**
692
     * {@inheritdoc}
693
     */
694
    public function canRespondToDeviceAuthorizationRequest(ServerRequestInterface $request)
695
    {
696
        return false;
697
    }
698
699
    /**
700
     * {@inheritdoc}
701
     */
702
    public function validateDeviceAuthorizationRequest(ServerRequestInterface $request)
703
    {
704
        throw new LogicException('This grant cannot validate an authorization request');
705
    }
706
707
    /**
708
     * {@inheritdoc}
709
     */
710
    public function completeDeviceAuthorizationRequest(DeviceAuthorizationRequest $deviceAuthorizationRequest)
711
    {
712
        throw new LogicException('This grant cannot complete a device authorization request');
713
    }
714
}
715