Passed
Pull Request — master (#1328)
by
unknown
31:59
created

BearerTokenValidator::withValidatedRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @author      Alex Bilbie <[email protected]>
5
 * @copyright   Copyright (c) Alex Bilbie
6
 * @license     http://mit-license.org/
7
 *
8
 * @link        https://github.com/thephpleague/oauth2-server
9
 */
10
11
declare(strict_types=1);
12
13
namespace League\OAuth2\Server\AuthorizationValidators;
14
15
use DateInterval;
16
use DateTimeZone;
17
use Lcobucci\Clock\SystemClock;
18
use Lcobucci\JWT\Configuration;
19
use Lcobucci\JWT\Exception;
20
use Lcobucci\JWT\Signer\Key\InMemory;
21
use Lcobucci\JWT\Signer\Rsa\Sha256;
22
use Lcobucci\JWT\UnencryptedToken;
23
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
24
use Lcobucci\JWT\Validation\Constraint\SignedWith;
25
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
26
use League\OAuth2\Server\CryptKeyInterface;
27
use League\OAuth2\Server\CryptTrait;
28
use League\OAuth2\Server\Exception\OAuthServerException;
29
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
30
use Psr\Http\Message\ServerRequestInterface;
31
use RuntimeException;
32
33
use function date_default_timezone_get;
34
use function preg_replace;
35
use function trim;
36
37
class BearerTokenValidator implements AuthorizationValidatorInterface
38
{
39
    use CryptTrait;
40
41
    protected CryptKeyInterface $publicKey;
42
43
    private Configuration $jwtConfiguration;
44
45 13
    public function __construct(private AccessTokenRepositoryInterface $accessTokenRepository, private ?DateInterval $jwtValidAtDateLeeway = null)
46
    {
47 13
    }
48
49
    /**
50
     * Set the public key
51
     */
52 13
    public function setPublicKey(CryptKeyInterface $key): void
53
    {
54 13
        $this->publicKey = $key;
55
56 13
        $this->initJwtConfiguration();
57
    }
58
59
    /**
60
     * Initialise the JWT configuration.
61
     */
62 13
    private function initJwtConfiguration(): void
63
    {
64 13
        $this->jwtConfiguration = Configuration::forSymmetricSigner(
65 13
            new Sha256(),
66 13
            InMemory::plainText('empty', 'empty')
67 13
        );
68
69 13
        $clock = new SystemClock(new DateTimeZone(date_default_timezone_get()));
70
71 13
        $publicKeyContents = $this->publicKey->getKeyContents();
72
73 13
        if ($publicKeyContents === '') {
74
            throw new RuntimeException('Public key is empty');
75
        }
76
77 13
        $this->jwtConfiguration->setValidationConstraints(
78 13
            new LooseValidAt($clock, $this->jwtValidAtDateLeeway),
79 13
            new SignedWith(
80 13
                new Sha256(),
81 13
                InMemory::plainText($publicKeyContents, $this->publicKey->getPassPhrase() ?? '')
82 13
            )
83 13
        );
84
    }
85
86
    /**
87
     * Configure the validated authorization request instance.
88
     */
89 13
    protected function withValidatedRequest(ServerRequestInterface $request, UnencryptedToken $token): ServerRequestInterface
0 ignored issues
show
Unused Code introduced by
The parameter $token is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

89
    protected function withValidatedRequest(ServerRequestInterface $request, /** @scrutinizer ignore-unused */ UnencryptedToken $token): ServerRequestInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
90
    {
91 13
        return $request;
92 1
    }
93
94
    /**
95 12
     * {@inheritdoc}
96 12
     */
97
    public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface
98 12
    {
99 1
        if ($request->hasHeader('authorization') === false) {
100
            throw OAuthServerException::accessDenied('Missing "Authorization" header');
101
        }
102
103
        $header = $request->getHeader('authorization');
104 11
        $jwt = trim((string) preg_replace('/^\s*Bearer\s/', '', $header[0]));
105 2
106 2
        if ($jwt === '') {
107
            throw OAuthServerException::accessDenied('Missing "Bearer" token');
108
        }
109
110
        try {
111 9
            // Attempt to parse the JWT
112 9
            $token = $this->jwtConfiguration->parser()->parse($jwt);
113 4
        } catch (Exception $exception) {
114 4
            throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
115
        }
116
117 5
        try {
118
            // Attempt to validate the JWT
119
            $constraints = $this->jwtConfiguration->validationConstraints();
120
            $this->jwtConfiguration->validator()->assert($token, ...$constraints);
121 5
        } catch (RequiredConstraintsViolated $exception) {
122
            throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception);
123
        }
124 5
125 1
        if (!$token instanceof UnencryptedToken) {
126
            throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken');
127
        }
128
129 4
        $claims = $token->claims();
130 4
131 4
        // Check if token has been revoked
132 4
        if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {
133 4
            throw OAuthServerException::accessDenied('Access token has been revoked');
134
        }
135
136
        // Return the request with additional attributes
137
        return $this->withValidatedRequest($request
138
            ->withAttribute('oauth_access_token_id', $claims->get('jti'))
139
            ->withAttribute('oauth_client_id', $claims->get('aud')[0])
140
            ->withAttribute('oauth_user_id', $claims->get('sub'))
141
            ->withAttribute('oauth_scopes', $claims->get('scopes')), $token);
142
    }
143
}
144