Passed
Push — master ( 13425d...f357db )
by Alexander
02:26
created

OpenIdConnect::getJwsLoader()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 3
nop 0
dl 0
loc 21
ccs 0
cts 10
cp 0
crap 20
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\AuthClient\Client;
6
7
use Exception;
8
use HttpException;
9
use Jose\Component\Checker\AlgorithmChecker;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Checker\AlgorithmChecker was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Jose\Component\Checker\HeaderCheckerManager;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Checker\HeaderCheckerManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Jose\Component\Core\AlgorithmManager;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Core\AlgorithmManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Jose\Component\Core\JWKSet;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Core\JWKSet was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Jose\Component\KeyManagement\JWKFactory;
0 ignored issues
show
Bug introduced by
The type Jose\Component\KeyManagement\JWKFactory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Jose\Component\Signature\JWSLoader;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Signature\JWSLoader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Jose\Component\Signature\JWSTokenSupport;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Signature\JWSTokenSupport was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Jose\Component\Signature\JWSVerifier;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Signature\JWSVerifier was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Jose\Component\Signature\Serializer\CompactSerializer;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Signature...lizer\CompactSerializer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Jose\Component\Signature\Serializer\JWSSerializerManager;
0 ignored issues
show
Bug introduced by
The type Jose\Component\Signature...er\JWSSerializerManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Psr\Http\Client\ClientInterface;
20
use Psr\Http\Message\RequestFactoryInterface;
21
use Psr\Http\Message\RequestInterface;
22
use Psr\Http\Message\ServerRequestInterface;
23
use Psr\SimpleCache\CacheInterface;
24
use Psr\SimpleCache\InvalidArgumentException;
25
use Yiisoft\Factory\FactoryInterface;
26
use Yiisoft\Json\Json;
27
use Yiisoft\Security\Random;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Security\Random was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use Yiisoft\Session\SessionInterface;
29
use Yiisoft\Yii\AuthClient\Exception\InvalidConfigException;
30
use Yiisoft\Yii\AuthClient\OAuth2;
31
use Yiisoft\Yii\AuthClient\OAuthToken;
32
use Yiisoft\Yii\AuthClient\Signature\HmacSha;
33
use Yiisoft\Yii\AuthClient\StateStorage\StateStorageInterface;
34
35
use function in_array;
36
37
/**
38
 * OpenIdConnect serves as a client for the OpenIdConnect flow.
39
 *
40
 * This class requires `web-token/jwt-checker`,`web-token/jwt-key-mgmt`, `web-token/jwt-signature`, `web-token/jwt-signature-algorithm-hmac`,
41
 * `web-token/jwt-signature-algorithm-ecdsa` and `web-token/jwt-signature-algorithm-rsa` libraries to be installed for
42
 * JWS verification. This can be done via composer:
43
 *
44
 * ```
45
 * composer require --prefer-dist "web-token/jwt-checker:>=1.0 <3.0" "web-token/jwt-signature:>=1.0 <3.0"
46
 * "web-token/jwt-signature:>=1.0 <3.0" "web-token/jwt-signature-algorithm-hmac:>=1.0 <3.0"
47
 * "web-token/jwt-signature-algorithm-ecdsa:>=1.0 <3.0" "web-token/jwt-signature-algorithm-rsa:>=1.0 <3.0"
48
 * ```
49
 *
50
 * Note: if you are using well-trusted OpenIdConnect provider, you may disable {@see validateJws}, making installation of
51
 * `web-token` library redundant, however it is not recommended as it violates the protocol specification.
52
 *
53
 * @link http://openid.net/connect/
54
 * @see OAuth2
55
 */
