Passed
Push — master ( 391225...942304 )
by Rutger
14:53 queued 11:50
created

generateClientAuthReqCompledRedirectResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
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
use Defuse\Crypto\Exception\BadFormatException;
12
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
13
use League\OAuth2\Server\CryptKey;
14
use League\OAuth2\Server\Exception\OAuthServerException;
15
use League\OAuth2\Server\Grant\GrantTypeInterface;
16
use rhertogh\Yii2Oauth2Server\base\Oauth2BaseModule;
17
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2ClientController;
18
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2DebugController;
19
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2EncryptionController;
20
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2MigrationsController;
21
use rhertogh\Yii2Oauth2Server\helpers\DiHelper;
22
use rhertogh\Yii2Oauth2Server\helpers\Psr7Helper;
23
use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\Oauth2ClientAuthorizationRequestInterface;
24
use rhertogh\Yii2Oauth2Server\interfaces\components\encryption\Oauth2EncryptorInterface;
25
use rhertogh\Yii2Oauth2Server\interfaces\components\factories\encryption\Oauth2EncryptionKeyFactoryInterface;
26
use rhertogh\Yii2Oauth2Server\interfaces\components\factories\grants\base\Oauth2GrantTypeFactoryInterface;
27
use rhertogh\Yii2Oauth2Server\interfaces\components\openidconnect\scope\Oauth2OidcScopeCollectionInterface;
28
use rhertogh\Yii2Oauth2Server\interfaces\components\openidconnect\server\Oauth2OidcBearerTokenResponseInterface;
29
use rhertogh\Yii2Oauth2Server\interfaces\components\server\Oauth2AuthorizationServerInterface;
30
use rhertogh\Yii2Oauth2Server\interfaces\components\server\Oauth2ResourceServerInterface;
31
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2CertificatesControllerInterface;
32
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2ConsentControllerInterface;
33
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2OidcControllerInterface;
34
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2ServerControllerInterface;
35
use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2WellKnownControllerInterface;
36
use rhertogh\Yii2Oauth2Server\interfaces\filters\auth\Oauth2HttpBearerAuthInterface;
37
use rhertogh\Yii2Oauth2Server\interfaces\models\base\Oauth2EncryptedStorageInterface;
38
use rhertogh\Yii2Oauth2Server\interfaces\models\external\user\Oauth2OidcUserInterface;
39
use rhertogh\Yii2Oauth2Server\interfaces\models\external\user\Oauth2UserInterface;
40
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientInterface;
41
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientScopeInterface;
42
use Yii;
43
use yii\base\BootstrapInterface;
44
use yii\base\InvalidArgumentException;
45
use yii\base\InvalidCallException;
46
use yii\base\InvalidConfigException;
47
use yii\console\Application as ConsoleApplication;
48
use yii\helpers\ArrayHelper;
49
use yii\helpers\StringHelper;
50
use yii\i18n\PhpMessageSource;
51
use yii\web\Application as WebApplication;
52
use yii\web\GroupUrlRule;
53
use yii\web\IdentityInterface;
54
use yii\web\Response;
55
use yii\web\UrlRule;
56
57
/**
58
 * This is the main module class for the Yii2 Oauth2 Server module.
59
 * To use it, include it as a module in the application configuration like the following:
60
 *
61
 * ~~~
62
 * return [
63
 *     'bootstrap' => ['oauth2'],
64
 *     'modules' => [
65
 *         'oauth2' => [
66
 *             'class' => 'rhertogh\Yii2Oauth2Server\Oauth2Module',
67
 *             // ... Please check docs/guide/start-installation.md further details
68
 *          ],
69
 *     ],
70
 * ]
71
 * ~~~
72
 *
73
 * @since 1.0.0
74
 */
