Passed
Push — master ( a721e7...5904cd )
by Rutger
13:40
created

Oauth2Module::rotateStorageEncryptionKeys()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.072

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 15
ccs 8
cts 10
cp 0.8
rs 9.9666
cc 3
nc 3
nop 1
crap 3.072
1
<?php
2
3
/**
4
 * @link http://www.yiiframework.com/
5
 * @copyright Copyright (c) 2008 Yii Software LLC
6
 * @license http://www.yiiframework.com/license/
7
 */
8
9
namespace rhertogh\Yii2Oauth2Server;
10
11
// phpcs:disable Generic.Files.LineLength.TooLong
12
use Defuse\Crypto\Exception\BadFormatException;
13
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
14
use GuzzleHttp\Psr7\Response as Psr7Response;
15
use GuzzleHttp\Psr7\ServerRequest as Psr7ServerRequest;
16
use League\OAuth2\Server\CryptKey;
17
use League\OAuth2\Server\Grant\GrantTypeInterface;
18
use rhertogh\Yii2Oauth2Server\base\Oauth2BaseModule;
19
use rhertogh\Yii2Oauth2Server\components\server\tokens\Oauth2AccessTokenData;
20
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2ClientController;
21
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2DebugController;
22
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2EncryptionController;
23
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2MigrationsController;
24
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2PersonalAccessTokenController;
25
use rhertogh\Yii2Oauth2Server\exceptions\Oauth2ServerException;
26
use rhertogh\Yii2Oauth2Server\helpers\DiHelper;
27
use rhertogh\Yii2Oauth2Server\helpers\Psr7Helper;
28
use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\base\Oauth2BaseAuthorizationRequestInterface;
29
use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\client\Oauth2ClientAuthorizationRequestInterface;
30
use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\EndSession\Oauth2EndSessionAuthorizationRequestInterface;
31
use rhertogh\Yii2Oauth2Server\interfaces\components\common\DefaultAccessTokenTtlInterface;
32
use rhertogh\Yii2Oauth2Server\interfaces\components\encryption\Oauth2CryptographerInterface;
33
use rhertogh\Yii2Oauth2Server\interfaces\components\factories\encryption\Oauth2EncryptionKeyFactoryInterface;
34
use rhertogh\Yii2Oauth2Server\interfaces\components\factories\grants\base\Oauth2GrantTypeFactoryInterface;
35
use rhertogh\Yii2Oauth2Server\interfaces\components\openidconnect\scope\Oauth2OidcScopeCollectionInterface;
36
use rhertogh\Yii2Oauth2Server\interfaces\components\openidconnect\server\responses\Oauth2OidcBearerTokenResponseInterface;
37
use rhertogh\Yii2Oauth2Server\interfaces\components\server\Oauth2AuthorizationServerInterface;
38
use rhertogh\Yii2Oauth2Server\interfaces\components\server\Oauth2ResourceServerInterface;
39
use rhertogh\Yii2Oauth2Server\interfaces\components\server\responses\Oauth2BearerTokenResponseInterface;
40
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2CertificatesControllerInterface;
41
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2ConsentControllerInterface;
42
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2OidcControllerInterface;
43
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2ServerControllerInterface;
44
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2WellKnownControllerInterface;
45
use rhertogh\Yii2Oauth2Server\interfaces\filters\auth\Oauth2HttpBearerAuthInterface;
46
use rhertogh\Yii2Oauth2Server\interfaces\models\base\Oauth2EncryptedStorageInterface;
47
use rhertogh\Yii2Oauth2Server\interfaces\models\external\user\Oauth2OidcUserInterface;
48
use rhertogh\Yii2Oauth2Server\interfaces\models\external\user\Oauth2UserInterface;
49
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientInterface;
50
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ScopeInterface;
51
use rhertogh\Yii2Oauth2Server\traits\DefaultAccessTokenTtlTrait;
52
use Yii;
53
use yii\base\BootstrapInterface;
54
use yii\base\InvalidArgumentException;
55
use yii\base\InvalidCallException;
56
use yii\base\InvalidConfigException;
57
use yii\console\Application as ConsoleApplication;
58
use yii\helpers\ArrayHelper;
59
use yii\helpers\Json;
60
use yii\helpers\StringHelper;
61
use yii\helpers\VarDumper;
62
use yii\i18n\PhpMessageSource;
63
use yii\log\Logger;
64
use yii\validators\IpValidator;
65
use yii\web\Application as WebApplication;
66
use yii\web\GroupUrlRule;
67
use yii\web\IdentityInterface;
68
use yii\web\Response;
69
use yii\web\UrlRule;
0 ignored issues
show
Coding Style introduced by
Header blocks must be separated by a single blank line
Loading history...
70
// phpcs:enable Generic.Files.LineLength.TooLong
71
72
/**
73
 * This is the main module class for the Yii2 Oauth2 Server module.
74
 * To use it, include it as a module in the application configuration like the following:
75
 *
76
 * ~~~
77
 * return [
78
 *     'bootstrap' => ['oauth2'],
79
 *     'modules' => [
80
 *         'oauth2' => [
81
 *             'class' => 'rhertogh\Yii2Oauth2Server\Oauth2Module',
82
 *             // ... Please check docs/guide/start-installation.md further details
83
 *          ],
84
 *     ],
85
 * ]
86
 * ~~~
87
 *
88
 * @property \DateInterval|string|null $defaultAccessTokenTTL
89
 * @since 1.0.0
90
 */
