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, JwtValidatorInterface |
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
|
|
|
* {@inheritdoc} |
88
|
|
|
*/ |
89
|
13 |
|
public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface |
90
|
|
|
{ |
91
|
13 |
|
if ($request->hasHeader('authorization') === false) { |
92
|
1 |
|
throw OAuthServerException::accessDenied('Missing "Authorization" header'); |
93
|
|
|
} |
94
|
|
|
|
95
|
12 |
|
$header = $request->getHeader('authorization'); |
96
|
12 |
|
$jwt = trim((string) preg_replace('/^\s*Bearer\s/', '', $header[0])); |
97
|
|
|
|
98
|
12 |
|
if ($jwt === '') { |
99
|
1 |
|
throw OAuthServerException::accessDenied('Missing "Bearer" token'); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$claims = $this->validateJwt($request, $jwt); |
103
|
|
|
|
104
|
11 |
|
// Return the request with additional attributes |
105
|
2 |
|
return $request |
106
|
2 |
|
->withAttribute('oauth_access_token_id', $claims['jti'] ?? null) |
107
|
|
|
->withAttribute('oauth_client_id', $claims['aud'][0] ?? null) |
108
|
|
|
->withAttribute('oauth_user_id', $claims['sub'] ?? null) |
109
|
|
|
->withAttribute('oauth_scopes', $claims['scopes'] ?? null); |
110
|
|
|
} |
111
|
9 |
|
|
112
|
9 |
|
/** |
113
|
4 |
|
* {@inheritdoc} |
114
|
4 |
|
*/ |
115
|
|
|
public function validateJwt(ServerRequestInterface $request, string $jwt, ?string $clientId = null): array |
116
|
|
|
{ |
117
|
5 |
|
try { |
118
|
|
|
// Attempt to parse the JWT |
119
|
|
|
$token = $this->jwtConfiguration->parser()->parse($jwt); |
120
|
|
|
} catch (Exception $exception) { |
121
|
5 |
|
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception); |
122
|
|
|
} |
123
|
|
|
|
124
|
5 |
|
try { |
125
|
1 |
|
// Attempt to validate the JWT |
126
|
|
|
$constraints = $this->jwtConfiguration->validationConstraints(); |
127
|
|
|
$this->jwtConfiguration->validator()->assert($token, ...$constraints); |
128
|
|
|
} catch (RequiredConstraintsViolated $exception) { |
129
|
4 |
|
throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception); |
130
|
4 |
|
} |
131
|
4 |
|
|
132
|
4 |
|
if (!$token instanceof UnencryptedToken) { |
133
|
4 |
|
throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken'); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
$claims = $token->claims(); |
137
|
|
|
|
138
|
|
|
// Check if token is linked to the client |
139
|
|
|
if ( |
140
|
|
|
$clientId !== null && |
141
|
|
|
$claims->get('client_id') !== $clientId && |
142
|
|
|
!$token->isPermittedFor($clientId) |
143
|
|
|
) { |
144
|
|
|
throw OAuthServerException::accessDenied('Access token is not linked to client'); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// Check if token has been revoked |
148
|
|
|
if ($this->accessTokenRepository->isAccessTokenRevoked($claims->get('jti'))) { |
149
|
|
|
throw OAuthServerException::accessDenied('Access token has been revoked'); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
return $claims->all(); |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.