75
class Oauth2Module extends Oauth2BaseModule implements BootstrapInterface
76
{
77
    /**
78
     * Application type "web": http response.
79
     * @since 1.0.0
80
     */
81
    public const APPLICATION_TYPE_WEB = 'web';
82
    /**
83
     * Application type "console": cli response.
84
     * @since 1.0.0
85
     */
86
    public const APPLICATION_TYPE_CONSOLE = 'console';
87
    /**
88
     * Supported Application types.
89
     * @since 1.0.0
90
     */
91
    public const APPLICATION_TYPES = [
92
        self::APPLICATION_TYPE_WEB,
93
        self::APPLICATION_TYPE_CONSOLE,
94
    ];
95
96
    /**
97
     * "Authorization Server" Role, please see guide for details.
98
     * @since 1.0.0
99
     */
100
    public const SERVER_ROLE_AUTHORIZATION_SERVER = 1;
101
    /**
102
     * "Resource Server" Role, please see guide for details.
103
     * @since 1.0.0
104
     */
105
    public const SERVER_ROLE_RESOURCE_SERVER = 2;
106
107
    /**
108
     * Required settings when the server role includes Authorization Server
109
     * @since 1.0.0
110
     */
111
    protected const REQUIRED_SETTINGS_AUTHORIZATION_SERVER = [
112
        'codesEncryptionKey',
113
        'storageEncryptionKeys',
114
        'defaultStorageEncryptionKey',
115
        'privateKey',
116
        'publicKey',
117
    ];
118
119
    /**
120
     * Encrypted Models
121
     *
122
     * @since 1.0.0
123
     */
124
    protected const ENCRYPTED_MODELS = [
125
        Oauth2ClientInterface::class,
126
    ];
127
128
    /**
129
     * Required settings when the server role includes Resource Server
130
     * @since 1.0.0
131
     */
132
    protected const REQUIRED_SETTINGS_RESOURCE_SERVER = [
133
        'publicKey',
134
    ];
135
136
    /**
137
     * Prefix used in session storage of Client Authorization Requests
138
     * @since 1.0.0
139
     */
140
    protected const CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX = 'OATH2_CLIENT_AUTHORIZATION_REQUEST_';
141
142
    /**
143
     * Controller mapping for the module. Will be parsed on `init()`.
144
     * @since 1.0.0
145
     */
146
    protected const CONTROLLER_MAP = [
147
        self::APPLICATION_TYPE_WEB => [
148
            Oauth2ServerControllerInterface::CONTROLLER_NAME => [
149
                'controller' => Oauth2ServerControllerInterface::class,
150
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
151
            ],
152
            Oauth2ConsentControllerInterface::CONTROLLER_NAME => [
153
                'controller' => Oauth2ConsentControllerInterface::class,
154
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
155
            ],
156
            Oauth2WellKnownControllerInterface::CONTROLLER_NAME => [
157
                'controller' => Oauth2WellKnownControllerInterface::class,
158
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
159
            ],
160
            Oauth2CertificatesControllerInterface::CONTROLLER_NAME => [
161
                'controller' => Oauth2CertificatesControllerInterface::class,
162
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
163
            ],
164
            Oauth2OidcControllerInterface::CONTROLLER_NAME => [
165
                'controller' => Oauth2OidcControllerInterface::class,
166
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
167
            ],
168
        ],
169
        self::APPLICATION_TYPE_CONSOLE => [
170
            'migrations' => [
171
                'controller' => Oauth2MigrationsController::class,
172
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER,
173
            ],
174
            'client' => [
175
                'controller' => Oauth2ClientController::class,
176
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
177
            ],
178
            'encryption' => [
179
                'controller' => Oauth2EncryptionController::class,
180
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER,
181
            ],
182
            'debug' => [
183
                'controller' => Oauth2DebugController::class,
184
                'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER,
185
            ],
186
        ]
187
    ];
188
189
    /**
190
     * @inheritdoc
191
     */
192
    public $controllerNamespace = __NAMESPACE__ . '\-'; // Set explicitly via $controllerMap in `init()`.
193
194
    /**
195
     * @var string|null The application type. If `null` the type will be automatically detected.
196
     * @see APPLICATION_TYPES
197
     */
198
    public $appType = null;
199
200
    /**
201
     * @var int The Oauth 2.0 Server Roles the module will perform.
202
     * @since 1.0.0
203
     */
204
    public $serverRole = self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER;
205
206
    /**
207
     * @var string|null The private key for the server. Can be a string containing the key itself or point to a file.
208
     * When pointing to a file it's recommended to use an absolute path prefixed with 'file://' or start with
209
     * '@' to use a Yii path alias.
210
     * @see $privateKeyPassphrase For setting a passphrase for the private key.
211
     * @since 1.0.0
212
     */
213
    public $privateKey = null;
214
215
    /**
216
     * @var string|null The passphrase for the private key.
217
     * @since 1.0.0
218
     */
219
    public $privateKeyPassphrase = null;
220
    /**
221
     * @var string|null The public key for the server. Can be a string containing the key itself or point to a file.
222
     * When pointing to a file it's recommended to use an absolute path prefixed with 'file://' or start with
223
     * '@' to use a Yii path alias.
224
     * @since 1.0.0
225
     */
226
    public $publicKey = null;
227
228
    /**
229
     * @var string|null The encryption key for authorization and refresh codes.
230
     * @since 1.0.0
231
     */
232
    public $codesEncryptionKey = null;
233
234
    /**
235
     * @var string[]|string|null The encryption keys for storage like client secrets.
236
     * Where the array key is the name of the key, and the value the key itself. E.g.
237
     * `['2022-01-01' => 'def00000cb36fd6ed6641e0ad70805b28d....']`
238
     * If a string (instead of an array of strings) is specified it will be JSON decoded
239
     * it should contain an object where each property name is the name of the key, its value the key itself. E.g.
240
     * `{"2022-01-01": "def00000cb36fd6ed6641e0ad70805b28d...."}`
241
     *
242
     * @since 1.0.0
243
     */
244
    public $storageEncryptionKeys = null;
245
246
    /**
247
     * @var string|null The index of the default key in storageEncryptionKeys. E.g. 'myKey'.
248
     * @since 1.0.0
249
     */
250
    public $defaultStorageEncryptionKey = null;
251
252
    /**
253
     * @var Oauth2UserInterface|string|null The Identity Class of your application,
254
     * most likely the same as the 'identityClass' of your application's User Component.
255
     * @since 1.0.0
256
     */
257
    public $identityClass = null;
258
259
    /**
260
     * @var null|string Prefix used for url rules. When `null` the module's uniqueId will be used.
261
     * @since 1.0.0
262
     */
263
    public $urlRulesPrefix = null;
264
265
    /**
266
     * @var string URL path for the access token endpoint (will be prefixed with $urlRulesPrefix).
267
     * @since 1.0.0
268
     */
269
    public $authorizePath = 'authorize';
270
271
    /**
272
     * @var string URL path for the access token endpoint (will be prefixed with $urlRulesPrefix).
273
     * @since 1.0.0
274
     */
275
    public $accessTokenPath = 'access-token';
276
277
    /**
278
     * @var string URL path for the certificates jwks endpoint (will be prefixed with $urlRulesPrefix).
279
     * @since 1.0.0
280
     */
281
    public $jwksPath = 'certs';
282
283
    /**
284
     * The URL to the page where the user can perform the client/scope authorization
285
     * (if `null` the build in page will be used).
286
     * @return string
287
     * @since 1.0.0
288
     */
289
    public $clientAuthorizationUrl = null;
290
291
    /**
292
     * @var string The URL path to the build in page where the user can authorize the client for the requested scopes
293
     * (will be prefixed with $urlRulesPrefix).
294
     * Note: This setting will only be used if $clientAuthorizationUrl is `null`.
295
     * @since 1.0.0
296
     */
297
    public $clientAuthorizationPath = 'authorize-client';
298
299
    /**
300
     * @var string The view to use in the "client authorization action" for the page where the user can
301
     * authorize the client for the requested scopes.
302
     * Note: This setting will only be used if $clientAuthorizationUrl is `null`.
303
     * @since 1.0.0
304
     */
305
    public $clientAuthorizationView = 'authorize-client';
306
307
    /**
308
     * @var string The URL path to the OpenID Connect Userinfo Action (will be prefixed with $urlRulesPrefix).
309
     * Note: This setting will only be used if $enableOpenIdConnect and $openIdConnectUserinfoEndpoint are `true`.
310
     * @since 1.0.0
311
     */
312
    public $openIdConnectUserinfoPath = 'oidc/userinfo';
313
314
    /**
315
     * @var Oauth2GrantTypeFactoryInterface[]|GrantTypeInterface[]|string[]|Oauth2GrantTypeFactoryInterface|GrantTypeInterface|string|callable
316
     * The Oauth 2.0 Grant Types that the module will serve.
317
     * @since 1.0.0
318
     */
319
    public $grantTypes = [];
320
321
    /**
322
     * @var string|null Default Time To Live for the access token, used when the Grant Type does not specify it.
323
     * When `null` default value of 1 hour is used.
324
     * The format should be a DateInterval duration (https://www.php.net/manual/en/dateinterval.construct.php).
325
     * @since 1.0.0
326
     */
327
    public $defaultAccessTokenTTL = null;
328
329
    /**
330
     * @var bool Should the resource server check for revocation of the access token.
331
     * @since 1.0.0
332
     */
333
    public $resourceServerAccessTokenRevocationValidation = true;
334
335
    /**
336
     * @var bool Enable support for OpenIdvConnect.
337
     * @since 1.0.0
338
     */
339
    public $enableOpenIdConnect = false;
340
341
    /**
342
     * @var bool Enable the .well-known/openid-configuration discovery endpoint.
343
     * @since 1.0.0
344
     */
345
    public $enableOpenIdConnectDiscovery = true;
346
347
    /**
348
     * @var bool include `grant_types_supported` in the OpenIdConnect Discovery.
349
     * Note: Since grant types can be specified per client not all clients might support all enabled grant types.
350
     * @since 1.0.0
351
     */
352
    public $openIdConnectDiscoveryIncludeSupportedGrantTypes = true;
353
354
    /**
355
     * @var string URL to include in the OpenID Connect Discovery Service of a page containing
356
     * human-readable information that developers might want or need to know when using the OpenID Provider.
357
     * @see 'service_documentation' in https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3
358
     * @since 1.0.0
359
     */
360
    public $openIdConnectDiscoveryServiceDocumentationUrl = null;
361
362
    /**
363
     * @var string|bool A string to a custom userinfo endpoint or `true` to enable the build in endpoint.
364
     * @since 1.0.0
365
     */
366
    public $openIdConnectUserinfoEndpoint = true;
367
368
    /**
369
     * Warning! Enabling this setting might introduce privacy concerns since the client could poll for the
370
     * online status of a user.
371
     *
372
     * @var bool If this setting is disabled in case of OpenID Connect Context the Access Token won't include a
373
     * Refresh Token when the 'offline_access' scope is not included in the authorization request.
374
     * In some cases it might be needed to always include a Refresh Token, in that case enable this setting and
375
     * implement the `Oauth2OidcUserSessionStatusInterface` on the User Identity model.
376
     * @since 1.0.0
377
     */
378
    public $openIdConnectIssueRefreshTokenWithoutOfflineAccessScope = false;
379
380
    /**
381
     * @var int The default option for "User Account Selection' when not specified for a client.
382
     * @since 1.0.0
383
     */
384
    public $defaultUserAccountSelection = self::USER_ACCOUNT_SELECTION_DISABLED;
385
386
    /**
387
     * @var bool|null Display exception messages that might leak server details. This could be useful for debugging.
388
     * In case of `null` (default) the YII_DEBUG constant will be used.
389
     * Warning: Should NOT be enabled in production!
390
     * @since 1.0.0
391
     */
392
    public $displayConfidentialExceptionMessages = null;
393
394
    /**
395
     * @var string|null The namespace with which migrations will be created (and by which they will be located).
396
     * Note: The specified namespace must be defined as a Yii alias (e.g. '@app').
397
     * @since 1.0.0
398
     */
399
    public $migrationsNamespace = null;
400
    /**
401
     * @var string|null Optional prefix used in the name of generated migrations
402
     * @since 1.0.0
403
     */
404
    public $migrationsPrefix = null;
405
    /**
406
     * @var string|array|int|null Sets the file ownership of generated migrations
407
     * @see \yii\helpers\BaseFileHelper::changeOwnership()
408
     * @since 1.0.0
409
     */
410
    public $migrationsFileOwnership = null;
411
    /**
412
     * @var int|null Sets the file mode of generated migrations
413
     * @see \yii\helpers\BaseFileHelper::changeOwnership()
414
     * @since 1.0.0
415
     */
416
    public $migrationsFileMode = null;
417
418
    /**
419
     * @var Oauth2AuthorizationServerInterface|null Cache for the authorization server
420
     * @since 1.0.0
421
     */
422
    protected $_authorizationServer = null;
423
424
    /**
425
     * @var Oauth2ResourceServerInterface|null Cache for the resource server
426
     * @since 1.0.0
427
     */
428
    protected $_resourceServer = null;
429
430
    /**
431
     * @var Oauth2EncryptorInterface|null Cache for the Oauth2Encryptor
432
     * @since 1.0.0
433
     */
434
    protected $_encryptor = null;
435
436
    /**
437
     * @var string|null The authorization header used when the authorization request was validated.
438
     * @since 1.0.0
439
     */
440
    protected $_oauthClaimsAuthorizationHeader = null;
441
442
    /**
443
     * @inheritDoc
444
     * @throws InvalidConfigException
445
     */
446 114
    public function init()
447
    {
448 114
        parent::init();
449
450 114
        $app = Yii::$app;
451
452 114
        if ($app instanceof WebApplication || $this->appType == static::APPLICATION_TYPE_WEB) {
453 18
            $controllerMap = static::CONTROLLER_MAP[static::APPLICATION_TYPE_WEB];
454 114
        } elseif ($app instanceof ConsoleApplication || $this->appType == static::APPLICATION_TYPE_CONSOLE) {
455 114
            $controllerMap = static::CONTROLLER_MAP[static::APPLICATION_TYPE_CONSOLE];
456
        } else {
457
            throw new InvalidConfigException(
458
                'Unable to detect application type, configure it manually by setting `$appType`.'
459
            );
460
        }
461 114
        $controllerMap = array_filter(
462
            $controllerMap,
463 114
            fn($controllerSettings) => $controllerSettings['serverRole'] & $this->serverRole
464
        );
465 114
        $this->controllerMap = ArrayHelper::getColumn($controllerMap, 'controller');
466
467 114
        if (empty($this->identityClass)) {
468 1
            throw new InvalidConfigException('$identityClass must be set.');
469 114
        } elseif (!is_a($this->identityClass, Oauth2UserInterface::class, true)) {
470 1
            throw new InvalidConfigException(
471 1
                $this->identityClass . ' must implement ' . Oauth2UserInterface::class
0 ignored issues
show
Bug introduced by
Are you sure $this->identityClass of type rhertogh\Yii2Oauth2Serve...th2UserInterface|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

471
                /** @scrutinizer ignore-type */ $this->identityClass . ' must implement ' . Oauth2UserInterface::class
Loading history...
472
            );
473
        }
474
475 114
        foreach (static::DEFAULT_INTERFACE_IMPLEMENTATIONS as $interface => $implementation) {
476 114
            if (!Yii::$container->has($interface)) {
477 114
                Yii::$container->set($interface, $implementation);
478
            }
479
        }
480
481 114
        if (empty($this->urlRulesPrefix)) {
482 114
            $this->urlRulesPrefix = $this->uniqueId;
483
        }
484
485 114
        $this->registerTranslations();
486
    }
487
488
    /**
489
     * @inheritdoc
490
     * @throws InvalidConfigException
491
     */
492 114
    public function bootstrap($app)
493
    {
494
        if (
495 114
            $app instanceof WebApplication
496 114
            && $this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER
497
        ) {
498
            $rules = [
499 18
                $this->accessTokenPath => Oauth2ServerControllerInterface::CONTROLLER_NAME
500
                    . '/' . Oauth2ServerControllerInterface::ACTION_NAME_ACCESS_TOKEN,
501 18
                $this->authorizePath => Oauth2ServerControllerInterface::CONTROLLER_NAME
502
                    . '/' . Oauth2ServerControllerInterface::ACTION_NAME_AUTHORIZE,
503 18
                $this->jwksPath => Oauth2CertificatesControllerInterface::CONTROLLER_NAME
504
                    . '/' . Oauth2CertificatesControllerInterface::ACTION_NAME_JWKS,
505
            ];
506
507 18
            if (empty($this->clientAuthorizationUrl)) {
508 17
                $rules[$this->clientAuthorizationPath] = Oauth2ConsentControllerInterface::CONTROLLER_NAME
509
                    . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_CLIENT;
510
            }
511
512 18
            if ($this->enableOpenIdConnect && $this->openIdConnectUserinfoEndpoint === true) {
513 18
                $rules[$this->openIdConnectUserinfoPath] =
514 18
                    Oauth2OidcControllerInterface::CONTROLLER_NAME
515
                    . '/' . Oauth2OidcControllerInterface::ACTION_NAME_USERINFO;
516
            }
517
518 18
            $urlManager = $app->getUrlManager();
519 18
            $urlManager->addRules([
520 18
                Yii::createObject([
521
                    'class' => GroupUrlRule::class,
522 18
                    'prefix' => $this->urlRulesPrefix,
523 18
                    'routePrefix' => $this->id,
524
                    'rules' => $rules,
525
                ]),
526
            ]);
527
528 18
            if ($this->enableOpenIdConnect && $this->enableOpenIdConnectDiscovery) {
529 18
                $urlManager->addRules([
530 18
                    Yii::createObject([
531
                        'class' => UrlRule::class,
532
                        'pattern' => '.well-known/openid-configuration',
533 18
                        'route' => $this->id
534
                            . '/' . Oauth2WellKnownControllerInterface::CONTROLLER_NAME
535
                            . '/' . Oauth2WellKnownControllerInterface::ACTION_NAME_OPENID_CONFIGURATION,
536
                    ]),
537
                ]);
538
            }
539
        }
540
    }
541
542
    /**
543
     * Registers the translations for the module
544
     * @param bool $force Force the setting of the translations (even if they are already defined).
545
     * @since 1.0.0
546
     */
547 114
    public function registerTranslations($force = false)
548
    {
549 114
        if ($force || !array_key_exists('oauth2', Yii::$app->i18n->translations)) {
550 114
            Yii::$app->i18n->translations['oauth2'] = [
551
                'class' => PhpMessageSource::class,
552
                'sourceLanguage' => 'en-US',
553 114
                'basePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
554
                'fileMap' => [
555
                    'oauth2' => 'oauth2.php',
556
                ],
557
            ];
558
        }
559
    }
560
561
    /**
562
     * @param string $identifier
563
     * @param string $name
564
     * @param int $grantTypes
565
     * @param string|string[] $redirectURIs
566
     * @param string $type
567
     * @param string|null $secret
568
     * @param string|string[]|null $scopes
569
     * @param int|null $userId
570
     * @return Oauth2ClientInterface
571
     * @throws InvalidConfigException
572
     * @throws \yii\db\Exception
573
     */
574 4
    public function createClient(
575
        $identifier,
576
        $name,
577
        $grantTypes,
578
        $redirectURIs,
579
        $type,
580
        $secret = null,
581
        $scopes = null,
582
        $userId = null
583
    ) {
584 4
        if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) {
585 1
            throw new InvalidCallException('Oauth2 server role does not include authorization server.');
586
        }
587
588
        /** @var Oauth2ClientInterface $client */
589 3
        $client = Yii::createObject([
590
            'class' => Oauth2ClientInterface::class,
591
            'identifier' => $identifier,
592
            'type' => $type,
593
            'name' => $name,
594
            'redirectUri' => $redirectURIs,
595
            'grantTypes' => $grantTypes,
596
            'client_credentials_grant_user_id' => $userId
597
        ]);
598
599 3
        $transaction = $client::getDb()->beginTransaction();
600
601
        try {
602 3
            if ($type == Oauth2ClientInterface::TYPE_CONFIDENTIAL) {
603 3
                $client->setSecret($secret, $this->getEncryptor());
604
            }
605
606 2
            $client->persist();
607
608 2
            if (!empty($scopes)) {
609 2
                if (is_string($scopes)) {
610 2
                    $scopeIdentifiers = explode(' ', $scopes);
611
                } else {
612
                    $scopeIdentifiers = $scopes;
613
                }
614
615 2
                foreach ($scopeIdentifiers as $scopeIdentifier) {
616 2
                    $scope = $this->getScopeRepository()->findModelByIdentifier($scopeIdentifier);
617 2
                    if (empty($scope)) {
618 1
                        throw new InvalidArgumentException('No scope with identifier "'
619
                            . $scopeIdentifier . '" found.');
620
                    }
621
622
                    /** @var Oauth2ClientScopeInterface $clientScope */
623 1
                    $clientScope = Yii::createObject([
624
                        'class' => Oauth2ClientScopeInterface::class,
625 1
                        'client_id' => $client->getPrimaryKey(),
626 1
                        'scope_id' => $scope->getPrimaryKey(),
627
                    ]);
628 1
                    $clientScope->persist();
629
                }
630
            }
631
632 1
            $transaction->commit();
633 2
        } catch (\Exception $e) {
634 2
            $transaction->rollBack();
635 2
            throw $e;
636
        }
637
638 1
        return $client;
639
    }