91
class Oauth2Module extends Oauth2BaseModule implements BootstrapInterface, DefaultAccessTokenTtlInterface
92
{
93
    use DefaultAccessTokenTtlTrait;
94
95
    /**
96
     * Application type "web": http response.
97
     * @since 1.0.0
98
     */
99
    public const APPLICATION_TYPE_WEB = 'web';
100
    /**
101
     * Application type "console": cli response.
102
     * @since 1.0.0
103
     */
104
    public const APPLICATION_TYPE_CONSOLE = 'console';
105
    /**
106
     * Supported Application types.
107
     * @since 1.0.0
108
     */
109
    public const APPLICATION_TYPES = [
110
        self::APPLICATION_TYPE_WEB,
111
        self::APPLICATION_TYPE_CONSOLE,
112
    ];
113
114
    /**
115
     * "Authorization Server" Role, please see guide for details.
116
     * @since 1.0.0
117
     */
118
    public const SERVER_ROLE_AUTHORIZATION_SERVER = 1;
119
    /**
120
     * "Resource Server" Role, please see guide for details.
121
     * @since 1.0.0
122
     */
123
    public const SERVER_ROLE_RESOURCE_SERVER = 2;
124
125
    /**
126
     * Required settings when the server role includes Authorization Server
127
     * @since 1.0.0
128
     */
129
    protected const REQUIRED_SETTINGS_AUTHORIZATION_SERVER = [
130
        'codesEncryptionKey',
131
        'storageEncryptionKeys',
132
        'defaultStorageEncryptionKey',
133
        'privateKey',
134
        'publicKey',
135
    ];
136
137
    /**
138
     * Encrypted Models
139
     *
140
     * @since 1.0.0
141
     */
142
    protected const ENCRYPTED_MODELS = [
143
        Oauth2ClientInterface::class,
144
    ];
145
146
    /**
147
     * Required settings when the server role includes Resource Server
148
     * @since 1.0.0
149
     */
150
    protected const REQUIRED_SETTINGS_RESOURCE_SERVER = [
151
        'publicKey',
152
    ];
153
154
    /**
155
     * Prefix used in session storage of Client Authorization Requests
156
     * @since 1.0.0
157
     */
158
    protected const CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX = 'OAUTH2_CLIENT_AUTHORIZATION_REQUEST_';
159
160
    /**
161
     * Prefix used in session storage of End Session Authorization Requests.
162
     *
163
     * @since 1.0.0
164
     */
165
    protected const END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX = 'OAUTH2_END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX_'; // phpcs:ignore Generic.Files.LineLength.TooLong
166
167
    /**
168
     * Controller mapping for the module. Will be parsed on `init()`.
169
     * @since 1.0.0
170
     */
171
    protected const CONTROLLER_MAP = [
172
        self::APPLICATION_TYPE_WEB => [
173
            Oauth2ServerControllerInterface::CONTROLLER_NAME => [
174
                'controller' => Oauth2ServerControllerInterface::class,
175
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
176
            ],
177
            Oauth2ConsentControllerInterface::CONTROLLER_NAME => [
178
                'controller' => Oauth2ConsentControllerInterface::class,
179
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
180
            ],
181
            Oauth2WellKnownControllerInterface::CONTROLLER_NAME => [
182
                'controller' => Oauth2WellKnownControllerInterface::class,
183
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
184
            ],
185
            Oauth2CertificatesControllerInterface::CONTROLLER_NAME => [
186
                'controller' => Oauth2CertificatesControllerInterface::class,
187
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
188
            ],
189
            Oauth2OidcControllerInterface::CONTROLLER_NAME => [
190
                'controller' => Oauth2OidcControllerInterface::class,
191
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
192
            ],
193
        ],
194
        self::APPLICATION_TYPE_CONSOLE => [
195
            'migrations' => [
196
                'controller' => Oauth2MigrationsController::class,
197
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER,
198
            ],
199
            'client' => [
200
                'controller' => Oauth2ClientController::class,
201
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
202
            ],
203
            'encryption' => [
204
                'controller' => Oauth2EncryptionController::class,
205
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
206
            ],
207
            'debug' => [
208
                'controller' => Oauth2DebugController::class,
209
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER,
210
            ],
211
            'pat' => [
212
                'controller' => Oauth2PersonalAccessTokenController::class,
213
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
214
            ]
215
        ]
216
    ];
217
218
    /**
219
     * @inheritdoc
220
     */
221
    public $controllerNamespace = __NAMESPACE__ . '\-'; // Set explicitly via $controllerMap in `init()`.
222
223
    /**
224
     * @var string|null The application type. If `null` the type will be automatically detected.
225
     * @see APPLICATION_TYPES
226
     */
227
    public $appType = null;
228
229
    /**
230
     * @var int The Oauth 2.0 Server Roles the module will perform.
231
     * @since 1.0.0
232
     */
233
    public $serverRole = self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER;
234
235
    /**
236
     * @var string|null The private key for the server. Can be a string containing the key itself or point to a file.
237
     * When pointing to a file it's recommended to use an absolute path prefixed with 'file://' or start with
238
     * '@' to use a Yii path alias.
239
     * @see $privateKeyPassphrase For setting a passphrase for the private key.
240
     * @since 1.0.0
241
     */
242
    public $privateKey = null;
243
244
    /**
245
     * @var string|null The passphrase for the private key.
246
     * @since 1.0.0
247
     */
248
    public $privateKeyPassphrase = null;
249
    /**
250
     * @var string|null The public key for the server. Can be a string containing the key itself or point to a file.
251
     * When pointing to a file it's recommended to use an absolute path prefixed with 'file://' or start with
252
     * '@' to use a Yii path alias.
253
     * @since 1.0.0
254
     */
255
    public $publicKey = null;
256
257
    /**
258
     * @var string|null The encryption key for authorization and refresh codes.
259
     * @since 1.0.0
260
     */
261
    public $codesEncryptionKey = null;
262
263
    /**
264
     * @var string[]|string|null The encryption keys for storage like client secrets.
265
     * Where the array key is the name of the key, and the value the key itself. E.g.
266
     * `['2022-01-01' => 'def00000cb36fd6ed6641e0ad70805b28d....']`
267
     * If a string (instead of an array of strings) is specified it will be JSON decoded
268
     * it should contain an object where each property name is the name of the key, its value the key itself. E.g.
269
     * `{"2022-01-01": "def00000cb36fd6ed6641e0ad70805b28d...."}`
270
     *
271
     * @since 1.0.0
272
     */
273
    public $storageEncryptionKeys = null;
274
275
    /**
276
     * @var string|null The index of the default key in storageEncryptionKeys. E.g. 'myKey'.
277
     * @since 1.0.0
278
     */
279
    public $defaultStorageEncryptionKey = null;
280
281
    /**
282
     * @var string|string[]|null IP addresses, CIDR ranges, or range aliases that are allowed to connect over a
283
     * non-TLS connection. If `null` or an empty array LTS is always required.
284
     *
285
     * Warning: Although you can use '*' or 'any' to allow a non-TLS connection from any ip address,
286
     * doing so would most likely introduce a security risk and should be done for debugging purposes only!
287
     *
288
     * @see \yii\validators\IpValidator::$networks for a list of available alliasses.
289
     */
290
    public $nonTlsAllowedRanges = 'localhost';
291
292
    /**
293
     * @var class-string<Oauth2UserInterface>|null The Identity Class of your application,
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Oauth2UserInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Oauth2UserInterface>|null.
Loading history...
294
     * most likely the same as the 'identityClass' of your application's User Component.
295
     * @since 1.0.0
296
     */
297
    public $identityClass = null;
298
299
    /**
300
     * @var null|string Prefix used for url rules. When `null` the module's uniqueId will be used.
301
     * @since 1.0.0
302
     */
303
    public $urlRulesPrefix = null;
304
305
    /**
306
     * @var string URL path for the access token endpoint (will be prefixed with $urlRulesPrefix).
307
     * @since 1.0.0
308
     */
309
    public $authorizePath = 'authorize';
310
311
    /**
312
     * @var string URL path for the access token endpoint (will be prefixed with $urlRulesPrefix).
313
     * @since 1.0.0
314
     */
315
    public $accessTokenPath = 'access-token';
316
317
    /**
318
     * @var string URL path for the token revocation endpoint (will be prefixed with $urlRulesPrefix).
319
     * @since 1.0.0
320
     */
321
    public $tokenRevocationPath = 'revoke';
322
323
    /**
324
     * @var string URL path for the certificates jwks endpoint (will be prefixed with $urlRulesPrefix).
325
     * @since 1.0.0
326
     */
327
    public $jwksPath = 'certs';
328
329
    /**
330
     * The URL to the page where the user can perform the Client/Scope authorization
331
     * (if `null` the build in page will be used).
332
     * @return string
333
     * @since 1.0.0
334
     * @see $clientAuthorizationPath
335
     */
336
    public $clientAuthorizationUrl = null;
337
338
    /**
339
     * @var string The URL path to the build in page where the user can authorize the Client for the requested Scopes
340
     * (will be prefixed with $urlRulesPrefix).
341
     * Note: This setting will only be used if $clientAuthorizationUrl is `null`.
342
     * @since 1.0.0
343
     * @see $clientAuthorizationView
344
     */
345
    public $clientAuthorizationPath = 'authorize-client';
346
347
    /**
348
     * @var string The view to use in the "Client Authorization" action for the page where the user can
349
     * authorize the Client for the requested Scopes.
350
     * Note: This setting will only be used if $clientAuthorizationUrl is `null`.
351
     * @since 1.0.0
352
     * @see $clientAuthorizationPath
353
     */
354
    public $clientAuthorizationView = 'authorize-client';
355
356
    /**
357
     * @var bool Allow clients to invoke token revocation (RFC 7009).
358
     * @see https://datatracker.ietf.org/doc/html/rfc7009
359
     */
360
    public $enableTokenRevocation = true;
361
362
    /**
363
     * @var bool Will the server throw an exception when a Client requests an unknown or unauthorized scope
364
     * (would be silently ignored otherwise).
365
     * Note: this setting can be overwritten per client.
366
     */
367
    public $exceptionOnInvalidScope = false;
368
369
    /**
370
     * Configuration for `Oauth2Client::getRedirectUrisEnvVarConfig()` fallback (the
371
     * Oauth2Client::$envVarConfig['redirectUris'] has precedence).
372
     * When configured, environment variables specified in the `Oauth2Client` redirect URI(s) will be substituted with
373
     * their values. Please see `EnvironmentHelper::parseEnvVars()` for more details.
374
     *
375
     * Warning: This setting applies to all clients, for security it's recommended to specify this configuration at the
376
     * individual client level via its `envVarConfig` setting.
377
     *
378
     * @var array{
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{ at position 2 could not be parsed: the token is null at position 2.
Loading history...
379
     *          allowList: array,
380
     *          denyList: array|null,
381
     *          parseNested: bool,
382
     *          exceptionWhenNotSet: bool,
383
     *          exceptionWhenNotAllowed: bool,
384
     *      }|null
385
     * @see Oauth2ClientInterface::setEnvVarConfig()
386
     * @see Oauth2ClientInterface::getRedirectUrisEnvVarConfig()
387
     * @see \rhertogh\Yii2Oauth2Server\helpers\EnvironmentHelper::parseEnvVars()
388
     */
389
    public $clientRedirectUrisEnvVarConfig = null;
390
391
    /**
392
     * @var string|null The URL path to the OpenID Connect Provider Configuration Information Action.
393
     * If set to `null` the endpoint will be disabled.
394
     * Note: This path is defined in the
395
     *       [OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.4)
396
     *       specification and should normally not be changed.
397
     * @since 1.0.0
398
     */
399
    public $openIdConnectProviderConfigurationInformationPath = '.well-known/openid-configuration';
400
401
    /**
402
     * @var string The URL path to the OpenID Connect Userinfo Action (will be prefixed with $urlRulesPrefix).
403
     * Note: This setting will only be used if $enableOpenIdConnect and $openIdConnectUserinfoEndpoint are `true`.
404
     * @since 1.0.0
405
     * @see $openIdConnectUserinfoEndpoint
406
     */
407
    public $openIdConnectUserinfoPath = 'oidc/userinfo';
408
409
    /**
410
     * @var string The URL path to the OpenID Connect End Session Action (will be prefixed with $urlRulesPrefix).
411
     * Note: This setting will only be used if $enableOpenIdConnect and
412
     * $openIdConnectRpInitiatedLogoutEndpoint are `true`.
413
     * @since 1.0.0
414
     * @see $openIdConnectRpInitiatedLogoutEndpoint
415
     * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html
416
     */
417
    public $openIdConnectRpInitiatedLogoutPath = 'oidc/end-session';
418
419
    /**
420
     * The URL to the page where the user can perform the End Session (logout) confirmation
421
     * (if `null` the build in page will be used).
422
     * @return string
423
     * @since 1.0.0
424
     * @see $openIdConnectLogoutConfirmationPath
425
     */
426
    public $openIdConnectLogoutConfirmationUrl = null;
427
428
    /**
429
     * @var string The URL path to the build in page where the user can confirm the End Session (logout) request
430
     * (will be prefixed with $urlRulesPrefix).
431
     * Note: This setting will only be used if $openIdConnectLogoutConfirmationUrl is `null`.
432
     * @since 1.0.0
433
     * @see $openIdConnectLogoutConfirmationView
434
     */
435
    public $openIdConnectLogoutConfirmationPath = 'confirm-logout';
436
437
    /**
438
     * @var string The view to use in the "End Session Authorization" action for the page where the user can
439
     * authorize the End Session (logout) request.
440
     * Note: This setting will only be used if $openIdConnectLogoutConfirmationUrl is `null`.
441
     * @since 1.0.0
442
     * @see $openIdConnectLogoutConfirmationPath
443
     */
444
    public $openIdConnectLogoutConfirmationView = 'confirm-logout';
445
446
447
    /**
448
     * @var Oauth2GrantTypeFactoryInterface[]|GrantTypeInterface[]|string[]|Oauth2GrantTypeFactoryInterface|GrantTypeInterface|string|callable
449
     * The Oauth 2.0 Grant Types that the module will serve.
450
     * @since 1.0.0
451
     */
452
    public $grantTypes = [];
453
454
    /**
455
     * @var bool Should the resource server check for revocation of the access token.
456
     * @since 1.0.0
457
     */
458
    public $resourceServerAccessTokenRevocationValidation = true;
459
460
    /**
461
     * @var bool Enable support for OpenIdvConnect.
462
     * @since 1.0.0
463
     */
464
    public $enableOpenIdConnect = false;
465
466
    /**
467
     * @var bool Enable the .well-known/openid-configuration discovery endpoint.
468
     * @since 1.0.0
469
     */
470
    public $enableOpenIdConnectDiscovery = true;
471
472
    /**
473
     * @var bool include `grant_types_supported` in the OpenIdConnect Discovery.
474
     * Note: Since grant types can be specified per client not all clients might support all enabled grant types.
475
     * @since 1.0.0
476
     */
477
    public $openIdConnectDiscoveryIncludeSupportedGrantTypes = true;
478
479
    /**
480
     * @var string URL to include in the OpenID Connect Discovery Service of a page containing
481
     * human-readable information that developers might want or need to know when using the OpenID Provider.
482
     * @see 'service_documentation' in https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3
483
     * @since 1.0.0
484
     */
485
    public $openIdConnectDiscoveryServiceDocumentationUrl = null;
486
487
    /**
488
     * @var string|bool A string to a custom userinfo endpoint or `true` to enable the build in endpoint.
489
     * @since 1.0.0
490
     * @see $openIdConnectUserinfoPath
491
     */
492
    public $openIdConnectUserinfoEndpoint = true;
493
494
    /**
495
     * @var string|bool A string to a custom logout endpoint or `true` to enable the build in endpoint.
496
     * @since 1.0.0
497
     * @see $openIdConnectRpInitiatedLogoutPath
498
     * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html
499
     */
500
    public $openIdConnectRpInitiatedLogoutEndpoint = false;
501
502
    /**
503
     * @var bool Allow access to the "end session" endpoint without user authentication (in the form of the
504
     * `id_token_hint` parameter). If enabled the "end session" endpoint will always prompt the user to verify the
505
     * logout if no `id_token_hint` is provided and no redirect after logout will be performed.
506
     * Note: If disabled the client's `oidc_rp_initiated_logout` will be used
507
     * to determine whether to prompt the end-user for logout validation.
508
     * @since 1.0.0
509
     * @see $openIdConnectRpInitiatedLogoutPath
510
     * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html
511
     */
512
    public $openIdConnectAllowAnonymousRpInitiatedLogout = false;
513
514
    /**
515
     * Warning! Enabling this setting might introduce privacy concerns since the client could poll for the
516
     * online status of a user.
517
     *
518
     * @var bool If this setting is disabled in case of OpenID Connect Context the Access Token won't include a
519
     * Refresh Token when the 'offline_access' scope is not included in the authorization request.
520
     * In some cases it might be needed to always include a Refresh Token, in that case enable this setting and
521
     * implement the `Oauth2OidcUserSessionStatusInterface` on the User Identity model.
522
     * @since 1.0.0
523
     */
524
    public $openIdConnectIssueRefreshTokenWithoutOfflineAccessScope = false;
525
526
    /**
527
     * @var int The default option for "User Account Selection' when not specified for a client.
528
     * @since 1.0.0
529
     */
530
    public $defaultUserAccountSelection = self::USER_ACCOUNT_SELECTION_DISABLED;
531
532
    /**
533
     * @var bool|null Display exception messages that might leak server details. This could be useful for debugging.
534
     * In case of `null` (default) the YII_DEBUG constant will be used.
535
     * Warning: Should NOT be enabled in production!
536
     * @since 1.0.0
537
     */
538
    public $displayConfidentialExceptionMessages = null;
539
540
    /**
541
     * @var string|null The namespace with which migrations will be created (and by which they will be located).
542
     * Note: The specified namespace must be defined as a Yii alias (e.g. '@app').
543
     * @since 1.0.0
544
     */
545
    public $migrationsNamespace = null;
546
    /**
547
     * @var string|null Optional prefix used in the name of generated migrations
548
     * @since 1.0.0
549
     */
550
    public $migrationsPrefix = null;
551
    /**
552
     * @var string|array|int|null Sets the file ownership of generated migrations
553
     * @see \yii\helpers\BaseFileHelper::changeOwnership()
554
     * @since 1.0.0
555
     */
556
    public $migrationsFileOwnership = null;
557
    /**
558
     * @var int|null Sets the file mode of generated migrations
559
     * @see \yii\helpers\BaseFileHelper::changeOwnership()
560
     * @since 1.0.0
561
     */
562
    public $migrationsFileMode = null;
563
564
    /**
565
     * The log level for HTTP Client Errors (HTTP status code 400 - 499). Can be one of the following:
566
     *  - A log level of `\yii\log\Logger` => LEVEL_ERROR, LEVEL_WARNING, LEVEL_INFO, LEVEL_TRACE.
567
     *  - `0` => disable logging for HTTP Client Errors
568
     *  - null => The `YII_DEBUG` constant will be used to determine the log level.
569
     *            If `true` LEVEL_ERROR will be used, LEVEL_INFO otherwise.
570
     * @var int|null
571
     * @see \yii\log\Logger
572
     */
573
    public $httpClientErrorsLogLevel = null;
574
575
    /**
576
     * @var Oauth2AuthorizationServerInterface|null Cache for the authorization server
577
     * @since 1.0.0
578
     */
579
    protected $_authorizationServer = null;
580
581
    /**
582
     * @var Oauth2ResourceServerInterface|null Cache for the resource server
583
     * @since 1.0.0
584
     */
585
    protected $_resourceServer = null;
586
587
    /**
588
     * @var Oauth2CryptographerInterface|null Cache for the Oauth2Cryptographer
589
     * @since 1.0.0
590
     */
591
    protected $_cryptographer = null;
592
593
    /**
594
     * @var string|null The authorization header used when the authorization request was validated.
595
     * @since 1.0.0
596
     */
597
    protected $_oauthClaimsAuthorizationHeader = null;
598
599
    /**
600
     * @inheritDoc
601
     * @throws InvalidConfigException
602
     */
603 161
    public function init()
604
    {
605 161
        parent::init();
606
607 161
        $app = Yii::$app;
608
609 161
        if ($app instanceof WebApplication || $this->appType == static::APPLICATION_TYPE_WEB) {
610 31
            $controllerMap = static::CONTROLLER_MAP[static::APPLICATION_TYPE_WEB];
611 161
        } elseif ($app instanceof ConsoleApplication || $this->appType == static::APPLICATION_TYPE_CONSOLE) {
0 ignored issues
show
introduced by
$app is always a sub-type of yii\console\Application.
Loading history...
612 161
            $controllerMap = static::CONTROLLER_MAP[static::APPLICATION_TYPE_CONSOLE];
613 161
            $this->defaultRoute = 'debug';
614
        } else {
615 1
            throw new InvalidConfigException(
616 1
                'Unable to detect application type, configure it manually by setting `$appType`.'
617 1
            );
618
        }
619 161
        $controllerMap = array_filter(
620 161
            $controllerMap,
621 161
            fn($controllerSettings) => $controllerSettings['serverRole'] & $this->serverRole
622 161
        );
623 161
        $this->controllerMap = ArrayHelper::getColumn($controllerMap, 'controller');
624
625 161
        if (empty($this->identityClass)) {
626 1
            throw new InvalidConfigException('$identityClass must be set.');
627 161
        } elseif (!is_a($this->identityClass, Oauth2UserInterface::class, true)) {
628 1
            throw new InvalidConfigException(
629 1
                $this->identityClass . ' must implement ' . Oauth2UserInterface::class
630 1
            );
631
        }
632
633 161
        foreach (static::DEFAULT_INTERFACE_IMPLEMENTATIONS as $interface => $implementation) {
634 161
            if (!Yii::$container->has($interface)) {
635 161
                Yii::$container->set($interface, $implementation);
636
            }
637
        }
638
639 161
        if (empty($this->urlRulesPrefix)) {
640 161
            $this->urlRulesPrefix = $this->uniqueId;
641
        }
642
643 161
        $this->registerTranslations();
644
    }
645
646
    /**
647
     * @inheritdoc
648
     * @throws InvalidConfigException
649
     */
650 161
    public function bootstrap($app)
651
    {
652
        if (
653 161
            $app instanceof WebApplication
654 161
            && $this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER
655
        ) {
656 31
            $rules = [
657 31
                $this->accessTokenPath => Oauth2ServerControllerInterface::CONTROLLER_NAME
658 31
                    . '/' . Oauth2ServerControllerInterface::ACTION_NAME_ACCESS_TOKEN,
659 31
                $this->authorizePath => Oauth2ServerControllerInterface::CONTROLLER_NAME
660 31
                    . '/' . Oauth2ServerControllerInterface::ACTION_NAME_AUTHORIZE,
661 31
                $this->jwksPath => Oauth2CertificatesControllerInterface::CONTROLLER_NAME
662 31
                    . '/' . Oauth2CertificatesControllerInterface::ACTION_NAME_JWKS,
663 31
            ];
664
665 31
            if ($this->enableTokenRevocation) {
666 31
                $rules[$this->tokenRevocationPath] = Oauth2ServerControllerInterface::CONTROLLER_NAME
667 31
                . '/' . Oauth2ServerControllerInterface::ACTION_NAME_REVOKE;
668
            }
669
670 31
            if (empty($this->clientAuthorizationUrl)) {
671 30
                $rules[$this->clientAuthorizationPath] = Oauth2ConsentControllerInterface::CONTROLLER_NAME
672 30
                    . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_CLIENT;
673
            }
674
675 31
            if ($this->enableOpenIdConnect && $this->openIdConnectUserinfoEndpoint === true) {
676 31
                $rules[$this->openIdConnectUserinfoPath] =
677 31
                    Oauth2OidcControllerInterface::CONTROLLER_NAME
678 31
                    . '/' . Oauth2OidcControllerInterface::ACTION_NAME_USERINFO;
679
            }
680
681 31
            if ($this->enableOpenIdConnect && $this->openIdConnectRpInitiatedLogoutEndpoint === true) {
682 31
                $rules[$this->openIdConnectRpInitiatedLogoutPath] =
683 31
                    Oauth2OidcControllerInterface::CONTROLLER_NAME
684 31
                    . '/' . Oauth2OidcControllerInterface::ACTION_END_SESSION;
685
686 31
                if (empty($this->openIdConnectLogoutConfirmationUrl)) {
687 31
                    $rules[$this->openIdConnectLogoutConfirmationPath] =
688 31
                        Oauth2ConsentControllerInterface::CONTROLLER_NAME
689 31
                        . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_END_SESSION;
690
                }
691
            }
692
693 31
            $urlManager = $app->getUrlManager();
694 31
            $urlManager->addRules([
695 31
                Yii::createObject([
696 31
                    'class' => GroupUrlRule::class,
697 31
                    'prefix' => $this->urlRulesPrefix,
698 31
                    'routePrefix' => $this->id,
699 31
                    'rules' => $rules,
700 31
                ]),
701 31
            ]);
702
703
            if (
704 31
                $this->enableOpenIdConnect
705 31
                && $this->enableOpenIdConnectDiscovery
706 31
                && $this->openIdConnectProviderConfigurationInformationPath
707
            ) {
708 31
                $urlManager->addRules([
709 31
                    Yii::createObject([
710 31
                        'class' => UrlRule::class,
711 31
                        'pattern' => $this->openIdConnectProviderConfigurationInformationPath,
712 31
                        'route' => $this->id
713 31
                            . '/' . Oauth2WellKnownControllerInterface::CONTROLLER_NAME
714 31
                            . '/' . Oauth2WellKnownControllerInterface::ACTION_NAME_OPENID_CONFIGURATION,
715 31
                    ]),
716 31
                ]);
717
            }
718
        }
719
    }
720
721
    /**
722
     * Registers the translations for the module
723
     * @param bool $force Force the setting of the translations (even if they are already defined).
724
     * @since 1.0.0
725
     */
726 161
    public function registerTranslations($force = false)
727
    {
728 161
        if ($force || !array_key_exists('oauth2', Yii::$app->i18n->translations)) {
729 161
            Yii::$app->i18n->translations['oauth2'] = [
730 161
                'class' => PhpMessageSource::class,
731 161
                'sourceLanguage' => 'en-US',
732 161
                'basePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
733 161
                'fileMap' => [
734 161
                    'oauth2' => 'oauth2.php',
735 161
                ],
736 161
            ];
737
        }
738
    }
739
740
    /**
741
     * @param string $identifier The client identifier
742
     * @param string $name The (user-friendly) name of the client
743
     * @param int $grantTypes The grant types enabled for this client.
744
     *        Use bitwise `OR` to combine multiple types,
745
     *        e.g. `Oauth2Module::GRANT_TYPE_AUTH_CODE | Oauth2Module::GRANT_TYPE_REFRESH_TOKEN`
746
     * @param string|string[] $redirectURIs One or multiple redirect URIs for the client
747
     * @param int $type The client type (e.g. Confidential or Public)
748
     *        See `\rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientInterface::TYPES` for possible values
749
     * @param string|null $secret The client secret in case the client `type` is `confidential`.
750
     * @param string|string[]|array[]|Oauth2ScopeInterface[]|null $scopes
751
     * @param int|null $userId
752
     * @return Oauth2ClientInterface
753
     * @throws InvalidConfigException
754
     * @throws \yii\db\Exception
755
     */
756 5
    public function createClient(
757
        $identifier,
758
        $name,
759
        $grantTypes,
760
        $redirectURIs,
761
        $type,
762
        $secret = null,
763
        $scopes = null,
764
        $userId = null,
765
        $endUsersMayAuthorizeClient = null,
766
        $skipAuthorizationIfScopeIsAllowed = null
767
    ) {
768 5
        if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) {
769 1
            throw new InvalidCallException('Oauth2 server role does not include authorization server.');
770
        }
771
772
        /** @var Oauth2ClientInterface $client */
773 4
        $client = Yii::createObject([
774 4
            'class' => Oauth2ClientInterface::class,
775 4
            'identifier' => $identifier,
776 4
            'type' => $type,
777 4
            'name' => $name,
778 4
            'redirectUri' => $redirectURIs,
779 4
            'grantTypes' => $grantTypes,
780 4
            'endUsersMayAuthorizeClient' => $endUsersMayAuthorizeClient,
781 4
            'skip_authorization_if_scope_is_allowed' => $skipAuthorizationIfScopeIsAllowed,
782 4
            'clientCredentialsGrantUserId' => $userId
783 4
        ]);
784
785 4
        $transaction = $client::getDb()->beginTransaction();
786
787
        try {
788 4
            if ($type == Oauth2ClientInterface::TYPE_CONFIDENTIAL) {
789 4
                $client->setSecret($secret, $this->getCryptographer());
790
            }
791
792 3
            $client
793 3
                ->persist()
794 3
                ->syncClientScopes($scopes, $this->getScopeRepository());
795
796 3
            $transaction->commit();
797 1
        } catch (\Exception $e) {
798 1
            $transaction->rollBack();
799 1
            throw $e;
800
        }
801
802 3
        return $client;
803
    }
