-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathBearerTokenValidator.php
143 lines (117 loc) · 4.64 KB
/
BearerTokenValidator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<?php
/**
* @author Alex Bilbie <[email protected]>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
declare(strict_types=1);
namespace League\OAuth2\Server\AuthorizationValidators;
use DateInterval;
use DateTimeZone;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Exception;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
use League\OAuth2\Server\CryptKeyInterface;
use League\OAuth2\Server\CryptTrait;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use RuntimeException;
use function date_default_timezone_get;
use function preg_replace;
use function trim;
class BearerTokenValidator implements AuthorizationValidatorInterface
{
use CryptTrait;
protected CryptKeyInterface $publicKey;
private Configuration $jwtConfiguration;
public function __construct(private AccessTokenRepositoryInterface $accessTokenRepository, private ?DateInterval $jwtValidAtDateLeeway = null)
{
}
/**
* Set the public key
*/
public function setPublicKey(CryptKeyInterface $key): void
{
$this->publicKey = $key;
$this->initJwtConfiguration();
}
/**
* Initialise the JWT configuration.
*/
private function initJwtConfiguration(): void
{
$this->jwtConfiguration = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText('empty', 'empty')
);
$clock = new SystemClock(new DateTimeZone(date_default_timezone_get()));
$publicKeyContents = $this->publicKey->getKeyContents();
if ($publicKeyContents === '') {
throw new RuntimeException('Public key is empty');
}
$this->jwtConfiguration->setValidationConstraints(
new LooseValidAt($clock, $this->jwtValidAtDateLeeway),
new SignedWith(
new Sha256(),
InMemory::plainText($publicKeyContents, $this->publicKey->getPassPhrase() ?? '')
)
);
}
/**
* Configure the validated authorization request instance.
*/
protected function withValidatedRequest(ServerRequestInterface $request, UnencryptedToken $token): ServerRequestInterface
{
return $request;
}
/**
* {@inheritdoc}
*/
public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface
{
if ($request->hasHeader('authorization') === false) {
throw OAuthServerException::accessDenied('Missing "Authorization" header');
}
$header = $request->getHeader('authorization');
$jwt = trim((string) preg_replace('/^\s*Bearer\s/', '', $header[0]));
if ($jwt === '') {
throw OAuthServerException::accessDenied('Missing "Bearer" token');
}
try {
// Attempt to parse the JWT
$token = $this->jwtConfiguration->parser()->parse($jwt);
} catch (Exception $exception) {
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
}
try {
// Attempt to validate the JWT
$constraints = $this->jwtConfiguration->validationConstraints();
$this->jwtConfiguration->validator()->assert($token, ...$constraints);
} catch (RequiredConstraintsViolated $exception) {
throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception);
}
if (!$token instanceof UnencryptedToken) {
throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken');
}
$claims = $token->claims();
// Check if token has been revoked
if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) {
throw OAuthServerException::accessDenied('Access token has been revoked');
}
// Return the request with additional attributes
return $this->withValidatedRequest($request
->withAttribute('oauth_access_token_id', $claims->get('jti'))
->withAttribute('oauth_client_id', $claims->get('aud')[0])
->withAttribute('oauth_user_id', $claims->get('sub'))
->withAttribute('oauth_scopes', $claims->get('scopes')), $token);
}
}