640
641
    /**
642
     * @return CryptKey The private key of the server.
643
     * @throws InvalidConfigException
644
     * @since 1.0.0
645
     */
646 17
    public function getPrivateKey()
647
    {
648 17
        $privateKey = $this->privateKey;
649 17
        if (StringHelper::startsWith($privateKey, '@')) {
650 14
            $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

650
            $privateKey = 'file://' . /** @scrutinizer ignore-type */ Yii::getAlias($privateKey);
Loading history...
651
        }
652 17
        return Yii::createObject(CryptKey::class, [$privateKey, $this->privateKeyPassphrase]);
653
    }
654
655
    /**
656
     * @return CryptKey The public key of the server.
657
     * @throws InvalidConfigException
658
     * @since 1.0.0
659
     */
660 9
    public function getPublicKey()
661
    {
662 9
        $publicKey = $this->publicKey;
663 9
        if (StringHelper::startsWith($publicKey, '@')) {
664 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

664
            $publicKey = 'file://' . /** @scrutinizer ignore-type */ Yii::getAlias($publicKey);
Loading history...
665
        }
666 9
        return Yii::createObject(CryptKey::class, [$publicKey]);
667
    }
668
669
    /**
670
     * @return Oauth2AuthorizationServerInterface The authorization server.
671
     * @throws InvalidConfigException
672
     * @since 1.0.0
673
     */