804
805
    /**
806
     * @return CryptKey The private key of the server.
807
     * @throws InvalidConfigException
808
     * @since 1.0.0
809
     */
810 22
    public function getPrivateKey()
811
    {
812 22
        $privateKey = $this->privateKey;
813 22
        if (StringHelper::startsWith($privateKey, '@')) {
814 19
            $privateKey = 'file://' . Yii::getAlias($privateKey);
0 ignored issues
show
Bug introduced by
Are you sure Yii::getAlias($privateKey) of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

814
            $privateKey = 'file://' . /** @scrutinizer ignore-type */ Yii::getAlias($privateKey);
Loading history...
815
        }
816 22
        return Yii::createObject(CryptKey::class, [$privateKey, $this->privateKeyPassphrase]);
817
    }
818
819
    /**
820
     * @return CryptKey The public key of the server.
821
     * @throws InvalidConfigException
822
     * @since 1.0.0
823
     */
824 9
    public function getPublicKey()
825
    {
826 9
        $publicKey = $this->publicKey;
827 9
        if (StringHelper::startsWith($publicKey, '@')) {
828 6
            $publicKey = 'file://' . Yii::getAlias($publicKey);
0 ignored issues
show
Bug introduced by
Are you sure Yii::getAlias($publicKey) of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

828
            $publicKey = 'file://' . /** @scrutinizer ignore-type */ Yii::getAlias($publicKey);
Loading history...
829
        }
830 9
        return Yii::createObject(CryptKey::class, [$publicKey]);
831
    }
