Passed
Pull Request — master (#1146)
by Andrew
66:38 queued 31:18
created

BearerTokenValidator   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 36
dl 0
loc 104
ccs 30
cts 30
cp 1
rs 10
c 2
b 0
f 0
wmc 11

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setPublicKey() 0 5 1
A initJwtConfiguration() 0 10 1
A __construct() 0 3 1
A validateAuthorization() 0 37 5
A convertSingleRecordAudToString() 0 3 3
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\Encoding\CannotDecodeContent;
16
use Lcobucci\JWT\Signer\Key\InMemory;
17
use Lcobucci\JWT\Signer\Key\LocalFileReference;
18
use Lcobucci\JWT\Signer\Rsa\Sha256;
19
use Lcobucci\JWT\Token\InvalidTokenStructure;
20
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
21
use Lcobucci\JWT\Validation\Constraint\SignedWith;
22
use Lcobucci\JWT\Validation\Constraint\ValidAt;
23
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
24
use League\OAuth2\Server\CryptKey;
25
use League\OAuth2\Server\CryptTrait;
26
use League\OAuth2\Server\Exception\OAuthServerException;
27
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
28
use Psr\Http\Message\ServerRequestInterface;
29
30
class BearerTokenValidator implements AuthorizationValidatorInterface
31
{
32
    use CryptTrait;
33
34
    /**
35
     * @var AccessTokenRepositoryInterface
36
     */
37
    private $accessTokenRepository;
38
39
    /**
40
     * @var CryptKey
41 10
     */
42
    protected $publicKey;
43 10
44 10
    /**
45
     * @var Configuration
46
     */
47
    private $jwtConfiguration;
48
49
    /**
50
     * @param AccessTokenRepositoryInterface $accessTokenRepository
51 10
     */
52
    public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
53 10
    {
54 10
        $this->accessTokenRepository = $accessTokenRepository;
55
    }
56
57
    /**
58
     * Set the public key
59 10
     *
60
     * @param CryptKey $key
61 10
     */
62 1
    public function setPublicKey(CryptKey $key)
63
    {
64
        $this->publicKey = $key;
65 9
66 9
        $this->initJwtConfiguration();
67
    }
68
69
    private function initJwtConfiguration()
70 9
    {
71
        $this->jwtConfiguration = Configuration::forSymmetricSigner(
72 6
            new Sha256(),
73 5
            InMemory::plainText('')
74
        );
75 2
76 1
        $this->jwtConfiguration->setValidationConstraints(
77
            new ValidAt(new SystemClock(new DateTimeZone(\date_default_timezone_get()))),
78
            new SignedWith(new Sha256(), LocalFileReference::file($this->publicKey->getKeyPath()))
79
        );
80 4
    }
81 4
82
    /**
83 4
     * {@inheritdoc}
84 4
     */
85
    public function validateAuthorization(ServerRequestInterface $request)
86 6
    {
87
        if ($request->hasHeader('authorization') === false) {
88 2
            throw OAuthServerException::accessDenied('Missing "Authorization" header');
89 4
        }
90
91 1
        $header = $request->getHeader('authorization');
92
        $jwt = \trim((string) \preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
93
94
        try {
95 3
            // Attempt to parse and validate the JWT
96 1
            $token = $this->jwtConfiguration->parser()->parse($jwt);
97
98
            $constraints = $this->jwtConfiguration->validationConstraints();
99
100
            try {
101 2
                $this->jwtConfiguration->validator()->assert($token, ...$constraints);
102 2
            } catch (RequiredConstraintsViolated $exception) {
103 2
                throw OAuthServerException::accessDenied('Access token could not be verified');
104 2
            }
105
        } catch (CannotDecodeContent | InvalidTokenStructure | UnsupportedHeaderFound $exception) {
106
            throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
107
        }
108
109
        $claims = $token->claims();
110
111
        // Check if token has been revoked
112
        if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {
113
            throw OAuthServerException::accessDenied('Access token has been revoked');
114
        }
115
116
        // Return the request with additional attributes
117
        return $request
118
            ->withAttribute('oauth_access_token_id', $claims->get('jti'))
119
            ->withAttribute('oauth_client_id', $this->convertSingleRecordAudToString($claims->get('aud')))
120
            ->withAttribute('oauth_user_id', $claims->get('sub'))
121
            ->withAttribute('oauth_scopes', $claims->get('scopes'));
122
    }
123
124
    /**
125
     * Convert single record arrays into strings to ensure backwards compatibility between v4 and v3.x of lcobucci/jwt
126
     *
127
     * @param $aud
128
     *
129
     * @return array|string
130
     */
131
    private function convertSingleRecordAudToString($aud)
132
    {
133
        return \is_countable($aud) && \count($aud) === 1 ? $aud[0] : $aud;
134
    }
135
}
136