56
final class OpenIdConnect extends OAuth2
57
{
58
    protected ?string $scope = 'openid';
59
    /**
60
     * @var string OpenID Issuer (provider) base URL, e.g. `https://example.com`.
61
     */
62
    private string $issuerUrl;
63
    /**
64
     * @var bool whether to validate/decrypt JWS received with Auth token.
65
     * Note: this functionality requires `web-token/jwt-checker`, `web-token/jwt-key-mgmt`, `web-token/jwt-signature`
66
     * composer package to be installed. You can disable this option in case of usage of trusted OpenIDConnect provider,
67
     * however this violates the protocol rules, so you are doing it on your own risk.
68
     */
69
    private bool $validateJws = true;
70
    /**
71
     * @var array JWS algorithms, which are allowed to be used.
72
     * These are used by `web-token` library for JWS validation/decryption.
73
     * Make sure to install `web-token/jwt-signature-algorithm-hmac`, `web-token/jwt-signature-algorithm-ecdsa`
74
     * and `web-token/jwt-signature-algorithm-rsa` packages that support the particular algorithm before adding it here.
75
     */
76
    private array $allowedJwsAlgorithms = [
77
        'HS256',
78
        'HS384',
79
        'HS512',
80
        'ES256',
81
        'ES384',
82
        'ES512',
83
        'RS256',
84
        'RS384',
85
        'RS512',
86
        'PS256',
87
        'PS384',
88
        'PS512',
89
    ];
90
91
    /**
92
     * @var string the prefix for the key used to store {@see configParams} data in cache.
93
     * Actual cache key will be formed addition {@see id} value to it.
94
     *
95
     * @see cache
96
     */
97
    private string $configParamsCacheKeyPrefix = 'config-params-';
98
99
    /**
100
     * @var bool|null whether to use and validate auth 'nonce' parameter in authentication flow.
101
     * The option is used for preventing replay attacks.
102
     */
103
    private ?bool $validateAuthNonce;
104
105
    /**
106
     * @var array OpenID provider configuration parameters.
107
     */
108
    private array $configParams = [];
109
    private CacheInterface $cache;
110
    private string $name;
111
    private string $title;
112
113
    /**
114
     * @var JWSLoader JSON Web Signature
115
     */
116
    private JWSLoader $jwsLoader;
117
    /**
118
     * @var JWKSet Key Set
119
     */
120
    private JWKSet $jwkSet;
121
122
    /**
123
     * OpenIdConnect constructor.
124
     *
125
     * @param string|null $endpoint
126
     * @param $name
127
     * @param $title
128
     * @param ClientInterface $httpClient
129
     * @param RequestFactoryInterface $requestFactory
130
     * @param CacheInterface $cache
131
     * @param StateStorageInterface $stateStorage
132
     * @param SessionInterface $session
133
     */
134
    public function __construct(
135
        $name,
136
        $title,
137
        ClientInterface $httpClient,
138
        RequestFactoryInterface $requestFactory,
139
        CacheInterface $cache,
140
        StateStorageInterface $stateStorage,
141
        SessionInterface $session,
142
        FactoryInterface $factory
143
    ) {
144
        $this->name = $name;
145
        $this->title = $title;
146
        $this->cache = $cache;
147
        parent::__construct($httpClient, $requestFactory, $stateStorage, $session, $factory);
148
    }
149
150
    public function buildAuthUrl(
151
        ServerRequestInterface $incomingRequest,
152
        array $params = []
153
    ): string {
154
        if ($this->authUrl === null) {
155
            $this->authUrl = $this->getConfigParam('authorization_endpoint');
156
        }
157
        return parent::buildAuthUrl($incomingRequest, $params);
158
    }
159
160
    /**
161
     * Returns particular configuration parameter value.
162
     *
163
     * @param string $name configuration parameter name.
164
     *
165
     * @throws InvalidConfigException
166
     * @throws InvalidArgumentException
167
     *
168
     * @return mixed configuration parameter value.
169
     */
170
    public function getConfigParam($name)
171
    {
172
        $params = $this->getConfigParams();
173
        return $params[$name];
174
    }
175
176
    /**
177
     * @throws InvalidConfigException
178
     * @throws InvalidArgumentException
179
     *
180
     * @return array OpenID provider configuration parameters.
181
     */
182
    public function getConfigParams(): array
183
    {
184
        if ($this->configParams === null) {
185
            $cacheKey = $this->configParamsCacheKeyPrefix . $this->getName();
186
            if (($configParams = $this->cache->get($cacheKey)) === null) {
187
                $configParams = $this->discoverConfig();
188
            }
189
190
            $this->configParams = $configParams;
191
            $this->cache->set($cacheKey, $configParams);
192
        }
193
        return $this->configParams;
194
    }
195
196
    /**
197
     * @return string service name.
198
     */
199
    public function getName(): string
200
    {
201
        return 'open_id_connect';
202
    }
203
204
    /**
205
     * Discovers OpenID Provider configuration parameters.
206
     *
207
     * @throws InvalidConfigException
208
     *
209
     * @return array OpenID Provider configuration parameters.
210
     */
211
    private function discoverConfig(): array
212
    {
213
        if ($this->issuerUrl === null) {
214
            throw new InvalidConfigException('Cannot discover config because issuer URL is not set.');
215
        }
216
        $configUrl = $this->issuerUrl . '/.well-known/openid-configuration';
217
        $request = $this->createRequest('GET', $configUrl);
218
        $response = $this->sendRequest($request);
219
220
        return Json::decode($response->getBody()->getContents());
0 ignored issues
show
Bug Best Practice introduced by
The expression return Yiisoft\Json\Json...tBody()->getContents()) could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
221
    }
222
223
    public function fetchAccessToken(ServerRequestInterface $request, $authCode, array $params = []): OAuthToken
