Passed
Push — master ( 9b4352...ff9a05 )
by Rutger
03:36
created

Oauth2Module::getPrivateKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 10
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
/**
4
 * @link http://www.yiiframework.com/
5
 * @copyright Copyright (c) 2008 Yii Software LLC
6
 * @license http://www.yiiframework.com/license/
7
 */
8
9
namespace rhertogh\Yii2Oauth2Server;
10
11
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 int $grantTypes
565
     * @param string|string[] $redirectURIs
566
     * @param string $type
567
     * @param string|string[]|null $scopes
568
     * @return Oauth2ClientInterface
569
     * @throws InvalidConfigException
570
     * @throws \yii\db\Exception
571
     */
572 4
    public function createClient($identifier, $name, $grantTypes, $redirectURIs, $type, $secret = null, $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
                if (is_string($scopes)) {
600 2
                    $scopeIdentifiers = explode(' ', $scopes);
601
                } else {
602
                    $scopeIdentifiers = $scopes;
603
                }
604
605 2
                foreach ($scopeIdentifiers as $scopeIdentifier) {
606
607 2
                    $scope = $this->getScopeRepository()->findModelByIdentifier($scopeIdentifier);
608 2
                    if (empty($scope)) {
609 1
                        throw new InvalidArgumentException('No scope with identifier "'
610
                            . $scopeIdentifier . '" found.');
611
                    }
612
613
                    /** @var Oauth2ClientScopeInterface $clientScope */
614 1
                    $clientScope = Yii::createObject([
615
                        'class' => Oauth2ClientScopeInterface::class,
616 1
                        'client_id' => $client->getPrimaryKey(),
617 1
                        'scope_id' => $scope->getPrimaryKey(),
618
                    ]);
619 1
                    $clientScope->persist();
620
                }
621
            }
622
623 1
            $transaction->commit();
624
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
625 2
        } catch (\Exception $e) {
626 2
            $transaction->rollBack();
627 2
            throw $e;
628
        }
629
630 1
        return $client;
631
    }
632
633
    /**
634
     * @return CryptKey The private key of the server.
635
     * @throws InvalidConfigException
636
     * @since 1.0.0
637
     */
638 17
    public function getPrivateKey()
639
    {
640 17
        $privateKey = $this->privateKey;
641 17
        if (StringHelper::startsWith($privateKey, '@')) {
642 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

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

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

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

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

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

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

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