674 22
    public function getAuthorizationServer()
675
    {
676 22
        if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) {
677 1
            throw new InvalidCallException('Oauth2 server role does not include authorization server.');
678
        }
679
680 21
        if (!$this->_authorizationServer) {
681 21
            $this->ensureProperties(static::REQUIRED_SETTINGS_AUTHORIZATION_SERVER);
682
683 16
            if (!$this->getEncryptor()->hasKey($this->defaultStorageEncryptionKey)) {
684 1
                throw new InvalidConfigException(
685 1
                    'Key "' . $this->defaultStorageEncryptionKey . '" is not set in $storageEncryptionKeys'
686
                );
687
            }
688
689
            /** @var Oauth2EncryptionKeyFactoryInterface $keyFactory */
690 14
            $keyFactory = Yii::createObject(Oauth2EncryptionKeyFactoryInterface::class);
691
            try {
692 14
                $codesEncryptionKey = $keyFactory->createFromAsciiSafeString($this->codesEncryptionKey);
693 1
            } catch (BadFormatException $e) {
694 1
                throw new InvalidConfigException(
695 1
                    '$codesEncryptionKey is malformed: ' . $e->getMessage(),
696
                    0,
697
                    $e
698
                );
699
            } catch (EnvironmentIsBrokenException $e) {
700
                throw new InvalidConfigException(
701
                    'Could not instantiate $codesEncryptionKey: ' . $e->getMessage(),
702
                    0,
703
                    $e
704
                );
705
            }
706
707 13
            $responseType = null;
708 13
            if ($this->enableOpenIdConnect) {
709 13
                $responseType = Yii::createObject(Oauth2OidcBearerTokenResponseInterface::class, [
710
                    $this,
711
                ]);
712
            }
713
714 13
            $this->_authorizationServer = Yii::createObject(Oauth2AuthorizationServerInterface::class, [
715 13
                $this->getClientRepository(),
716 13
                $this->getAccessTokenRepository(),
717 13
                $this->getScopeRepository(),
718 13
                $this->getPrivateKey(),
719
                $codesEncryptionKey,
720
                $responseType
721
            ]);
722
723 13
            if (!empty($this->grantTypes)) {
724 13
                $grantTypes = $this->grantTypes;
725
726 13
                if (is_callable($grantTypes)) {
727 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

727
                    call_user_func(/** @scrutinizer ignore-type */ $grantTypes, $this->_authorizationServer, $this);
Loading history...
728
                } else {
729 12
                    if (!is_array($grantTypes)) {
730 2
                        $grantTypes = [$grantTypes];
731
                    }
732
733 12
                    foreach ($grantTypes as $grantTypeDefinition) {
734 12
                        if ($grantTypeDefinition instanceof GrantTypeInterface) {
735 1
                            $accessTokenTTL = $this->defaultAccessTokenTTL
736
                                ? new \DateInterval($this->defaultAccessTokenTTL)
737 1
                                : null;
738 1
                            $this->_authorizationServer->enableGrantType($grantTypeDefinition, $accessTokenTTL);
739
                        } elseif (
740
                            (
741 11
                                is_numeric($grantTypeDefinition)
742 11
                                && array_key_exists($grantTypeDefinition, static::DEFAULT_GRANT_TYPE_FACTORIES)
743
                            )
744 11
                            || is_a($grantTypeDefinition, Oauth2GrantTypeFactoryInterface::class, true)
745
                        ) {
746
                            if (
747 10
                                is_numeric($grantTypeDefinition)
748 10
                                && array_key_exists($grantTypeDefinition, static::DEFAULT_GRANT_TYPE_FACTORIES)
749
                            ) {
750 10
                                $grantTypeDefinition = static::DEFAULT_GRANT_TYPE_FACTORIES[$grantTypeDefinition];
751
                            }
752
753
                            /** @var Oauth2GrantTypeFactoryInterface $factory */
754 10
                            $factory = Yii::createObject([
755
                                'class' => $grantTypeDefinition,
756
                                'module' => $this,
757
                            ]);
758 10
                            $accessTokenTTL = $factory->accessTokenTTL ?? $this->defaultAccessTokenTTL ?? null;
0 ignored issues
show
Bug introduced by
Accessing accessTokenTTL on the interface rhertogh\Yii2Oauth2Serve...antTypeFactoryInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
759 10
                            $this->_authorizationServer->enableGrantType(
760 10
                                $factory->getGrantType(),
761 10
                                $accessTokenTTL ? new \DateInterval($accessTokenTTL) : null
762
                            );
763
                        } else {
764 1
                            throw new InvalidConfigException(
765
                                'Unknown grantType '
766
                                . (
767 1
                                    is_scalar($grantTypeDefinition)
768 1
                                        ? '"' . $grantTypeDefinition . '".'
769 1
                                        : 'with data type ' . gettype($grantTypeDefinition)
770
                                )
771
                            );
772
                        }
773
                    }
774
                }
775
            }
776
        }