224
    {
225
        if ($this->tokenUrl === null) {
226
            $this->tokenUrl = $this->getConfigParam('token_endpoint');
227
        }
228
229
        if (!isset($params['nonce']) && $this->getValidateAuthNonce()) {
230
            $nonce = $this->generateAuthNonce();
231
            $this->setState('authNonce', $nonce);
232
            $params['nonce'] = $nonce;
233
        }
234
235
        return parent::fetchAccessToken($request, $authCode, $params);
236
    }
237
238
    /**
239
     * @throws InvalidConfigException
240
     * @throws InvalidArgumentException
241
     *
242
     * @return bool whether to use and validate auth 'nonce' parameter in authentication flow.
243
     */
244
    public function getValidateAuthNonce(): bool
245
    {
246
        if ($this->validateAuthNonce === null) {
247
            $this->validateAuthNonce = $this->validateJws && in_array(
248
                    'nonce',
249
                    $this->getConfigParam('claims_supported'),
250
                    true
251
                );
252
        }
253
        return $this->validateAuthNonce;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->validateAuthNonce could return the type null which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
254
    }
255
256
    /**
257
     * @param bool $validateAuthNonce whether to use and validate auth 'nonce' parameter in authentication flow.
258
     */
259
    public function setValidateAuthNonce($validateAuthNonce): void
260
    {
261
        $this->validateAuthNonce = $validateAuthNonce;
262
    }
263
264
    /**
265
     * Generates the auth nonce value.
266
     *
267
     * @throws Exception
268
     *
269
     * @return string auth nonce value.
270
     */
271
    protected function generateAuthNonce(): string
272
    {
273
        return Random::string();
274
    }
275
276
    public function refreshAccessToken(OAuthToken $token): OAuthToken
277
    {
278
        if ($this->tokenUrl === null) {
279
            $this->tokenUrl = $this->getConfigParam('token_endpoint');
280
        }
281
        return parent::refreshAccessToken($token);
282
    }
283
284
    /**
285
     * @return string service title.
286
     */
287
    public function getTitle(): string
288
    {
289
        return 'OpenID Connect';
290
    }
291
292
    public function setIssuerUrl(string $url): void
293
    {
294
        $this->issuerUrl = rtrim($url, '/');
295
    }
296
297
    protected function initUserAttributes(): array
298
    {
299
        return $this->api($this->getConfigParam('userinfo_endpoint'), 'GET');
300
    }
301
302
    protected function applyClientCredentialsToRequest(RequestInterface $request): RequestInterface
303
    {
304
        $supportedAuthMethods = $this->getConfigParam('token_endpoint_auth_methods_supported');
305
306
        if (in_array('client_secret_basic', $supportedAuthMethods, true)) {
307
            $request = $request->withHeader(
308
                'Authorization',
309
                'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret)
310
            );
311
        } elseif (in_array('client_secret_post', $supportedAuthMethods, true)) {
312
            $request = RequestUtil::addParams(
0 ignored issues
show
Bug introduced by
The type Yiisoft\Yii\AuthClient\Client\RequestUtil was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
313
                $request,
314
                [
315
                    'client_id' => $this->clientId,
316
                    'client_secret' => $this->clientSecret,
317
                ]
318
            );
319
        } elseif (in_array('client_secret_jwt', $supportedAuthMethods, true)) {
320
            $header = [
321
                'typ' => 'JWT',
322
                'alg' => 'HS256',
323
            ];
324
            $payload = [
325
                'iss' => $this->clientId,
326
                'sub' => $this->clientId,
327
                'aud' => $this->tokenUrl,
328
                'jti' => $this->generateAuthNonce(),
329
                'iat' => time(),
330
                'exp' => time() + 3600,
331
            ];
332
333
            $signatureBaseString = base64_encode(Json::encode($header)) . '.' . base64_encode(Json::encode($payload));
334
            $signatureMethod = new HmacSha('sha256');
335
            $signature = $signatureMethod->generateSignature($signatureBaseString, $this->clientSecret);
336
337
            $assertion = $signatureBaseString . '.' . $signature;
338
339
            $request = RequestUtil::addParams(
340
                $request,
341
                [
342
                    'assertion' => $assertion,
343
                ]
344
            );
345
        } else {
346
            throw new InvalidConfigException(
347
                'Unable to authenticate request: none of following auth methods is supported: ' . implode(
348
                    ', ',
349
                    $supportedAuthMethods
350
                )
351
            );
352
        }
353
        return $request;
354
    }
355
356
    protected function defaultReturnUrl(ServerRequestInterface $request): string
357
    {
358
        $params = $request->getQueryParams();
359
        // OAuth2 specifics :
360
        unset($params['code'], $params['state'], $params['nonce'], $params['authuser'], $params['session_state'], $params['prompt']);
361
        // OpenIdConnect specifics :
362
363
364
        return $request->getUri()->withQuery(http_build_query($params, '', '&', PHP_QUERY_RFC3986))->__toString();
365
    }