832
833
    /**
834
     * @return Oauth2AuthorizationServerInterface The authorization server.
835
     * @throws InvalidConfigException
836
     * @since 1.0.0
837
     */
838 27
    public function getAuthorizationServer()
839
    {
840 27
        if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) {
841 1
            throw new InvalidCallException('Oauth2 server role does not include authorization server.');
842
        }
843
844 26
        if (!$this->_authorizationServer) {
845 26
            $this->ensureProperties(static::REQUIRED_SETTINGS_AUTHORIZATION_SERVER);
846
847 21
            if (!$this->getCryptographer()->hasKey($this->defaultStorageEncryptionKey)) {
848 1
                throw new InvalidConfigException(
849 1
                    'Key "' . $this->defaultStorageEncryptionKey . '" is not set in $storageEncryptionKeys'
850 1
                );
851
            }
852
853
            /** @var Oauth2EncryptionKeyFactoryInterface $keyFactory */
854 19
            $keyFactory = Yii::createObject(Oauth2EncryptionKeyFactoryInterface::class);
855
            try {
856 19
                $codesEncryptionKey = $keyFactory->createFromAsciiSafeString($this->codesEncryptionKey);
857 1
            } catch (BadFormatException $e) {
858 1
                throw new InvalidConfigException(
859 1
                    '$codesEncryptionKey is malformed: ' . $e->getMessage(),
860 1
                    0,
861 1
                    $e
862 1
                );
863
            } catch (EnvironmentIsBrokenException $e) {
864
                throw new InvalidConfigException(
865
                    'Could not instantiate $codesEncryptionKey: ' . $e->getMessage(),
866
                    0,
867
                    $e
868
                );
869
            }
870
871 18
            if ($this->enableOpenIdConnect) {
872 18
                $responseTypeClass = Oauth2OidcBearerTokenResponseInterface::class;
873
            } else {
874
                $responseTypeClass = Oauth2BearerTokenResponseInterface::class;
875
            }
876 18
            $responseType = Yii::createObject($responseTypeClass, [
877 18
                $this,
878 18
            ]);
879
880 18
            $this->_authorizationServer = Yii::createObject(Oauth2AuthorizationServerInterface::class, [
881 18
                $this->getClientRepository(),
882 18
                $this->getAccessTokenRepository(),
883 18
                $this->getScopeRepository(),
884 18
                $this->getPrivateKey(),
885 18
                $codesEncryptionKey,
886 18
                $responseType
887 18
            ]);
888
889 18
            if (!empty($this->grantTypes)) {
890 18
                $grantTypes = $this->grantTypes;
891
892 18
                if (is_callable($grantTypes)) {
893 1
                    call_user_func($grantTypes, $this->_authorizationServer, $this);
0 ignored issues
show
Bug introduced by
It seems like $grantTypes can also be of type League\OAuth2\Server\Grant\GrantTypeInterface and rhertogh\Yii2Oauth2Serve...antTypeFactoryInterface; however, parameter $callback of call_user_func() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

893
                    call_user_func(/** @scrutinizer ignore-type */ $grantTypes, $this->_authorizationServer, $this);
Loading history...
894
                } else {
895 17
                    if (!is_array($grantTypes)) {
896 2
                        $grantTypes = [$grantTypes];
897
                    }
898
899 17
                    foreach ($grantTypes as $grantTypeDefinition) {
900 17
                        if ($grantTypeDefinition instanceof GrantTypeInterface) {
901 1
                            $accessTokenTTL = $this->getDefaultAccessTokenTTL();
902 1
                            $this->_authorizationServer->enableGrantType($grantTypeDefinition, $accessTokenTTL);
903
                        } elseif (
904
                            (
905 16
                                is_numeric($grantTypeDefinition)
906 16
                                && array_key_exists($grantTypeDefinition, static::DEFAULT_GRANT_TYPE_FACTORIES)
907
                            )
908 16
                            || is_a($grantTypeDefinition, Oauth2GrantTypeFactoryInterface::class, true)
909
                        ) {
910
                            if (
911 15
                                is_numeric($grantTypeDefinition)
912 15
                                && array_key_exists($grantTypeDefinition, static::DEFAULT_GRANT_TYPE_FACTORIES)
913
                            ) {
914 15
                                $grantTypeDefinition = static::DEFAULT_GRANT_TYPE_FACTORIES[$grantTypeDefinition];
915
                            }
916
917
                            /** @var Oauth2GrantTypeFactoryInterface $factory */
918 15
                            $factory = Yii::createObject([
919 15
                                'class' => $grantTypeDefinition,
920 15
                                'module' => $this,
921 15
                            ]);
922 15
                            $accessTokenTTL = $factory->getDefaultAccessTokenTTL()
923 15
                                ?? $this->getDefaultAccessTokenTTL();
924 15
                            $this->_authorizationServer->enableGrantType($factory->getGrantType(), $accessTokenTTL);
925
                        } else {
926 1
                            throw new InvalidConfigException(
927 1
                                'Unknown grantType '
928 1
                                . (
929 1
                                    is_scalar($grantTypeDefinition)
930 1
                                        ? '"' . $grantTypeDefinition . '".'
931 1
                                        : 'with data type ' . gettype($grantTypeDefinition)
932 1
                                )
933 1
                            );
934
                        }
935
                    }
936
                }
937
            }
938
        }