777
778 12
        return $this->_authorizationServer;
779
    }
780
781
    /**
782
     * @inheritDoc
783
     * @throws InvalidConfigException
784
     */
785 5
    public function getOidcScopeCollection()
786
    {
787 5
        if ($this->_oidcScopeCollection === null) {
788 5
            $openIdConnectScopes = $this->getOpenIdConnectScopes();
789 5
            if ($openIdConnectScopes instanceof Oauth2OidcScopeCollectionInterface) {
790 1
                $this->_oidcScopeCollection = $openIdConnectScopes;
791 4
            } elseif (is_callable($openIdConnectScopes)) {
792 1
                $this->_oidcScopeCollection = call_user_func($openIdConnectScopes, $this);
793 1
                if (!($this->_oidcScopeCollection instanceof Oauth2OidcScopeCollectionInterface)) {
794
                    throw new InvalidConfigException(
795
                        '$openIdConnectScopes must return an instance of '
796
                            . Oauth2OidcScopeCollectionInterface::class
797
                    );
798
                }
799 3
            } elseif (is_array($openIdConnectScopes) || is_string($openIdConnectScopes)) {
800 2
                $this->_oidcScopeCollection = Yii::createObject([
801
                    'class' => Oauth2OidcScopeCollectionInterface::class,
802 2
                    'oidcScopes' => (array)$openIdConnectScopes,
803
                ]);
804
            } else {
805 1
                throw new InvalidConfigException(
806
                    '$openIdConnectScopes must be a callable, array, string or '
807
                        . Oauth2OidcScopeCollectionInterface::class
808
                );
809
            }
810
        }
811
812 4
        return $this->_oidcScopeCollection;
813
    }
