Passed
Push — master ( 06cf71...6c597b )
by Rutger
03:06
created

Oauth2Module::getSupportedPromptValues()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

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

818
            $privateKey = 'file://' . /** @scrutinizer ignore-type */ Yii::getAlias($privateKey);
Loading history...
819
        }
820 22
        return Yii::createObject(CryptKey::class, [$privateKey, $this->privateKeyPassphrase]);
821
    }
822
823
    /**
824
     * @return CryptKey The public key of the server.
825
     * @throws InvalidConfigException
826
     * @since 1.0.0
827
     */
828 9
    public function getPublicKey()
829
    {
830 9
        $publicKey = $this->publicKey;
831 9
        if (StringHelper::startsWith($publicKey, '@')) {
832 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

832
            $publicKey = 'file://' . /** @scrutinizer ignore-type */ Yii::getAlias($publicKey);
Loading history...
833
        }
834 9
        return Yii::createObject(CryptKey::class, [$publicKey]);
835
    }
836
837
    /**
838
     * @return Oauth2AuthorizationServerInterface The authorization server.
839
     * @throws InvalidConfigException
840
     * @since 1.0.0
841
     */
842 27
    public function getAuthorizationServer()
843
    {
844 27
        if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) {
845 1
            throw new InvalidCallException('Oauth2 server role does not include authorization server.');
846
        }