939
940 17
        return $this->_authorizationServer;
941
    }
942
943
    /**
944
     * @inheritDoc
945
     * @throws InvalidConfigException
946
     */
947 6
    public function getOidcScopeCollection()
948
    {
949 6
        if ($this->_oidcScopeCollection === null) {
950 6
            $openIdConnectScopes = $this->getOpenIdConnectScopes();
951 6
            if ($openIdConnectScopes instanceof Oauth2OidcScopeCollectionInterface) {
952 1
                $this->_oidcScopeCollection = $openIdConnectScopes;
953 5
            } elseif (is_callable($openIdConnectScopes)) {
954 1
                $this->_oidcScopeCollection = call_user_func($openIdConnectScopes, $this);
955 1
                if (!($this->_oidcScopeCollection instanceof Oauth2OidcScopeCollectionInterface)) {
956 1
                    throw new InvalidConfigException(
957 1
                        '$openIdConnectScopes must return an instance of '
958 1
                            . Oauth2OidcScopeCollectionInterface::class
959 1
                    );
960
                }
961 4
            } elseif (is_array($openIdConnectScopes) || is_string($openIdConnectScopes)) {
962 3
                $this->_oidcScopeCollection = Yii::createObject([
963 3
                    'class' => Oauth2OidcScopeCollectionInterface::class,
964 3
                    'oidcScopes' => (array)$openIdConnectScopes,
965 3
                ]);
966
            } else {
967 1
                throw new InvalidConfigException(
968 1
                    '$openIdConnectScopes must be a callable, array, string or '
969 1
                        . Oauth2OidcScopeCollectionInterface::class
970 1
                );
971
            }
972
        }
973
974 5
        return $this->_oidcScopeCollection;
975
    }