814
815
    /**
816
     * @return Oauth2ResourceServerInterface The resource server.
817
     * @throws InvalidConfigException
818
     * @since 1.0.0
819
     */
820 7
    public function getResourceServer()
821
    {
822 7
        if (!($this->serverRole & static::SERVER_ROLE_RESOURCE_SERVER)) {
823 1
            throw new InvalidCallException('Oauth2 server role does not include resource server.');
824
        }
825
826 6
        if (!$this->_resourceServer) {
827 6
            $this->ensureProperties(static::REQUIRED_SETTINGS_RESOURCE_SERVER);
828
829 5
            $accessTokenRepository = $this->getAccessTokenRepository()
830 5
                ->setRevocationValidation($this->resourceServerAccessTokenRevocationValidation);
831
832 5
            $this->_resourceServer = Yii::createObject(Oauth2ResourceServerInterface::class, [
833
                $accessTokenRepository,
834 5
                $this->getPublicKey(),
835
            ]);
836
        }
837
838 5
        return $this->_resourceServer;
839
    }
840
841
    /**
842
     * @return Oauth2EncryptorInterface The data encryptor for the module.
843
     * @throws InvalidConfigException
844
     * @since 1.0.0
845
     */
846 21
    public function getEncryptor()
847
    {
848 21
        if (!$this->_encryptor) {
849 21
            $this->_encryptor = Yii::createObject([
850
                'class' => Oauth2EncryptorInterface::class,
851 21
                'keys' => $this->storageEncryptionKeys,
852 21
                'defaultKeyName' => $this->defaultStorageEncryptionKey,
853
            ]);
854
        }
855
856 20
        return $this->_encryptor;
857
    }