847
848 26
        if (!$this->_authorizationServer) {
849 26
            $this->ensureProperties(static::REQUIRED_SETTINGS_AUTHORIZATION_SERVER);
850
851 21
            if (!$this->getCryptographer()->hasKey($this->defaultStorageEncryptionKey)) {
852 1
                throw new InvalidConfigException(
853 1
                    'Key "' . $this->defaultStorageEncryptionKey . '" is not set in $storageEncryptionKeys'
854 1
                );
855
            }
856
857
            /** @var Oauth2EncryptionKeyFactoryInterface $keyFactory */
858 19
            $keyFactory = Yii::createObject(Oauth2EncryptionKeyFactoryInterface::class);
859
            try {
860 19
                $codesEncryptionKey = $keyFactory->createFromAsciiSafeString($this->codesEncryptionKey);
861 1
            } catch (BadFormatException $e) {
862 1
                throw new InvalidConfigException(
863 1
                    '$codesEncryptionKey is malformed: ' . $e->getMessage(),
864 1
                    0,
865 1
                    $e
866 1
                );
867
            } catch (EnvironmentIsBrokenException $e) {
868
                throw new InvalidConfigException(
869
                    'Could not instantiate $codesEncryptionKey: ' . $e->getMessage(),
870
                    0,
871
                    $e
872
                );
873
            }
874
875 18
            if ($this->enableOpenIdConnect) {
876 18
                $responseTypeClass = Oauth2OidcBearerTokenResponseInterface::class;
877
            } else {
878
                $responseTypeClass = Oauth2BearerTokenResponseInterface::class;
879
            }
880 18
            $responseType = Yii::createObject($responseTypeClass, [
881 18
                $this,
882 18
            ]);
883
884 18
            $this->_authorizationServer = Yii::createObject(Oauth2AuthorizationServerInterface::class, [
885 18
                $this->getClientRepository(),
886 18
                $this->getAccessTokenRepository(),
887 18
                $this->getScopeRepository(),
888 18
                $this->getPrivateKey(),
889 18
                $codesEncryptionKey,
890 18
                $responseType
891 18
            ]);
892
893 18
            if (!empty($this->grantTypes)) {
894 18
                $grantTypes = $this->grantTypes;
895
896 18
                if (is_callable($grantTypes)) {
897 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

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

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

1389
        $psr7Request = Psr7Helper::yiiToPsr7Request(/** @scrutinizer ignore-type */ Yii::$app->request);
Loading history...
1390
1391 3
        $psr7Request = $this->getResourceServer()->validateAuthenticatedRequest($psr7Request);
1392
1393 3
        $this->_oauthClaims = $psr7Request->getAttributes();
1394 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...
1395
    }
1396
1397
    /**
1398
     * Find a user identity bases on an access token.
1399
     * Note: validateAuthenticatedRequest() must be called before this method is called.
1400
     * @param string $token
1401
     * @param string $type
1402
     * @return Oauth2UserInterface|null
1403
     * @throws InvalidConfigException
1404
     * @throws Oauth2ServerException
1405
     * @see validateAuthenticatedRequest()
1406
     * @since 1.0.0
1407
     */
1408 4
    public function findIdentityByAccessToken($token, $type)
1409
    {
1410 4
        if (!is_a($type, Oauth2HttpBearerAuthInterface::class, true)) {
1411 1
            throw new InvalidCallException($type . ' must implement ' . Oauth2HttpBearerAuthInterface::class);
1412
        }
1413
1414
        if (
1415 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

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

1482
        return new Oauth2AccessTokenData(/** @scrutinizer ignore-type */ Json::decode(
Loading history...
1483 3
            $this->getAuthorizationServer()
1484 3
                ->respondToAccessTokenRequest(
1485 3
                    $request,
1486 3
                    new Psr7Response()
1487 3
                )
1488 3
                ->getBody()
1489 3
                ->__toString()
1490 3
        ));
1491
    }
1492
1493
    /**
1494
     * @inheritDoc
1495
     */
1496 5
    protected function getRequestOauthClaim($attribute, $default = null)
1497
    {
1498 5
        if (empty($this->_oauthClaimsAuthorizationHeader)) {
1499
            // User authorization was not processed by Oauth2Module.
1500 1
            return $default;
1501
        }
1502 4
        if (Yii::$app->request->getHeaders()->get('Authorization') !== $this->_oauthClaimsAuthorizationHeader) {
1503 1
            throw new InvalidCallException(
1504 1
                'App Request Authorization header does not match the processed Oauth header.'
1505 1
            );
1506
        }
1507 3
        return $this->_oauthClaims[$attribute] ?? $default;
1508
    }
1509
1510
    /**
1511
     * Helper function to ensure the required properties are configured for the module.
1512
     * @param string[] $properties
1513
     * @throws InvalidConfigException
1514
     * @since 1.0.0
1515
     */
1516 32
    protected function ensureProperties($properties)
1517
    {
1518 32
        foreach ($properties as $property) {
1519 32
            if (empty($this->$property)) {
1520 6
                throw new InvalidConfigException(__CLASS__ . '::$' . $property . ' must be set.');
1521
            }
1522
        }
1523
    }
1524
1525
    /**
1526
     * @throws InvalidConfigException
1527
     */
1528
    public function logoutUser($revokeTokens = true)
1529
    {
1530
        $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...
1531
1532
        if ($identity) {
0 ignored issues
show
introduced by
$identity is of type null, thus it always evaluated to false.
Loading history...
1533
            if ($revokeTokens) {
1534
                $this->revokeTokensByUserId($identity->getId());
1535
            }
1536
1537
            Yii::$app->user->logout();
1538
        }
1539
    }
1540
1541
    public function revokeTokensByUserId($userId)
1542
    {
1543
        $accessTokens = $this->getAccessTokenRepository()->revokeAccessTokensByUserId($userId);
1544
        $accessTokenIds = array_map(fn($accessToken) => $accessToken->getPrimaryKey(), $accessTokens);
1545
        $this->getRefreshTokenRepository()->revokeRefreshTokensByAccessTokenIds($accessTokenIds);
1546
    }
1547
1548 4
    public function getSupportedPromptValues()
1549
    {
1550 4
        $supportedPromptValues = [
1551 4
            Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_NONE,
1552 4
            Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_LOGIN,
1553 4
            Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_CONSENT,
1554 4
            Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_SELECT_ACCOUNT,
1555 4
        ];
1556
1557 4
        if (!empty($this->userAccountCreationUrl)) {
1558 4
            $supportedPromptValues[] = Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_CREATE;
1559
        }
1560
1561 4
        return $supportedPromptValues;
1562
    }
1563
1564
    /**
1565
     * @return int
1566
     */
1567 2
    public function getElaboratedHttpClientErrorsLogLevel()
1568
    {
1569 2
        if ($this->httpClientErrorsLogLevel === null) {
1570 1
            return YII_DEBUG ? Logger::LEVEL_ERROR : Logger::LEVEL_INFO;
1571
        }
1572
1573 1
        return $this->httpClientErrorsLogLevel;
1574
    }
1575
}
1576