976
977
    /**
978
     * @return Oauth2ResourceServerInterface The resource server.
979
     * @throws InvalidConfigException
980
     * @since 1.0.0
981
     */
982 7
    public function getResourceServer()
983
    {
984 7
        if (!($this->serverRole & static::SERVER_ROLE_RESOURCE_SERVER)) {
985 1
            throw new InvalidCallException('Oauth2 server role does not include resource server.');
986
        }
987
988 6
        if (!$this->_resourceServer) {
989 6
            $this->ensureProperties(static::REQUIRED_SETTINGS_RESOURCE_SERVER);
990
991 5
            $accessTokenRepository = $this->getAccessTokenRepository()
992 5
                ->setRevocationValidation($this->resourceServerAccessTokenRevocationValidation);
993
994 5
            $this->_resourceServer = Yii::createObject(Oauth2ResourceServerInterface::class, [
995 5
                $accessTokenRepository,
996 5
                $this->getPublicKey(),
997 5
            ]);
998
        }
999
1000 5
        return $this->_resourceServer;
1001
    }
1002
1003
    /**
1004
     * @return Oauth2CryptographerInterface The data cryptographer for the module.
1005
     * @throws InvalidConfigException
1006
     * @since 1.0.0
1007
     */
1008 27
    public function getCryptographer()
1009
    {
1010 27
        if (!$this->_cryptographer) {
1011 27
            $this->_cryptographer = Yii::createObject([
1012 27
                'class' => Oauth2CryptographerInterface::class,
1013 27
                'keys' => $this->storageEncryptionKeys,
1014 27
                'defaultKeyName' => $this->defaultStorageEncryptionKey,
1015 27
            ]);
1016
        }
1017
1018 26
        return $this->_cryptographer;
1019
    }
1020
1021
    /**
1022
     * @param string|null $newKeyName
1023
     * @return array
1024
     * @throws InvalidConfigException
1025
     */
1026 1
    public function rotateStorageEncryptionKeys($newKeyName = null)
1027
    {
1028 1
        $cryptographer = $this->getCryptographer();
1029
1030 1
        $result = [];
1031 1
        foreach (static::ENCRYPTED_MODELS as $modelInterface) {
1032 1
            $modelClass = DiHelper::getValidatedClassName($modelInterface);
1033 1
            if (!is_a($modelClass, Oauth2EncryptedStorageInterface::class, true)) {
1034
                throw new InvalidConfigException($modelInterface . ' must implement '
1035
                    . Oauth2EncryptedStorageInterface::class);
1036
            }
1037 1
            $result[$modelClass] = $modelClass::rotateStorageEncryptionKeys($cryptographer, $newKeyName);
1038
        }
1039
1040 1
        return $result;
1041
    }
1042
1043
    /**
1044
     * Checks if the connection is using TLS or if the remote IP address is allowed to connect without TLS.
1045
     * @return bool
1046
     */
1047 12
    public function validateTlsConnection()
1048
    {
1049 12
        if (Yii::$app->request->getIsSecureConnection()) {
1050 1
            return true;
1051
        }
1052
1053
        if (
1054 11
            !empty($this->nonTlsAllowedRanges)
1055 11
            && (new IpValidator(['ranges' => $this->nonTlsAllowedRanges]))->validate(Yii::$app->request->getRemoteIP())
1056
        ) {
1057 7
            return true;
1058
        }
1059
1060 4
        return false;
1061
    }
1062
1063
    /**
1064
     * @return array
1065
     * @throws InvalidConfigException
1066
     */
1067
    public function getStorageEncryptionKeyUsage()
1068
    {
1069
        $cryptographer = $this->getCryptographer();
1070
1071
        $result = [];
1072
        foreach (static::ENCRYPTED_MODELS as $modelInterface) {
1073
            $modelClass = DiHelper::getValidatedClassName($modelInterface);
1074
            if (!is_a($modelClass, Oauth2EncryptedStorageInterface::class, true)) {
1075
                throw new InvalidConfigException($modelInterface . ' must implement '
1076
                    . Oauth2EncryptedStorageInterface::class);
1077
            }
1078
1079
            $result[$modelClass] = $modelClass::getUsedStorageEncryptionKeys($cryptographer);
1080
        }
1081
1082
        return $result;
1083
    }
1084
1085
    /**
1086
     * @param Oauth2ClientInterface $client
1087
     * @param string[] $requestedScopeIdentifiers
1088
     * @throws Oauth2ServerException
1089
     */
1090 6
    public function validateAuthRequestScopes($client, $requestedScopeIdentifiers, $redirectUri = null)
1091
    {
1092 6
        if (!$client->validateAuthRequestScopes($requestedScopeIdentifiers, $unknownScopes, $unauthorizedScopes)) {
1093
            Yii::info('Invalid scope for client "' . $client->getIdentifier() . '": '
1094
                . VarDumper::export(['unauthorizedScopes' => $unauthorizedScopes, 'unknownScopes' => $unknownScopes]));
1095
1096
            if (
1097
                $client->getExceptionOnInvalidScope() === true
1098
                || (
1099
                    $client->getExceptionOnInvalidScope() === null
1100
                    && $this->exceptionOnInvalidScope === true
1101
                )
1102
            ) {
1103
                if ($unknownScopes) {
1104
                    throw Oauth2ServerException::unknownScope(array_shift($unknownScopes), $redirectUri);
1105
                }
1106
                throw Oauth2ServerException::scopeNotAllowedForClient(array_shift($unauthorizedScopes), $redirectUri);
1107
            }
1108
        }
1109
    }
1110
1111
    /**
1112
     * Generates a redirect Response to the Client Authorization page where the user is prompted to authorize the
1113
     * Client and requested Scope.
1114
     * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest
1115
     * @return Response
1116
     * @since 1.0.0
1117
     */
1118 5
    public function generateClientAuthReqRedirectResponse($clientAuthorizationRequest)
1119
    {
1120 5
        $this->setClientAuthReqSession($clientAuthorizationRequest);
1121 5
        if (!empty($this->clientAuthorizationUrl)) {
1122 1
            $url = $this->clientAuthorizationUrl;
1123
        } else {
1124 4
            $url = $this->uniqueId
1125 4
                . '/' . Oauth2ConsentControllerInterface::CONTROLLER_NAME
1126 4
                . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_CLIENT;
1127
        }
1128 5
        return Yii::$app->response->redirect([
1129 5
            $url,
1130 5
            'clientAuthorizationRequestId' => $clientAuthorizationRequest->getRequestId(),
1131 5
        ]);
1132
    }
1133
1134
    /**
1135
     * Generates a redirect Response to the End Session Authorization page where the user is prompted to authorize the
1136
     * logout.
1137
     * @param Oauth2EndSessionAuthorizationRequestInterface $endSessionAuthorizationRequest
1138
     * @return Response
1139
     * @since 1.0.0
1140
     */
1141
    public function generateEndSessionAuthReqRedirectResponse($endSessionAuthorizationRequest)
1142
    {
1143
        $this->setEndSessionAuthReqSession($endSessionAuthorizationRequest);
1144
        if (!empty($this->openIdConnectLogoutConfirmationUrl)) {
1145
            $url = $this->openIdConnectLogoutConfirmationUrl;
1146
        } else {
1147
            $url = $this->uniqueId
1148
                . '/' . Oauth2ConsentControllerInterface::CONTROLLER_NAME
1149
                . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_END_SESSION;
1150
        }
1151
        return Yii::$app->response->redirect([
1152
            $url,
1153
            'endSessionAuthorizationRequestId' => $endSessionAuthorizationRequest->getRequestId(),
1154
        ]);
1155
    }
1156
1157
    /**
1158
     * Get a previously stored Client Authorization Request from the session.
1159
     * @param string $requestId
1160
     * @return Oauth2ClientAuthorizationRequestInterface|null
1161
     * @since 1.0.0
1162
     */
1163 5
    public function getClientAuthReqSession($requestId)
1164
    {
1165 5
        return $this->getAuthReqSession(
1166 5
            $requestId,
1167 5
            static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX,
1168 5
            Oauth2ClientAuthorizationRequestInterface::class,
1169 5
        );
1170
    }
1171
1172
    /**
1173
     * Get a previously stored OIDC End Session Authorization Request from the session.
1174
     * @param string $requestId
1175
     * @return Oauth2EndSessionAuthorizationRequestInterface|null
1176
     * @since 1.0.0
1177
     */
1178
    public function getEndSessionAuthReqSession($requestId)
1179
    {
1180
        return $this->getAuthReqSession(
1181
            $requestId,
1182
            static::END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX,
1183
            Oauth2EndSessionAuthorizationRequestInterface::class,
1184
        );
1185
    }