858
859
    /**
860
     * @param string|null $newKeyName
861
     * @return array
862
     * @throws InvalidConfigException
863
     */
864 1
    public function rotateStorageEncryptionKeys($newKeyName = null)
865
    {
866 1
        $encryptor = $this->getEncryptor();
867
868 1
        $result = [];
869 1
        foreach (static::ENCRYPTED_MODELS as $modelInterface) {
870 1
            $modelClass = DiHelper::getValidatedClassName($modelInterface);
871 1
            if (!is_a($modelClass, Oauth2EncryptedStorageInterface::class, true)) {
872
                throw new InvalidConfigException($modelInterface . ' must implement '
873
                    . Oauth2EncryptedStorageInterface::class);
874
            }
875 1
            $result[$modelClass] = $modelClass::rotateStorageEncryptionKeys($encryptor, $newKeyName);
876
        }
877
878 1
        return $result;
879
    }
880
881
    /**
882
     * @return array
883
     * @throws InvalidConfigException
884
     */
885
    public function getStorageEncryptionKeyUsage()
886
    {
887
        $encryptor = $this->getEncryptor();
888
889
        $result = [];
890
        foreach (static::ENCRYPTED_MODELS as $modelInterface) {
891
            $modelClass = DiHelper::getValidatedClassName($modelInterface);
892
            if (!is_a($modelClass, Oauth2EncryptedStorageInterface::class, true)) {
893
                throw new InvalidConfigException($modelInterface . ' must implement '
894
                    . Oauth2EncryptedStorageInterface::class);
895
            }
896
897
            $result[$modelClass] = $modelClass::getUsedStorageEncryptionKeys($encryptor);
898
        }
899
900
        return $result;
901
    }
902
903
    /**
904
     * Generates a redirect Response to the client authorization page where the user is prompted to authorize the
905
     * client and requested scope.
906
     * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest
907
     * @return Response
908
     * @since 1.0.0
909
     */
910 5
    public function generateClientAuthReqRedirectResponse($clientAuthorizationRequest)
911
    {
912 5
        $this->setClientAuthReqSession($clientAuthorizationRequest);
913 5
        if (!empty($this->clientAuthorizationUrl)) {
914 1
            $url = $this->clientAuthorizationUrl;
915
        } else {
916 4
            $url = $this->uniqueId
917
                . '/' . Oauth2ConsentControllerInterface::CONTROLLER_NAME
918
                . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_CLIENT;
919
        }
920 5
        return Yii::$app->response->redirect([
921
            $url,
922 5
            'clientAuthorizationRequestId' => $clientAuthorizationRequest->getRequestId(),
923
        ]);
924
    }
925
926
    /**
927
     * Get a previously stored Client Authorization Request from the session.
928
     * @param string $requestId
929
     * @return Oauth2ClientAuthorizationRequestInterface|null
930
     * @since 1.0.0
931
     */
932 5
    public function getClientAuthReqSession($requestId)
933
    {
934 5
        if (empty($requestId)) {
935
            return null;
936
        }
937 5
        $key = static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX . $requestId;
938 5
        $clientAuthorizationRequest = 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

938
        /** @scrutinizer ignore-call */ 
939
        $clientAuthorizationRequest = 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...
939 5
        if (!($clientAuthorizationRequest instanceof Oauth2ClientAuthorizationRequestInterface)) {
940 2
            if (!empty($clientAuthorizationRequest)) {
941 1
                Yii::warning(
942 1
                    'Found a ClientAuthorizationRequestSession with key "' . $key
943
                        . '", but it\'s not a ' . Oauth2ClientAuthorizationRequestInterface::class
944
                );
945
            }
946 2
            return null;
947
        }
948 5
        if ($clientAuthorizationRequest->getRequestId() !== $requestId) {
949 1
            Yii::warning(
950 1
                'Found a ClientAuthorizationRequestSession with key "' . $key
951
                    . '", but it\'s request id does not match "' . $requestId . '".'
952
            );
953 1
            return null;
954
        }
955 5
        $clientAuthorizationRequest->setModule($this);
956
957 5
        return $clientAuthorizationRequest;
958
    }
959
960
    /**
961
     * Stores the Client Authorization Request in the session.
962
     * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest
963
     * @since 1.0.0
964
     */
965 8
    public function setClientAuthReqSession($clientAuthorizationRequest)
966
    {
967 8
        $requestId = $clientAuthorizationRequest->getRequestId();
968 8
        if (empty($requestId)) {
969 1
            throw new InvalidArgumentException('$scopeAuthorization must return a request id.');
970
        }
971 7
        $key = static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX . $requestId;
972 7
        Yii::$app->session->set($key, $clientAuthorizationRequest);
973
    }
974
975
    /**
976
     * Stores whether the user was authenticated during the completion of the Client Authorization Request.
977
     * @param string $clientAuthorizationRequestId
978
     * @param bool $authenticatedDuringRequest
979
     * @since 1.0.0
980
     */
981
    public function setUserAuthenticatedDuringClientAuthRequest(
982
        $clientAuthorizationRequestId,
983
        $authenticatedDuringRequest
984
    ) {
985
        $clientAuthorizationRequest = $this->getClientAuthReqSession($clientAuthorizationRequestId);
986
        if ($clientAuthorizationRequest) {
987
            $clientAuthorizationRequest->setUserAuthenticatedDuringRequest($authenticatedDuringRequest);
988
            $this->setClientAuthReqSession($clientAuthorizationRequest);
989
        }
990
    }
991
992
    /**
993
     * Stores the user identity selected during the completion of the Client Authorization Request.
994
     * @param string $clientAuthorizationRequestId
995
     * @param Oauth2UserInterface $userIdentity
996
     * @since 1.0.0
997
     */
