Passed
Branch master (9b4352)
by Rutger
13:03
created

Oauth2Module::getResourceServer()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 19
ccs 10
cts 10
cp 1
rs 9.9332
cc 3
nc 3
nop 0
crap 3
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\Oauth2ClientInterface;
39
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientScopeInterface;
40
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2OidcUserInterface;
41
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2UserInterface;
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 bool 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 string $type
565
     * @param string|string[]|null $scopes
566
     * @param int $grantTypes
567
     * @param string|string[] $redirectURIs
568
     * @return Oauth2ClientInterface
569
     * @throws InvalidConfigException
570
     * @throws \yii\db\Exception
571
     */
572 4
    public function createClient($identifier, $name, $type, $secret, $grantTypes, $redirectURIs, $scopes = null)
573
    {
574 4
        if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) {
575 1
            throw new InvalidCallException('Oauth2 server role does not include authorization server.');
576
        }
577
578
        /** @var Oauth2ClientInterface $client */
579 3
        $client = Yii::createObject([
580
            'class' => Oauth2ClientInterface::class,
581
            'identifier' => $identifier,
582
            'type' => $type,
583
            'name' => $name,
584
            'redirect_uris' => $redirectURIs,
585
            'token_types' => 1, # Bearer
586
            'grant_types' => $grantTypes,
587
        ]);
588
589 3
        $transaction = $client::getDb()->beginTransaction();
590
591
        try {
592 3
            if ($type == Oauth2ClientInterface::TYPE_CONFIDENTIAL) {
593 3
                $client->setSecret($secret, $this->getEncryptor());
594
            }
595
596 2
            $client->persist();
597
598 2
            if (!empty($scopes)) {
599 2
                $scopeIdentifiers = explode(' ', $scopes);
0 ignored issues
show
Bug introduced by
It seems like $scopes can also be of type string[]; however, parameter $string of explode() 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

599
                $scopeIdentifiers = explode(' ', /** @scrutinizer ignore-type */ $scopes);
Loading history...
600 2
                foreach ($scopeIdentifiers as $scopeIdentifier) {
601
602 2
                    $scope = $this->getScopeRepository()->findModelByIdentifier($scopeIdentifier);
603 2
                    if (empty($scope)) {
604 1
                        throw new InvalidArgumentException('No scope with identifier "'
605
                            . $scopeIdentifier . '" found.');
606
                    }
607
608
                    /** @var Oauth2ClientScopeInterface $clientScope */
609 1
                    $clientScope = Yii::createObject([
610
                        'class' => Oauth2ClientScopeInterface::class,
611 1
                        'client_id' => $client->getPrimaryKey(),
612 1
                        'scope_id' => $scope->getPrimaryKey(),
613
                    ]);
614 1
                    $clientScope->persist();
615
                }
616
            }
617
618 1
            $transaction->commit();
619
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
620 2
        } catch (\Exception $e) {
621 2
            $transaction->rollBack();
622 2
            throw $e;
623
        }
624
625 1
        return $client;
626
    }
627
628
    /**
629
     * @return CryptKey The private key of the server.
630
     * @throws InvalidConfigException
631
     * @since 1.0.0
632
     */
633 17
    public function getPrivateKey()
634
    {
635 17
        $privateKey = $this->privateKey;
636 17
        if (StringHelper::startsWith($privateKey, '@')) {
637 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

637
            $privateKey = 'file://' . /** @scrutinizer ignore-type */ Yii::getAlias($privateKey);
Loading history...
638
        }
639 17
        return Yii::createObject(CryptKey::class, [$privateKey, $this->privateKeyPassphrase]);
640
    }
641
642
    /**
643
     * @return CryptKey The public key of the server.
644
     * @throws InvalidConfigException
645
     * @since 1.0.0
646
     */
647 9
    public function getPublicKey()
648
    {
649 9
        $publicKey = $this->publicKey;
650 9
        if (StringHelper::startsWith($publicKey, '@')) {
651 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

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

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

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

1046
        $psr7Request = Psr7Helper::yiiToPsr7Request(/** @scrutinizer ignore-type */ Yii::$app->request);
Loading history...
1047
1048 3
        $psr7Request = $this->getResourceServer()->validateAuthenticatedRequest($psr7Request);
1049
1050 3
        $this->_oauthClaims = $psr7Request->getAttributes();
1051 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...
1052
    }
1053
1054
    /**
1055
     * Find a user identity bases on an access token.
1056
     * Note: validateAuthenticatedRequest() must be called before this method is called.
1057
     * @param string $token
1058
     * @param string $type
1059
     * @return Oauth2UserInterface|null
1060
     * @throws InvalidConfigException
1061
     * @throws OAuthServerException
1062
     * @see validateAuthenticatedRequest()
1063
     * @since 1.0.0
1064
     */
1065 4
    public function findIdentityByAccessToken($token, $type)
1066
    {
1067 4
        if (!is_a($type, Oauth2HttpBearerAuthInterface::class, true)) {
1068 1
            throw new InvalidCallException($type . ' must implement ' . Oauth2HttpBearerAuthInterface::class);
1069
        }
1070
1071
        if (
1072 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

1072
            !preg_match('/^Bearer\s+(.*?)$/', /** @scrutinizer ignore-type */ $this->_oauthClaimsAuthorizationHeader, $matches)
Loading history...
1073 3
            || !Yii::$app->security->compareString($matches[1], $token)
1074
        ) {
1075 1
            throw new InvalidCallException(
1076
                'validateAuthenticatedRequest() must be called before findIdentityByAccessToken().'
1077
            );
1078
        }
1079
1080 2
        $userId = $this->getRequestOauthUserId();
1081 2
        if (empty($userId)) {
1082 1
            return null;
1083
        }
1084
1085 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

1085
        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...
1086
    }
1087
1088
    /**
1089
     * @inheritDoc
1090
     */
1091 5
    protected function getRequestOauthClaim($attribute, $default = null)
1092
    {
1093 5
        if (empty($this->_oauthClaimsAuthorizationHeader)) {
1094
            // User authorization was not processed by Oauth2Module.
1095 1
            return $default;
1096
        }
1097 4
        if (Yii::$app->request->getHeaders()->get('Authorization') !== $this->_oauthClaimsAuthorizationHeader) {
1098 1
            throw new InvalidCallException(
1099
                'App Request Authorization header does not match the processed Oauth header.'
1100
            );
1101
        }
1102 3
        return $this->_oauthClaims[$attribute] ?? $default;
1103
    }
1104
1105
    /**
1106
     * Helper function to ensure the required properties are configured for the module.
1107
     * @param $properties
1108
     * @throws InvalidConfigException
1109
     * @since 1.0.0
1110
     */
1111 27
    protected function ensureProperties($properties)
1112
    {
1113 27
        foreach ($properties as $property) {
1114 27
            if (empty($this->$property)) {
1115 6
                throw new InvalidConfigException('$' . $property . ' must be set.');
1116
            }
1117
        }
1118
    }
1119
}
1120