1186
1187
    /**
1188
     * Get a previously stored Authorization Request from the session.
1189
     * @template T of Oauth2BaseAuthorizationRequestInterface
1190
     * @param string $requestId
1191
     * @param string $cachePrefix
1192
     * @param class-string<T> $expectedInterface
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
1193
     * @return T|null
1194
     * @since 1.0.0
1195
     */
1196 5
    protected function getAuthReqSession($requestId, $cachePrefix, $expectedInterface)
1197
    {
1198 5
        if (empty($requestId)) {
1199
            return null;
1200
        }
1201 5
        $key = $cachePrefix . $requestId;
1202 5
        $authorizationRequest = Yii::$app->session->get($key);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1202
        /** @scrutinizer ignore-call */ 
1203
        $authorizationRequest = Yii::$app->session->get($key);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1203 5
        if (!($authorizationRequest instanceof $expectedInterface)) {
1204 2
            if (!empty($authorizationRequest)) {
1205 1
                Yii::warning(
1206 1
                    'Found a Authorization Request Session with key "' . $key
1207 1
                    . '", but it\'s not a ' . $expectedInterface
1208 1
                );
1209
            }
1210 2
            return null;
1211
        }
1212 5
        if ($authorizationRequest->getRequestId() !== $requestId) {
1213 1
            Yii::warning(
1214 1
                'Found a Authorization Request Session with key "' . $key
1215 1
                . '", but its request id does not match "' . $requestId . '".'
1216 1
            );
1217 1
            return null;
1218
        }
1219 5
        $authorizationRequest->setModule($this);
1220
1221 5
        return $authorizationRequest;
1222
    }
1223
1224
    /**
1225
     * Stores the Client Authorization Request in the session.
1226
     * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest
1227
     * @since 1.0.0
1228
     */
1229 8
    public function setClientAuthReqSession($clientAuthorizationRequest)
1230
    {
1231 8
        $this->setAuthReqSession(
1232 8
            $clientAuthorizationRequest,
1233 8
            static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX
1234 8
        );
1235
    }
1236
1237
    /**
1238
     * Stores the OIDC End Session Authorization Request in the session.
1239
     * @param Oauth2EndSessionAuthorizationRequestInterface $endSessionAuthorizationRequest
1240
     * @since 1.0.0
1241
     */
1242
    public function setEndSessionAuthReqSession($endSessionAuthorizationRequest)
1243
    {
1244
        $this->setAuthReqSession(
1245
            $endSessionAuthorizationRequest,
1246
            static::END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX
1247
        );
1248
    }
1249
1250
    /**
1251
     * Stores the Authorization Request in the session.
1252
     * @param Oauth2BaseAuthorizationRequestInterface $authorizationRequest
1253
     * @param string $cachePrefix
1254
     * @since 1.0.0
1255
     */
1256 8
    protected function setAuthReqSession($authorizationRequest, $cachePrefix)
1257
    {
1258 8
        $requestId = $authorizationRequest->getRequestId();
1259 8
        if (empty($requestId)) {
1260 1
            throw new InvalidArgumentException('$authorizationRequest must return a request id.');
1261
        }
1262 7
        $key = $cachePrefix . $requestId;
1263 7
        Yii::$app->session->set($key, $authorizationRequest);
1264
    }
1265
1266
    /**
1267
     * Clears a Client Authorization Request from the session storage.
1268
     * @param string $requestId
1269
     * @since 1.0.0
1270
     */
1271 2
    public function removeClientAuthReqSession($requestId)
1272
    {
1273 2
        $this->removeAuthReqSession($requestId, static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX);
1274
    }
1275
1276
    /**
1277
     * Clears an End Session Authorization Request from the session storage.
1278
     * @param string $requestId
1279
     * @since 1.0.0
1280
     */
1281
    public function removeEndSessionAuthReqSession($requestId)
1282
    {
1283
        $this->removeAuthReqSession($requestId, static::END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX);
1284
    }
1285
1286
    /**
1287
     * Clears an Authorization Request from the session storage.
1288
     * @param string $requestId
1289
     * @param string $cachePrefix
1290
     * @since 1.0.0
1291
     */
1292 2
    public function removeAuthReqSession($requestId, $cachePrefix)
1293
    {
1294 2
        if (empty($requestId)) {
1295 1
            throw new InvalidArgumentException('$requestId can not be empty.');
1296
        }
1297 1
        $key = $cachePrefix . $requestId;
1298 1
        Yii::$app->session->remove($key);
1299
    }
1300
1301
    /**
1302
     * Stores whether the user was authenticated during the completion of the Client Authorization Request.
1303
     * @param string $clientAuthorizationRequestId
1304
     * @param bool $authenticatedDuringRequest
1305
     * @since 1.0.0
1306
     */
1307
    public function setUserAuthenticatedDuringClientAuthRequest(
1308
        $clientAuthorizationRequestId,
1309
        $authenticatedDuringRequest
1310
    ) {
1311
        $clientAuthorizationRequest = $this->getClientAuthReqSession($clientAuthorizationRequestId);
1312
        if ($clientAuthorizationRequest) {
1313
            $clientAuthorizationRequest->setUserAuthenticatedDuringRequest($authenticatedDuringRequest);
1314
            $this->setClientAuthReqSession($clientAuthorizationRequest);
1315
        }
1316
    }
1317
1318
    /**
1319
     * Stores the user identity selected during the completion of the Client Authorization Request.
1320
     * @param string $clientAuthorizationRequestId
1321
     * @param Oauth2UserInterface $userIdentity
1322
     * @since 1.0.0
1323
     */
1324
    public function setClientAuthRequestUserIdentity($clientAuthorizationRequestId, $userIdentity)
1325
    {
1326
        $clientAuthorizationRequest = $this->getClientAuthReqSession($clientAuthorizationRequestId);
1327
        if ($clientAuthorizationRequest) {
1328
            $clientAuthorizationRequest->setUserIdentity($userIdentity);
1329
            $this->setClientAuthReqSession($clientAuthorizationRequest);
1330
        }
1331
    }
1332
1333
    /**
1334
     * Generates a redirect Response when the Client Authorization Request is completed.
1335
     * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest
1336
     * @return Response
1337
     * @since 1.0.0
1338
     */
1339 1
    public function generateClientAuthReqCompledRedirectResponse($clientAuthorizationRequest)
1340
    {
1341 1
        $clientAuthorizationRequest->processAuthorization();
1342 1
        $this->setClientAuthReqSession($clientAuthorizationRequest);
1343 1
        return Yii::$app->response->redirect($clientAuthorizationRequest->getAuthorizationRequestUrl());
1344
    }
1345
1346
    /**
1347
     * Generates a redirect Response when the End Session Authorization Request is completed.
1348
     * @param Oauth2EndSessionAuthorizationRequestInterface $endSessionAuthorizationRequest
1349
     * @return Response
1350
     * @since 1.0.0
1351
     */
1352
    public function generateEndSessionAuthReqCompledRedirectResponse($endSessionAuthorizationRequest)
1353
    {
1354
        $endSessionAuthorizationRequest->processAuthorization();
1355
        $this->setEndSessionAuthReqSession($endSessionAuthorizationRequest);
1356
        return Yii::$app->response->redirect($endSessionAuthorizationRequest->getEndSessionRequestUrl());
1357
    }
1358
1359
    /**
1360
     * @return IdentityInterface|Oauth2UserInterface|Oauth2OidcUserInterface|null
1361
     * @throws InvalidConfigException
1362
     * @since 1.0.0
1363
     */
1364 5
    public function getUserIdentity()
1365
    {
1366 5
        $user = Yii::$app->user->identity;
1367 5
        if (!empty($user) && !($user instanceof Oauth2UserInterface)) {
1368 1
            throw new InvalidConfigException(
1369 1
                'Yii::$app->user->identity (currently ' . get_class($user)
1370 1
                    . ') must implement ' . Oauth2UserInterface::class
1371 1
            );
1372
        }
1373 4
        return $user;
1374
    }
1375
1376
    /**
1377
     * Validates a bearer token authenticated request. Note: this method does not return a result but will throw
1378
     * an exception when the authentication fails.
1379
     * @throws InvalidConfigException
1380
     * @throws Oauth2ServerException
1381
     * @since 1.0.0
1382
     */
1383 3
    public function validateAuthenticatedRequest()