998
    public function setClientAuthRequestUserIdentity($clientAuthorizationRequestId, $userIdentity)
999
    {
1000
        $clientAuthorizationRequest = $this->getClientAuthReqSession($clientAuthorizationRequestId);
1001
        if ($clientAuthorizationRequest) {
1002
            $clientAuthorizationRequest->setUserIdentity($userIdentity);
1003
            $this->setClientAuthReqSession($clientAuthorizationRequest);
1004
        }
1005
    }
1006
1007
    /**
1008
     * Clears a Client Authorization Request from the session storage.
1009
     * @param string $requestId
1010
     * @since 1.0.0
1011
     */
1012 2
    public function removeClientAuthReqSession($requestId)
1013
    {
1014 2
        if (empty($requestId)) {
1015 1
            throw new InvalidArgumentException('$requestId can not be empty.');
1016
        }
1017 1
        $key = static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX . $requestId;
1018 1
        Yii::$app->session->remove($key);
1019
    }
1020
1021
    /**
1022
     * Generates a redirect Response when the Client Authorization Request is completed.
1023
     * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest
1024
     * @return Response
1025
     * @since 1.0.0
1026
     */
1027 1
    public function generateClientAuthReqCompledRedirectResponse($clientAuthorizationRequest)
1028
    {
1029 1
        $clientAuthorizationRequest->processAuthorization();
1030 1
        $this->setClientAuthReqSession($clientAuthorizationRequest);
1031 1
        return Yii::$app->response->redirect($clientAuthorizationRequest->getAuthorizationRequestUrl());
1032
    }
1033
1034
    /**
1035
     * @return IdentityInterface|Oauth2UserInterface|Oauth2OidcUserInterface|null
1036
     * @throws InvalidConfigException
1037
     * @since 1.0.0
1038
     */
1039 5
    public function getUserIdentity()
1040
    {
1041 5
        $user = Yii::$app->user->identity;
1042 5
        if (!empty($user) && !($user instanceof Oauth2UserInterface)) {
1043 1
            throw new InvalidConfigException(
1044 1
                'Yii::$app->user->identity (currently ' . get_class($user)
1045
                    . ') must implement ' . Oauth2UserInterface::class
1046
            );
1047
        }
1048 4
        return $user;
1049
    }
1050
1051
    /**
1052
     * Validates a bearer token authenticated request. Note: this method does not return a result but will throw
1053
     * an exception when the authentication fails.
1054
     * @throws InvalidConfigException
1055
     * @throws OAuthServerException
1056
     * @since 1.0.0
1057
     */
1058 3
    public function validateAuthenticatedRequest()
1059
    {
1060 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

1060
        $psr7Request = Psr7Helper::yiiToPsr7Request(/** @scrutinizer ignore-type */ Yii::$app->request);
Loading history...
1061
1062 3
        $psr7Request = $this->getResourceServer()->validateAuthenticatedRequest($psr7Request);
1063
1064 3
        $this->_oauthClaims = $psr7Request->getAttributes();
1065 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...
1066
    }
1067
1068
    /**
1069
     * Find a user identity bases on an access token.
1070
     * Note: validateAuthenticatedRequest() must be called before this method is called.
1071
     * @param string $token
1072
     * @param string $type
1073
     * @return Oauth2UserInterface|null
1074
     * @throws InvalidConfigException
1075
     * @throws OAuthServerException
1076
     * @see validateAuthenticatedRequest()
1077
     * @since 1.0.0
1078
     */
1079 4
    public function findIdentityByAccessToken($token, $type)
1080
    {
1081 4
        if (!is_a($type, Oauth2HttpBearerAuthInterface::class, true)) {
1082 1
            throw new InvalidCallException($type . ' must implement ' . Oauth2HttpBearerAuthInterface::class);
1083
        }
1084
1085
        if (
1086 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

1086
            !preg_match('/^Bearer\s+(.*?)$/', /** @scrutinizer ignore-type */ $this->_oauthClaimsAuthorizationHeader, $matches)
Loading history...
1087 3
            || !Yii::$app->security->compareString($matches[1], $token)
1088
        ) {
1089 1
            throw new InvalidCallException(
1090
                'validateAuthenticatedRequest() must be called before findIdentityByAccessToken().'
1091
            );
1092
        }
1093
1094 2
        $userId = $this->getRequestOauthUserId();
1095 2
        if (empty($userId)) {
1096 1
            return null;
1097
        }
1098
1099 1
        return $this->identityClass::findIdentity($userId);
0 ignored issues
show
Bug introduced by
The method findIdentity() 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

1099
        return $this->identityClass::/** @scrutinizer ignore-call */ findIdentity($userId);

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...
1100
    }
1101
1102
    /**
1103
     * @inheritDoc
1104
     */
1105 5
    protected function getRequestOauthClaim($attribute, $default = null)
1106
    {
1107 5
        if (empty($this->_oauthClaimsAuthorizationHeader)) {
1108
            // User authorization was not processed by Oauth2Module.
1109 1
            return $default;
1110
        }
1111 4
        if (Yii::$app->request->getHeaders()->get('Authorization') !== $this->_oauthClaimsAuthorizationHeader) {
1112 1
            throw new InvalidCallException(
1113
                'App Request Authorization header does not match the processed Oauth header.'
1114
            );
1115
        }
1116 3
        return $this->_oauthClaims[$attribute] ?? $default;
1117
    }
1118
1119
    /**
1120
     * Helper function to ensure the required properties are configured for the module.
1121
     * @param string[] $properties
1122
     * @throws InvalidConfigException
1123
     * @since 1.0.0
1124
     */
1125 27
    protected function ensureProperties($properties)
1126
    {
1127 27
        foreach ($properties as $property) {
1128 27
            if (empty($this->$property)) {
1129 6
                throw new InvalidConfigException('$' . $property . ' must be set.');
1130
            }
1131
        }
1132
    }
1133
}
1134