Passed
Push — master ( e886a2...8837ed )
by Andrew
29:07 queued 27:32
created

convertSingleRecordAudToString()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 1
nc 4
nop 1
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 12
rs 10
c 0
b 0
f 0
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
    /**
70 9
     * Initialise the JWT configuration.
71
     */
72 6
    private function initJwtConfiguration()
73 5
    {
74
        $this->jwtConfiguration = Configuration::forSymmetricSigner(
75 2
            new Sha256(),
76 1
            InMemory::plainText('')
77
        );
78
79
        $this->jwtConfiguration->setValidationConstraints(
80 4
            new ValidAt(new SystemClock(new DateTimeZone(\date_default_timezone_get()))),
81 4
            new SignedWith(new Sha256(), LocalFileReference::file($this->publicKey->getKeyPath()))
82
        );
83 4
    }
84 4
85
    /**
86 6
     * {@inheritdoc}
87
     */
88 2
    public function validateAuthorization(ServerRequestInterface $request)
89 4
    {
90
        if ($request->hasHeader('authorization') === false) {
91 1
            throw OAuthServerException::accessDenied('Missing "Authorization" header');
92
        }
93
94
        $header = $request->getHeader('authorization');
95 3
        $jwt = \trim((string) \preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
96 1
97
        try {
98
            // Attempt to parse and validate the JWT
99
            $token = $this->jwtConfiguration->parser()->parse($jwt);
100
101 2
            $constraints = $this->jwtConfiguration->validationConstraints();
102 2
103 2
            try {
104 2
                $this->jwtConfiguration->validator()->assert($token, ...$constraints);
105
            } catch (RequiredConstraintsViolated $exception) {
106
                throw OAuthServerException::accessDenied('Access token could not be verified');
107
            }
108
        } catch (CannotDecodeContent | InvalidTokenStructure | UnsupportedHeaderFound $exception) {
109
            throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
110
        }
111
112
        $claims = $token->claims();
113
114
        // Check if token has been revoked
115
        if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {
116
            throw OAuthServerException::accessDenied('Access token has been revoked');
117
        }
118
119
        // Return the request with additional attributes
120
        return $request
121
            ->withAttribute('oauth_access_token_id', $claims->get('jti'))
122
            ->withAttribute('oauth_client_id', $this->convertSingleRecordAudToString($claims->get('aud')))
123
            ->withAttribute('oauth_user_id', $claims->get('sub'))
124
            ->withAttribute('oauth_scopes', $claims->get('scopes'));
125
    }
126
127
    /**
128
     * Convert single record arrays into strings to ensure backwards compatibility between v4 and v3.x of lcobucci/jwt
129
     *
130
     * @param mixed $aud
131
     *
132
     * @return array|string
133
     */
134
    private function convertSingleRecordAudToString($aud)
135
    {
136
        return \is_countable($aud) && \count($aud) === 1 ? $aud[0] : $aud;
137
    }
138
}
139