1384
    {
1385 3
        $psr7Request = Psr7Helper::yiiToPsr7Request(Yii::$app->request);
0 ignored issues
show
Bug introduced by
It seems like Yii::app->request can also be of type yii\console\Request; however, parameter $request of rhertogh\Yii2Oauth2Serve...per::yiiToPsr7Request() does only seem to accept yii\web\Request, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1385
        $psr7Request = Psr7Helper::yiiToPsr7Request(/** @scrutinizer ignore-type */ Yii::$app->request);
Loading history...
1386
1387 3
        $psr7Request = $this->getResourceServer()->validateAuthenticatedRequest($psr7Request);
1388
1389 3
        $this->_oauthClaims = $psr7Request->getAttributes();
1390 3
        $this->_oauthClaimsAuthorizationHeader = Yii::$app->request->getHeaders()->get('Authorization');
0 ignored issues
show
Documentation Bug introduced by
It seems like Yii::app->request->getHe...)->get('Authorization') can also be of type array. However, the property $_oauthClaimsAuthorizationHeader is declared as type null|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1391
    }
1392
1393
    /**
1394
     * Find a user identity bases on an access token.
1395
     * Note: validateAuthenticatedRequest() must be called before this method is called.
1396
     * @param string $token
1397
     * @param string $type
1398
     * @return Oauth2UserInterface|null
1399
     * @throws InvalidConfigException
1400
     * @throws Oauth2ServerException
1401
     * @see validateAuthenticatedRequest()
1402
     * @since 1.0.0
1403
     */
1404 4
    public function findIdentityByAccessToken($token, $type)
1405
    {
1406 4
        if (!is_a($type, Oauth2HttpBearerAuthInterface::class, true)) {
1407 1
            throw new InvalidCallException($type . ' must implement ' . Oauth2HttpBearerAuthInterface::class);
1408
        }
1409
1410
        if (
1411 3
            !preg_match('/^Bearer\s+(.*?)$/', $this->_oauthClaimsAuthorizationHeader, $matches)
0 ignored issues
show
Bug introduced by
It seems like $this->_oauthClaimsAuthorizationHeader can also be of type null; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1411
            !preg_match('/^Bearer\s+(.*?)$/', /** @scrutinizer ignore-type */ $this->_oauthClaimsAuthorizationHeader, $matches)
Loading history...
1412 3
            || !Yii::$app->security->compareString($matches[1], $token)
1413
        ) {
1414 1
            throw new InvalidCallException(
1415 1
                'validateAuthenticatedRequest() must be called before findIdentityByAccessToken().'
1416 1
            );
1417
        }
1418
1419 2
        $userId = $this->getRequestOauthUserId();
1420 2
        if (empty($userId)) {
1421 1
            return null;
1422
        }
1423
1424 1
        return $this->identityClass::findIdentity($userId);
1425
    }
1426
1427
    /**
1428
     * Generate a "Personal Access Token" (PAT) which can be used as an alternative to using passwords
1429
     * for authentication (e.g. when using an API or command line).
1430
     *
1431
     * Note: Personal Access Tokens are intended to access resources on behalf users themselves.
1432
     *       To grant access to resources on behalf of an organization, or for long-lived integrations,
1433
     *       you most likely want to define an Oauth2 Client with the "Client Credentials" grant
1434
     *       (https://oauth.net/2/grant-types/client-credentials).
1435
     *
1436
     * @param string $clientIdentifier The Oauth2 client identifier for which the PAT should be generated.
1437
     * @param int|string $userIdentifier The identifier (primary key) of the user for which the PAT should be generated.
1438
     * @param Oauth2ScopeInterface[]|string[]|string|null $scope The Access Token scope.
1439
     * @param string|true|null $clientSecret If the client is a "confidential" client the secret is required.
1440
     *        If the boolean value `true` is passed, the client secret is automatically injected.
1441
     * @return Oauth2AccessTokenData
1442
     */
1443 3
    public function generatePersonalAccessToken($clientIdentifier, $userIdentifier, $scope = null, $clientSecret = null)
1444
    {
1445 3
        if (is_array($scope)) {
1446 2
            $scopeIdentifiers = [];
1447 2
            foreach ($scope as $scopeItem) {
1448 2
                if (is_string($scopeItem)) {
1449 1
                    $scopeIdentifiers[] = $scopeItem;
1450 1
                } elseif ($scopeItem instanceof Oauth2ScopeInterface) {
1451 1
                    $scopeIdentifiers[] = $scopeItem->getIdentifier();
1452
                } else {
1453
                    throw new InvalidArgumentException('If $scope is an array its elements must be either'
1454
                        . ' a string or an instance of ' . Oauth2ScopeInterface::class);
1455
                }
1456
            }
1457 2
            $scope = implode(' ', $scopeIdentifiers);
1458
        }
1459
1460 3
        if ($clientSecret === true) {
1461
            /** @var Oauth2ClientInterface $client */
1462 3
            $client = $this->getClientRepository()->findModelByIdentifier($clientIdentifier);
1463 3
            if ($client && $client->isConfidential()) {
1464 3
                $clientSecret = $client->getDecryptedSecret($this->getCryptographer());
1465
            } else {
1466
                $clientSecret = null;
1467
            }
1468
        }
1469
1470 3
        $request = (new Psr7ServerRequest('POST', ''))->withParsedBody([
1471 3
            'grant_type' => static::GRANT_TYPE_IDENTIFIER_PERSONAL_ACCESS_TOKEN,
1472 3
            'client_id' => $clientIdentifier,
1473 3
            'client_secret' => $clientSecret,
1474 3
            'user_id' => $userIdentifier,
1475 3
            'scope' => $scope,
1476 3
        ]);
1477
1478 3
        return new Oauth2AccessTokenData(Json::decode(
0 ignored issues
show
Bug introduced by
It seems like yii\helpers\Json::decode...etBody()->__toString()) can also be of type null; however, parameter $data of rhertogh\Yii2Oauth2Serve...okenData::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1478
        return new Oauth2AccessTokenData(/** @scrutinizer ignore-type */ Json::decode(
Loading history...
1479 3
            $this->getAuthorizationServer()
1480 3
                ->respondToAccessTokenRequest(
1481 3
                    $request,
1482 3
                    new Psr7Response()
1483 3
                )
1484 3
                ->getBody()
1485 3
                ->__toString()
1486 3
        ));
1487
    }
1488
1489
    /**
1490
     * @inheritDoc
1491
     */
1492 5
    protected function getRequestOauthClaim($attribute, $default = null)
1493
    {
1494 5
        if (empty($this->_oauthClaimsAuthorizationHeader)) {
1495
            // User authorization was not processed by Oauth2Module.
1496 1
            return $default;
1497
        }
1498 4
        if (Yii::$app->request->getHeaders()->get('Authorization') !== $this->_oauthClaimsAuthorizationHeader) {
1499 1
            throw new InvalidCallException(
1500 1
                'App Request Authorization header does not match the processed Oauth header.'
1501 1
            );
1502
        }
1503 3
        return $this->_oauthClaims[$attribute] ?? $default;
1504
    }
1505
1506
    /**
1507
     * Helper function to ensure the required properties are configured for the module.
1508
     * @param string[] $properties
1509
     * @throws InvalidConfigException
1510
     * @since 1.0.0
1511
     */
1512 32
    protected function ensureProperties($properties)
1513
    {
1514 32
        foreach ($properties as $property) {
1515 32
            if (empty($this->$property)) {
1516 6
                throw new InvalidConfigException(__CLASS__ . '::$' . $property . ' must be set.');
1517
            }
1518
        }
1519
    }
1520
1521
    /**
1522
     * @throws InvalidConfigException
1523
     */
1524
    public function logoutUser($revokeTokens = true)
1525
    {
1526
        $identity = $this->getUserIdentity();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $identity is correct as $this->getUserIdentity() targeting rhertogh\Yii2Oauth2Serve...dule::getUserIdentity() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1527
1528
        if ($identity) {
0 ignored issues
show
introduced by
$identity is of type null, thus it always evaluated to false.
Loading history...
1529
            if ($revokeTokens) {
1530
                $this->revokeTokensByUserId($identity->getId());
1531
            }
1532
1533
            Yii::$app->user->logout();
1534
        }
1535
    }
1536
1537
    public function revokeTokensByUserId($userId)
1538
    {
1539
        $accessTokens = $this->getAccessTokenRepository()->revokeAccessTokensByUserId($userId);
1540
        $accessTokenIds = array_map(fn($accessToken) => $accessToken->getPrimaryKey(), $accessTokens);
1541
        $this->getRefreshTokenRepository()->revokeRefreshTokensByAccessTokenIds($accessTokenIds);
1542
    }
1543
1544
    /**
1545
     * @return int
1546
     */
1547 2
    public function getElaboratedHttpClientErrorsLogLevel()
1548
    {
1549 2
        if ($this->httpClientErrorsLogLevel === null) {
1550 1
            return YII_DEBUG ? Logger::LEVEL_ERROR : Logger::LEVEL_INFO;
1551
        }
1552
1553 1
        return $this->httpClientErrorsLogLevel;
1554
    }
1555
}
1556