Passed
Push — master ( 35fc37...405876 )
by Andrew
01:36
created

BearerTokenValidator   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Test Coverage

Coverage 97.5%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 40
c 3
b 0
f 0
dl 0
loc 112
ccs 39
cts 40
cp 0.975
rs 10
wmc 12

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setPublicKey() 0 5 1
A __construct() 0 3 1
A initJwtConfiguration() 0 14 2
A convertSingleRecordAudToString() 0 3 3
A validateAuthorization() 0 37 5
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\AuthorizationValidators;
11
12
use DateTimeZone;
13
use Lcobucci\Clock\SystemClock;
14
use Lcobucci\JWT\Configuration;
15
use Lcobucci\JWT\Signer\Key\InMemory;
16
use Lcobucci\JWT\Signer\Rsa\Sha256;
17
use Lcobucci\JWT\Validation\Constraint\SignedWith;
18
use Lcobucci\JWT\Validation\Constraint\StrictValidAt;
19
use Lcobucci\JWT\Validation\Constraint\ValidAt;
20
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
21
use League\OAuth2\Server\CryptKey;
22
use League\OAuth2\Server\CryptTrait;
23
use League\OAuth2\Server\Exception\OAuthServerException;
24
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
25
use Psr\Http\Message\ServerRequestInterface;
26
27
class BearerTokenValidator implements AuthorizationValidatorInterface
28
{
29
    use CryptTrait;
30
31
    /**
32
     * @var AccessTokenRepositoryInterface
33
     */
34
    private $accessTokenRepository;
35
36
    /**
37
     * @var CryptKey
38
     */
39
    protected $publicKey;
40
41
    /**
42
     * @var Configuration
43
     */
44
    private $jwtConfiguration;
45
46
    /**
47
     * @param AccessTokenRepositoryInterface $accessTokenRepository
48
     */
49 11
    public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
50
    {
51 11
        $this->accessTokenRepository = $accessTokenRepository;
52 11
    }
53
54
    /**
55
     * Set the public key
56
     *
57
     * @param CryptKey $key
58
     */
59 11
    public function setPublicKey(CryptKey $key)
60
    {
61 11
        $this->publicKey = $key;
62
63 11
        $this->initJwtConfiguration();
64 11
    }
65
66
    /**
67
     * Initialise the JWT configuration.
68
     */
69 11
    private function initJwtConfiguration()
70
    {
71 11
        $this->jwtConfiguration = Configuration::forSymmetricSigner(
72 11
            new Sha256(),
73 11
            InMemory::plainText('')
74
        );
75
76 11
        $this->jwtConfiguration->setValidationConstraints(
77 11
            \class_exists(StrictValidAt::class)
78
                ? new StrictValidAt(new SystemClock(new DateTimeZone(\date_default_timezone_get())))
79 11
                : new ValidAt(new SystemClock(new DateTimeZone(\date_default_timezone_get()))),
0 ignored issues
show
Deprecated Code introduced by
The class Lcobucci\JWT\Validation\Constraint\ValidAt has been deprecated: Use \Lcobucci\JWT\Validation\Constraint\LooseValidAt ( Ignorable by Annotation )

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

79
                : /** @scrutinizer ignore-deprecated */ new ValidAt(new SystemClock(new DateTimeZone(\date_default_timezone_get()))),
Loading history...
80 11
            new SignedWith(
81 11
                new Sha256(),
82 11
                InMemory::plainText($this->publicKey->getKeyContents(), $this->publicKey->getPassPhrase() ?? '')
83
            )
84
        );
85 11
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 11
    public function validateAuthorization(ServerRequestInterface $request)
91
    {
92 11
        if ($request->hasHeader('authorization') === false) {
93 1
            throw OAuthServerException::accessDenied('Missing "Authorization" header');
94
        }
95
96 10
        $header = $request->getHeader('authorization');
97 10
        $jwt = \trim((string) \preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
98
99
        try {
100
            // Attempt to parse the JWT
101 10
            $token = $this->jwtConfiguration->parser()->parse($jwt);
102 3
        } catch (\Lcobucci\JWT\Exception $exception) {
103 3
            throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
104
        }
105
106
        try {
107
            // Attempt to validate the JWT
108 7
            $constraints = $this->jwtConfiguration->validationConstraints();
109 7
            $this->jwtConfiguration->validator()->assert($token, ...$constraints);
110 3
        } catch (RequiredConstraintsViolated $exception) {
111 3
            throw OAuthServerException::accessDenied('Access token could not be verified');
112
        }
113
114 4
        $claims = $token->claims();
115
116
        // Check if token has been revoked
117 4
        if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {
118 1
            throw OAuthServerException::accessDenied('Access token has been revoked');
119
        }
120
121
        // Return the request with additional attributes
122
        return $request
123 3
            ->withAttribute('oauth_access_token_id', $claims->get('jti'))
124 3
            ->withAttribute('oauth_client_id', $this->convertSingleRecordAudToString($claims->get('aud')))
125 3
            ->withAttribute('oauth_user_id', $claims->get('sub'))
126 3
            ->withAttribute('oauth_scopes', $claims->get('scopes'));
127
    }
128
129
    /**
130
     * Convert single record arrays into strings to ensure backwards compatibility between v4 and v3.x of lcobucci/jwt
131
     *
132
     * @param mixed $aud
133
     *
134
     * @return array|string
135
     */
136 3
    private function convertSingleRecordAudToString($aud)
137
    {
138 3
        return \is_array($aud) && \count($aud) === 1 ? $aud[0] : $aud;
139
    }
140
}
141