366
367
    protected function createToken(array $tokenConfig = []): OAuthToken
368
    {
369
        if ($this->validateJws) {
370
            $jwsData = $this->loadJws($tokenConfig['params']['id_token']);
371
            $this->validateClaims($jwsData);
372
            $tokenConfig['params'] = array_merge($tokenConfig['params'], $jwsData);
373
374
            if ($this->getValidateAuthNonce()) {
375
                $authNonce = $this->getState('authNonce');
376
                if (!isset($jwsData['nonce']) || empty($authNonce) || strcmp($jwsData['nonce'], $authNonce) !== 0) {
377
                    throw new HttpException('Invalid auth nonce', 400);
378
                }
379
380
                $this->removeState('authNonce');
381
            }
382
        }
383
384
        return parent::createToken($tokenConfig);
385
    }
386
387
    /**
388
     * Decrypts/validates JWS, returning related data.
389
     *
390
     * @param string $jws raw JWS input.
391
     *
392
     * @throws HttpException on invalid JWS signature.
393
     * @throws InvalidArgumentException
394
     *
395
     * @return array JWS underlying data.
396
     */
397
    protected function loadJws(string $jws): array
398
    {
399
        try {
400
            $jwsLoader = $this->getJwsLoader();
401
            $signature = null;
402
            $jwsVerified = $jwsLoader->loadAndVerifyWithKeySet($jws, $this->getJwkSet(), $signature);
403
            return Json::decode($jwsVerified->getPayload());
0 ignored issues
show
Bug Best Practice introduced by
The expression return Yiisoft\Json\Json...Verified->getPayload()) could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
404
        } catch (Exception $e) {
405
            $message = YII_DEBUG ? 'Unable to verify JWS: ' . $e->getMessage() : 'Invalid JWS';
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Yii\AuthClient\Client\YII_DEBUG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
406
            throw new HttpException($message, $e->getCode(), $e);
407
        }
408
    }
409
410
    /**
411
     * Return JWSLoader that validate the JWS token.
412
     *
413
     * @throws InvalidConfigException on invalid algorithm provide in configuration.
414
     *
415
     * @return JWSLoader to do token validation.
416
     */
417
    protected function getJwsLoader(): JWSLoader
418
    {
419
        if ($this->jwsLoader === null) {
420
            $algorithms = [];
421
            foreach ($this->allowedJwsAlgorithms as $algorithm) {
422
                $class = '\Jose\Component\Signature\Algorithm\\' . $algorithm;
423
                if (!class_exists($class)) {
424
                    throw new InvalidConfigException("Algorithm class $class doesn't exist");
425
                }
426
                $algorithms[] = new $class();
427
            }
428
            $this->jwsLoader = new JWSLoader(
429
                new JWSSerializerManager([new CompactSerializer()]),
430
                new JWSVerifier(new AlgorithmManager($algorithms)),
431
                new HeaderCheckerManager(
432
                    [new AlgorithmChecker($this->allowedJwsAlgorithms)],
433
                    [new JWSTokenSupport()]
434
                )
435
            );
436
        }
437
        return $this->jwsLoader;
438
    }
439
440
    /**
441
     * Return JwkSet, returning related data.
442
     *
443
     * @throws InvalidConfigException
444
     * @throws InvalidArgumentException
445
     *
446
     * @return JWKSet object represents a key set.
447
     */
448
    protected function getJwkSet(): JWKSet
449
    {
450
        if ($this->jwkSet === null) {
451
            $cacheKey = $this->configParamsCacheKeyPrefix . 'jwkSet';
452
            if (($jwkSet = $this->cache->get($cacheKey)) === false) {
453
                $request = $this->createRequest('GET', $this->getConfigParam('jwks_uri'));
454
                $response = $this->sendRequest($request);
455
                $jwkSet = JWKFactory::createFromValues($response);
456
            }
457
458
            $this->jwkSet = $jwkSet;
459
            $this->cache->set($cacheKey, $jwkSet);
460
        }
461
        return $this->jwkSet;
462
    }
463
464
    /**
465
     * Validates the claims data received from OpenID provider.
466
     *
467
     * @param array $claims claims data.
468
     *
469
     * @throws HttpException on invalid claims.
470
     */
471
    protected function validateClaims(array $claims): void
472
    {
473
        if (!isset($claims['iss']) || (strcmp(rtrim($claims['iss'], '/'), rtrim($this->issuerUrl, '/')) !== 0)) {
474
            throw new HttpException('Invalid "iss"', 400);
475
        }
476
        if (!isset($claims['aud']) || (strcmp($claims['aud'], $this->clientId) !== 0)) {
477
            throw new HttpException('Invalid "aud"', 400);
478
        }
479
    }
480
}
481