rhertogh /
yii2-oauth2-server
| 1 | <?php |
||||||
| 2 | |||||||
| 3 | /** |
||||||
| 4 | * @link http://www.yiiframework.com/ |
||||||
| 5 | * @copyright Copyright (c) 2008 Yii Software LLC |
||||||
| 6 | * @license http://www.yiiframework.com/license/ |
||||||
| 7 | */ |
||||||
| 8 | |||||||
| 9 | namespace rhertogh\Yii2Oauth2Server; |
||||||
| 10 | |||||||
| 11 | // phpcs:disable Generic.Files.LineLength.TooLong |
||||||
| 12 | use Defuse\Crypto\Exception\BadFormatException; |
||||||
| 13 | use Defuse\Crypto\Exception\EnvironmentIsBrokenException; |
||||||
| 14 | use GuzzleHttp\Psr7\Response as Psr7Response; |
||||||
| 15 | use GuzzleHttp\Psr7\ServerRequest as Psr7ServerRequest; |
||||||
| 16 | use Lcobucci\JWT\Configuration; |
||||||
| 17 | use Lcobucci\JWT\Signer\Key\InMemory; |
||||||
| 18 | use Lcobucci\JWT\Signer\Rsa\Sha256; |
||||||
| 19 | use Lcobucci\JWT\Token; |
||||||
| 20 | use Lcobucci\JWT\Validation\Constraint\SignedWith; |
||||||
| 21 | use League\OAuth2\Server\CryptKey; |
||||||
| 22 | use League\OAuth2\Server\Grant\GrantTypeInterface; |
||||||
| 23 | use rhertogh\Yii2Oauth2Server\base\Oauth2BaseModule; |
||||||
| 24 | use rhertogh\Yii2Oauth2Server\components\server\tokens\Oauth2AccessTokenData; |
||||||
| 25 | use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2ClientController; |
||||||
| 26 | use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2DebugController; |
||||||
| 27 | use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2EncryptionController; |
||||||
| 28 | use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2MigrationsController; |
||||||
| 29 | use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2PersonalAccessTokenController; |
||||||
| 30 | use rhertogh\Yii2Oauth2Server\exceptions\Oauth2ServerException; |
||||||
| 31 | use rhertogh\Yii2Oauth2Server\helpers\DiHelper; |
||||||
| 32 | use rhertogh\Yii2Oauth2Server\helpers\Psr7Helper; |
||||||
| 33 | use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\base\Oauth2BaseAuthorizationRequestInterface; |
||||||
| 34 | use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\client\Oauth2ClientAuthorizationRequestInterface; |
||||||
| 35 | use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\EndSession\Oauth2EndSessionAuthorizationRequestInterface; |
||||||
| 36 | use rhertogh\Yii2Oauth2Server\interfaces\components\common\DefaultAccessTokenTtlInterface; |
||||||
| 37 | use rhertogh\Yii2Oauth2Server\interfaces\components\encryption\Oauth2CryptographerInterface; |
||||||
| 38 | use rhertogh\Yii2Oauth2Server\interfaces\components\factories\encryption\Oauth2EncryptionKeyFactoryInterface; |
||||||
| 39 | use rhertogh\Yii2Oauth2Server\interfaces\components\factories\grants\base\Oauth2GrantTypeFactoryInterface; |
||||||
| 40 | use rhertogh\Yii2Oauth2Server\interfaces\components\openidconnect\request\Oauth2OidcAuthenticationRequestInterface; |
||||||
| 41 | use rhertogh\Yii2Oauth2Server\interfaces\components\openidconnect\scope\Oauth2OidcScopeCollectionInterface; |
||||||
| 42 | use rhertogh\Yii2Oauth2Server\interfaces\components\openidconnect\server\responses\Oauth2OidcBearerTokenResponseInterface; |
||||||
| 43 | use rhertogh\Yii2Oauth2Server\interfaces\components\server\Oauth2AuthorizationServerInterface; |
||||||
| 44 | use rhertogh\Yii2Oauth2Server\interfaces\components\server\Oauth2ResourceServerInterface; |
||||||
| 45 | use rhertogh\Yii2Oauth2Server\interfaces\components\server\responses\Oauth2BearerTokenResponseInterface; |
||||||
| 46 | use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2CertificatesControllerInterface; |
||||||
| 47 | use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2ConsentControllerInterface; |
||||||
| 48 | use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2OidcControllerInterface; |
||||||
| 49 | use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2ServerControllerInterface; |
||||||
| 50 | use rhertogh\Yii2Oauth2Server\interfaces\controllers\web\Oauth2WellKnownControllerInterface; |
||||||
| 51 | use rhertogh\Yii2Oauth2Server\interfaces\filters\auth\Oauth2HttpBearerAuthInterface; |
||||||
| 52 | use rhertogh\Yii2Oauth2Server\interfaces\models\base\Oauth2EncryptedStorageInterface; |
||||||
| 53 | use rhertogh\Yii2Oauth2Server\interfaces\models\external\user\Oauth2OidcUserInterface; |
||||||
| 54 | use rhertogh\Yii2Oauth2Server\interfaces\models\external\user\Oauth2UserInterface; |
||||||
| 55 | use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientInterface; |
||||||
| 56 | use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ScopeInterface; |
||||||
| 57 | use rhertogh\Yii2Oauth2Server\traits\DefaultAccessTokenTtlTrait; |
||||||
| 58 | use Yii; |
||||||
| 59 | use yii\base\BootstrapInterface; |
||||||
| 60 | use yii\base\InvalidArgumentException; |
||||||
| 61 | use yii\base\InvalidCallException; |
||||||
| 62 | use yii\base\InvalidConfigException; |
||||||
| 63 | use yii\console\Application as ConsoleApplication; |
||||||
| 64 | use yii\helpers\ArrayHelper; |
||||||
| 65 | use yii\helpers\Json; |
||||||
| 66 | use yii\helpers\StringHelper; |
||||||
| 67 | use yii\helpers\VarDumper; |
||||||
| 68 | use yii\i18n\PhpMessageSource; |
||||||
| 69 | use yii\log\Logger; |
||||||
| 70 | use yii\validators\IpValidator; |
||||||
| 71 | use yii\web\Application as WebApplication; |
||||||
| 72 | use yii\web\GroupUrlRule; |
||||||
| 73 | use yii\web\IdentityInterface; |
||||||
| 74 | use yii\web\Response; |
||||||
| 75 | use yii\web\UrlRule; |
||||||
| 76 | |||||||
| 77 | // phpcs:enable Generic.Files.LineLength.TooLong |
||||||
| 78 | |||||||
| 79 | /** |
||||||
| 80 | * This is the main module class for the Yii2 Oauth2 Server module. |
||||||
| 81 | * To use it, include it as a module in the application configuration like the following: |
||||||
| 82 | * |
||||||
| 83 | * ~~~ |
||||||
| 84 | * return [ |
||||||
| 85 | * 'bootstrap' => ['oauth2'], |
||||||
| 86 | * 'modules' => [ |
||||||
| 87 | * 'oauth2' => [ |
||||||
| 88 | * 'class' => 'rhertogh\Yii2Oauth2Server\Oauth2Module', |
||||||
| 89 | * // ... Please check docs/guide/start-installation.md further details |
||||||
| 90 | * ], |
||||||
| 91 | * ], |
||||||
| 92 | * ] |
||||||
| 93 | * ~~~ |
||||||
| 94 | * |
||||||
| 95 | * @property \DateInterval|string|null $defaultAccessTokenTTL |
||||||
| 96 | * @since 1.0.0 |
||||||
| 97 | */ |
||||||
| 98 | class Oauth2Module extends Oauth2BaseModule implements BootstrapInterface, DefaultAccessTokenTtlInterface |
||||||
| 99 | { |
||||||
| 100 | use DefaultAccessTokenTtlTrait; |
||||||
| 101 | |||||||
| 102 | /** |
||||||
| 103 | * Application type "web": http response. |
||||||
| 104 | * @since 1.0.0 |
||||||
| 105 | */ |
||||||
| 106 | public const APPLICATION_TYPE_WEB = 'web'; |
||||||
| 107 | /** |
||||||
| 108 | * Application type "console": cli response. |
||||||
| 109 | * @since 1.0.0 |
||||||
| 110 | */ |
||||||
| 111 | public const APPLICATION_TYPE_CONSOLE = 'console'; |
||||||
| 112 | /** |
||||||
| 113 | * Supported Application types. |
||||||
| 114 | * @since 1.0.0 |
||||||
| 115 | */ |
||||||
| 116 | public const APPLICATION_TYPES = [ |
||||||
| 117 | self::APPLICATION_TYPE_WEB, |
||||||
| 118 | self::APPLICATION_TYPE_CONSOLE, |
||||||
| 119 | ]; |
||||||
| 120 | |||||||
| 121 | /** |
||||||
| 122 | * "Authorization Server" Role, please see guide for details. |
||||||
| 123 | * @since 1.0.0 |
||||||
| 124 | */ |
||||||
| 125 | public const SERVER_ROLE_AUTHORIZATION_SERVER = 1; |
||||||
| 126 | /** |
||||||
| 127 | * "Resource Server" Role, please see guide for details. |
||||||
| 128 | * @since 1.0.0 |
||||||
| 129 | */ |
||||||
| 130 | public const SERVER_ROLE_RESOURCE_SERVER = 2; |
||||||
| 131 | |||||||
| 132 | /** |
||||||
| 133 | * Required settings when the server role includes Authorization Server |
||||||
| 134 | * @since 1.0.0 |
||||||
| 135 | */ |
||||||
| 136 | protected const REQUIRED_SETTINGS_AUTHORIZATION_SERVER = [ |
||||||
| 137 | 'codesEncryptionKey', |
||||||
| 138 | 'storageEncryptionKeys', |
||||||
| 139 | 'defaultStorageEncryptionKey', |
||||||
| 140 | 'privateKey', |
||||||
| 141 | 'publicKey', |
||||||
| 142 | ]; |
||||||
| 143 | |||||||
| 144 | /** |
||||||
| 145 | * Encrypted Models |
||||||
| 146 | * |
||||||
| 147 | * @since 1.0.0 |
||||||
| 148 | */ |
||||||
| 149 | protected const ENCRYPTED_MODELS = [ |
||||||
| 150 | Oauth2ClientInterface::class, |
||||||
| 151 | ]; |
||||||
| 152 | |||||||
| 153 | /** |
||||||
| 154 | * Required settings when the server role includes Resource Server |
||||||
| 155 | * @since 1.0.0 |
||||||
| 156 | */ |
||||||
| 157 | protected const REQUIRED_SETTINGS_RESOURCE_SERVER = [ |
||||||
| 158 | 'publicKey', |
||||||
| 159 | ]; |
||||||
| 160 | |||||||
| 161 | /** |
||||||
| 162 | * Prefix used in session storage of Client Authorization Requests |
||||||
| 163 | * @since 1.0.0 |
||||||
| 164 | */ |
||||||
| 165 | protected const CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX = 'OAUTH2_CLIENT_AUTHORIZATION_REQUEST_'; |
||||||
| 166 | |||||||
| 167 | /** |
||||||
| 168 | * Prefix used in session storage of End Session Authorization Requests. |
||||||
| 169 | * |
||||||
| 170 | * @since 1.0.0 |
||||||
| 171 | */ |
||||||
| 172 | protected const END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX = 'OAUTH2_END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX_'; // phpcs:ignore Generic.Files.LineLength.TooLong |
||||||
| 173 | |||||||
| 174 | /** |
||||||
| 175 | * Controller mapping for the module. Will be parsed on `init()`. |
||||||
| 176 | * @since 1.0.0 |
||||||
| 177 | */ |
||||||
| 178 | protected const CONTROLLER_MAP = [ |
||||||
| 179 | self::APPLICATION_TYPE_WEB => [ |
||||||
| 180 | Oauth2ServerControllerInterface::CONTROLLER_NAME => [ |
||||||
| 181 | 'controller' => Oauth2ServerControllerInterface::class, |
||||||
| 182 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 183 | ], |
||||||
| 184 | Oauth2ConsentControllerInterface::CONTROLLER_NAME => [ |
||||||
| 185 | 'controller' => Oauth2ConsentControllerInterface::class, |
||||||
| 186 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 187 | ], |
||||||
| 188 | Oauth2WellKnownControllerInterface::CONTROLLER_NAME => [ |
||||||
| 189 | 'controller' => Oauth2WellKnownControllerInterface::class, |
||||||
| 190 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 191 | ], |
||||||
| 192 | Oauth2CertificatesControllerInterface::CONTROLLER_NAME => [ |
||||||
| 193 | 'controller' => Oauth2CertificatesControllerInterface::class, |
||||||
| 194 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 195 | ], |
||||||
| 196 | Oauth2OidcControllerInterface::CONTROLLER_NAME => [ |
||||||
| 197 | 'controller' => Oauth2OidcControllerInterface::class, |
||||||
| 198 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 199 | ], |
||||||
| 200 | ], |
||||||
| 201 | self::APPLICATION_TYPE_CONSOLE => [ |
||||||
| 202 | 'migrations' => [ |
||||||
| 203 | 'controller' => Oauth2MigrationsController::class, |
||||||
| 204 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER, |
||||||
| 205 | ], |
||||||
| 206 | 'client' => [ |
||||||
| 207 | 'controller' => Oauth2ClientController::class, |
||||||
| 208 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 209 | ], |
||||||
| 210 | 'encryption' => [ |
||||||
| 211 | 'controller' => Oauth2EncryptionController::class, |
||||||
| 212 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 213 | ], |
||||||
| 214 | 'debug' => [ |
||||||
| 215 | 'controller' => Oauth2DebugController::class, |
||||||
| 216 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER, |
||||||
| 217 | ], |
||||||
| 218 | 'pat' => [ |
||||||
| 219 | 'controller' => Oauth2PersonalAccessTokenController::class, |
||||||
| 220 | 'serverRole' => self::SERVER_ROLE_AUTHORIZATION_SERVER, |
||||||
| 221 | ] |
||||||
| 222 | ] |
||||||
| 223 | ]; |
||||||
| 224 | |||||||
| 225 | /** |
||||||
| 226 | * Offset of Bearer: in Authorization header |
||||||
| 227 | */ |
||||||
| 228 | protected const BEARER_TOKEN_OFFSET = 7; |
||||||
| 229 | |||||||
| 230 | /** |
||||||
| 231 | * @inheritdoc |
||||||
| 232 | */ |
||||||
| 233 | public $controllerNamespace = __NAMESPACE__ . '\-'; // Set explicitly via $controllerMap in `init()`. |
||||||
| 234 | |||||||
| 235 | /** |
||||||
| 236 | * @var string|null The application type. If `null` the type will be automatically detected. |
||||||
| 237 | * @see APPLICATION_TYPES |
||||||
| 238 | */ |
||||||
| 239 | public $appType = null; |
||||||
| 240 | |||||||
| 241 | /** |
||||||
| 242 | * @var int The Oauth 2.0 Server Roles the module will perform. |
||||||
| 243 | * @since 1.0.0 |
||||||
| 244 | */ |
||||||
| 245 | public $serverRole = self::SERVER_ROLE_AUTHORIZATION_SERVER | self::SERVER_ROLE_RESOURCE_SERVER; |
||||||
| 246 | |||||||
| 247 | /** |
||||||
| 248 | * @var string|null The private key for the server. Can be a string containing the key itself or point to a file. |
||||||
| 249 | * When pointing to a file it's recommended to use an absolute path prefixed with 'file://' or start with |
||||||
| 250 | * '@' to use a Yii path alias. |
||||||
| 251 | * @see $privateKeyPassphrase For setting a passphrase for the private key. |
||||||
| 252 | * @since 1.0.0 |
||||||
| 253 | */ |
||||||
| 254 | public $privateKey = null; |
||||||
| 255 | |||||||
| 256 | /** |
||||||
| 257 | * @var string|null The passphrase for the private key. |
||||||
| 258 | * @since 1.0.0 |
||||||
| 259 | */ |
||||||
| 260 | public $privateKeyPassphrase = null; |
||||||
| 261 | /** |
||||||
| 262 | * @var string|null The public key for the server. Can be a string containing the key itself or point to a file. |
||||||
| 263 | * When pointing to a file it's recommended to use an absolute path prefixed with 'file://' or start with |
||||||
| 264 | * '@' to use a Yii path alias. |
||||||
| 265 | * @since 1.0.0 |
||||||
| 266 | */ |
||||||
| 267 | public $publicKey = null; |
||||||
| 268 | |||||||
| 269 | /** |
||||||
| 270 | * @var string|null The encryption key for authorization and refresh codes. |
||||||
| 271 | * @since 1.0.0 |
||||||
| 272 | */ |
||||||
| 273 | public $codesEncryptionKey = null; |
||||||
| 274 | |||||||
| 275 | /** |
||||||
| 276 | * @var string[]|string|null The encryption keys for storage like client secrets. |
||||||
| 277 | * Where the array key is the name of the key, and the value the key itself. E.g. |
||||||
| 278 | * `['2022-01-01' => 'def00000cb36fd6ed6641e0ad70805b28d....']` |
||||||
| 279 | * If a string (instead of an array of strings) is specified it will be JSON decoded |
||||||
| 280 | * it should contain an object where each property name is the name of the key, its value the key itself. E.g. |
||||||
| 281 | * `{"2022-01-01": "def00000cb36fd6ed6641e0ad70805b28d...."}` |
||||||
| 282 | * |
||||||
| 283 | * @since 1.0.0 |
||||||
| 284 | */ |
||||||
| 285 | public $storageEncryptionKeys = null; |
||||||
| 286 | |||||||
| 287 | /** |
||||||
| 288 | * @var string|null The index of the default key in storageEncryptionKeys. E.g. 'myKey'. |
||||||
| 289 | * @since 1.0.0 |
||||||
| 290 | */ |
||||||
| 291 | public $defaultStorageEncryptionKey = null; |
||||||
| 292 | |||||||
| 293 | /** |
||||||
| 294 | * @var string|string[]|null IP addresses, CIDR ranges, or range aliases that are allowed to connect over a |
||||||
| 295 | * non-TLS connection. If `null` or an empty array LTS is always required. |
||||||
| 296 | * |
||||||
| 297 | * Warning: Although you can use '*' or 'any' to allow a non-TLS connection from any ip address, |
||||||
| 298 | * doing so would most likely introduce a security risk and should be done for debugging purposes only! |
||||||
| 299 | * |
||||||
| 300 | * @see \yii\validators\IpValidator::$networks for a list of available alliasses. |
||||||
| 301 | */ |
||||||
| 302 | public $nonTlsAllowedRanges = 'localhost'; |
||||||
| 303 | |||||||
| 304 | /** |
||||||
| 305 | * @var class-string<Oauth2UserInterface>|null The Identity Class of your application, |
||||||
|
0 ignored issues
–
show
Documentation
Bug
introduced
by
Loading history...
|
|||||||
| 306 | * most likely the same as the 'identityClass' of your application's User Component. |
||||||
| 307 | * @since 1.0.0 |
||||||
| 308 | */ |
||||||
| 309 | public $identityClass = null; |
||||||
| 310 | |||||||
| 311 | /** |
||||||
| 312 | * @var null|string Prefix used for url rules. When `null` the module's uniqueId will be used. |
||||||
| 313 | * @since 1.0.0 |
||||||
| 314 | */ |
||||||
| 315 | public $urlRulesPrefix = null; |
||||||
| 316 | |||||||
| 317 | /** |
||||||
| 318 | * @var string URL path for the access token endpoint (will be prefixed with $urlRulesPrefix). |
||||||
| 319 | * @since 1.0.0 |
||||||
| 320 | */ |
||||||
| 321 | public $authorizePath = 'authorize'; |
||||||
| 322 | |||||||
| 323 | /** |
||||||
| 324 | * @var string URL path for the access token endpoint (will be prefixed with $urlRulesPrefix). |
||||||
| 325 | * @since 1.0.0 |
||||||
| 326 | */ |
||||||
| 327 | public $accessTokenPath = 'access-token'; |
||||||
| 328 | |||||||
| 329 | /** |
||||||
| 330 | * @var string URL path for the token revocation endpoint (will be prefixed with $urlRulesPrefix). |
||||||
| 331 | * @since 1.0.0 |
||||||
| 332 | */ |
||||||
| 333 | public $tokenRevocationPath = 'revoke'; |
||||||
| 334 | |||||||
| 335 | /** |
||||||
| 336 | * @var string URL path for the certificates jwks endpoint (will be prefixed with $urlRulesPrefix). |
||||||
| 337 | * @since 1.0.0 |
||||||
| 338 | */ |
||||||
| 339 | public $jwksPath = 'certs'; |
||||||
| 340 | |||||||
| 341 | /** |
||||||
| 342 | * The URL to the page where the user can perform the Client/Scope authorization |
||||||
| 343 | * (if `null` the build in page will be used). |
||||||
| 344 | * @return string |
||||||
| 345 | * @since 1.0.0 |
||||||
| 346 | * @see $clientAuthorizationPath |
||||||
| 347 | */ |
||||||
| 348 | public $clientAuthorizationUrl = null; |
||||||
| 349 | |||||||
| 350 | /** |
||||||
| 351 | * @var string The URL path to the build in page where the user can authorize the Client for the requested Scopes |
||||||
| 352 | * (will be prefixed with $urlRulesPrefix). |
||||||
| 353 | * Note: This setting will only be used if $clientAuthorizationUrl is `null`. |
||||||
| 354 | * @since 1.0.0 |
||||||
| 355 | * @see $clientAuthorizationView |
||||||
| 356 | */ |
||||||
| 357 | public $clientAuthorizationPath = 'authorize-client'; |
||||||
| 358 | |||||||
| 359 | /** |
||||||
| 360 | * @var string The view to use in the "Client Authorization" action for the page where the user can |
||||||
| 361 | * authorize the Client for the requested Scopes. |
||||||
| 362 | * Note: This setting will only be used if $clientAuthorizationUrl is `null`. |
||||||
| 363 | * @since 1.0.0 |
||||||
| 364 | * @see $clientAuthorizationPath |
||||||
| 365 | */ |
||||||
| 366 | public $clientAuthorizationView = 'authorize-client'; |
||||||
| 367 | |||||||
| 368 | /** |
||||||
| 369 | * @var bool Allow clients to invoke token revocation (RFC 7009). |
||||||
| 370 | * @see https://datatracker.ietf.org/doc/html/rfc7009 |
||||||
| 371 | */ |
||||||
| 372 | public $enableTokenRevocation = true; |
||||||
| 373 | |||||||
| 374 | /** |
||||||
| 375 | * @var bool Will the server throw an exception when a Client requests an unknown or unauthorized scope |
||||||
| 376 | * (would be silently ignored otherwise). |
||||||
| 377 | * Note: this setting can be overwritten per client. |
||||||
| 378 | */ |
||||||
| 379 | public $exceptionOnInvalidScope = false; |
||||||
| 380 | |||||||
| 381 | /** |
||||||
| 382 | * Configuration for `Oauth2Client::getRedirectUrisEnvVarConfig()` fallback (the |
||||||
| 383 | * Oauth2Client::$envVarConfig['redirectUris'] has precedence). |
||||||
| 384 | * When configured, environment variables specified in the `Oauth2Client` redirect URI(s) will be substituted with |
||||||
| 385 | * their values. Please see `EnvironmentHelper::parseEnvVars()` for more details. |
||||||
| 386 | * |
||||||
| 387 | * Warning: This setting applies to all clients, for security it's recommended to specify this configuration at the |
||||||
| 388 | * individual client level via its `envVarConfig` setting. |
||||||
| 389 | * |
||||||
| 390 | * @var array{ |
||||||
|
0 ignored issues
–
show
|
|||||||
| 391 | * allowList: array, |
||||||
| 392 | * denyList: array|null, |
||||||
| 393 | * parseNested: bool, |
||||||
| 394 | * exceptionWhenNotSet: bool, |
||||||
| 395 | * exceptionWhenNotAllowed: bool, |
||||||
| 396 | * }|null |
||||||
| 397 | * @see Oauth2ClientInterface::setEnvVarConfig() |
||||||
| 398 | * @see Oauth2ClientInterface::getRedirectUrisEnvVarConfig() |
||||||
| 399 | * @see \rhertogh\Yii2Oauth2Server\helpers\EnvironmentHelper::parseEnvVars() |
||||||
| 400 | */ |
||||||
| 401 | public $clientRedirectUrisEnvVarConfig = null; |
||||||
| 402 | |||||||
| 403 | public $userAccountCreationUrl = null; |
||||||
|
0 ignored issues
–
show
|
|||||||
| 404 | |||||||
| 405 | /** |
||||||
| 406 | * @var string|null The URL path to the OpenID Connect Provider Configuration Information Action. |
||||||
| 407 | * If set to `null` the endpoint will be disabled. |
||||||
| 408 | * Note: This path is defined in the |
||||||
| 409 | * [OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.4) |
||||||
| 410 | * specification and should normally not be changed. |
||||||
| 411 | * @since 1.0.0 |
||||||
| 412 | */ |
||||||
| 413 | public $openIdConnectProviderConfigurationInformationPath = '.well-known/openid-configuration'; |
||||||
| 414 | |||||||
| 415 | /** |
||||||
| 416 | * @var string The URL path to the OpenID Connect Userinfo Action (will be prefixed with $urlRulesPrefix). |
||||||
| 417 | * Note: This setting will only be used if $enableOpenIdConnect and $openIdConnectUserinfoEndpoint are `true`. |
||||||
| 418 | * @since 1.0.0 |
||||||
| 419 | * @see $openIdConnectUserinfoEndpoint |
||||||
| 420 | */ |
||||||
| 421 | public $openIdConnectUserinfoPath = 'oidc/userinfo'; |
||||||
| 422 | |||||||
| 423 | /** |
||||||
| 424 | * @var string The URL path to the OpenID Connect End Session Action (will be prefixed with $urlRulesPrefix). |
||||||
| 425 | * Note: This setting will only be used if $enableOpenIdConnect and |
||||||
| 426 | * $openIdConnectRpInitiatedLogoutEndpoint are `true`. |
||||||
| 427 | * @since 1.0.0 |
||||||
| 428 | * @see $openIdConnectRpInitiatedLogoutEndpoint |
||||||
| 429 | * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html |
||||||
| 430 | */ |
||||||
| 431 | public $openIdConnectRpInitiatedLogoutPath = 'oidc/end-session'; |
||||||
| 432 | |||||||
| 433 | /** |
||||||
| 434 | * The URL to the page where the user can perform the End Session (logout) confirmation |
||||||
| 435 | * (if `null` the build in page will be used). |
||||||
| 436 | * @return string |
||||||
| 437 | * @since 1.0.0 |
||||||
| 438 | * @see $openIdConnectLogoutConfirmationPath |
||||||
| 439 | */ |
||||||
| 440 | public $openIdConnectLogoutConfirmationUrl = null; |
||||||
| 441 | |||||||
| 442 | /** |
||||||
| 443 | * @var string The URL path to the build in page where the user can confirm the End Session (logout) request |
||||||
| 444 | * (will be prefixed with $urlRulesPrefix). |
||||||
| 445 | * Note: This setting will only be used if $openIdConnectLogoutConfirmationUrl is `null`. |
||||||
| 446 | * @since 1.0.0 |
||||||
| 447 | * @see $openIdConnectLogoutConfirmationView |
||||||
| 448 | */ |
||||||
| 449 | public $openIdConnectLogoutConfirmationPath = 'confirm-logout'; |
||||||
| 450 | |||||||
| 451 | /** |
||||||
| 452 | * @var string The view to use in the "End Session Authorization" action for the page where the user can |
||||||
| 453 | * authorize the End Session (logout) request. |
||||||
| 454 | * Note: This setting will only be used if $openIdConnectLogoutConfirmationUrl is `null`. |
||||||
| 455 | * @since 1.0.0 |
||||||
| 456 | * @see $openIdConnectLogoutConfirmationPath |
||||||
| 457 | */ |
||||||
| 458 | public $openIdConnectLogoutConfirmationView = 'confirm-logout'; |
||||||
| 459 | |||||||
| 460 | |||||||
| 461 | /** |
||||||
| 462 | * @var Oauth2GrantTypeFactoryInterface[]|GrantTypeInterface[]|string[]|Oauth2GrantTypeFactoryInterface|GrantTypeInterface|string|callable |
||||||
| 463 | * The Oauth 2.0 Grant Types that the module will serve. |
||||||
| 464 | * @since 1.0.0 |
||||||
| 465 | */ |
||||||
| 466 | public $grantTypes = []; |
||||||
| 467 | |||||||
| 468 | /** |
||||||
| 469 | * @var bool Should the resource server check for revocation of the access token. |
||||||
| 470 | * @since 1.0.0 |
||||||
| 471 | */ |
||||||
| 472 | public $resourceServerAccessTokenRevocationValidation = true; |
||||||
| 473 | |||||||
| 474 | /** |
||||||
| 475 | * @var bool Enable support for OpenIdvConnect. |
||||||
| 476 | * @since 1.0.0 |
||||||
| 477 | */ |
||||||
| 478 | public $enableOpenIdConnect = false; |
||||||
| 479 | |||||||
| 480 | /** |
||||||
| 481 | * @var bool Enable the .well-known/openid-configuration discovery endpoint. |
||||||
| 482 | * @since 1.0.0 |
||||||
| 483 | */ |
||||||
| 484 | public $enableOpenIdConnectDiscovery = true; |
||||||
| 485 | |||||||
| 486 | /** |
||||||
| 487 | * @var bool include `grant_types_supported` in the OpenIdConnect Discovery. |
||||||
| 488 | * Note: Since grant types can be specified per client not all clients might support all enabled grant types. |
||||||
| 489 | * @since 1.0.0 |
||||||
| 490 | */ |
||||||
| 491 | public $openIdConnectDiscoveryIncludeSupportedGrantTypes = true; |
||||||
| 492 | |||||||
| 493 | /** |
||||||
| 494 | * @var string URL to include in the OpenID Connect Discovery Service of a page containing |
||||||
| 495 | * human-readable information that developers might want or need to know when using the OpenID Provider. |
||||||
| 496 | * @see 'service_documentation' in https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 |
||||||
| 497 | * @since 1.0.0 |
||||||
| 498 | */ |
||||||
| 499 | public $openIdConnectDiscoveryServiceDocumentationUrl = null; |
||||||
| 500 | |||||||
| 501 | /** |
||||||
| 502 | * @var string|bool A string to a custom userinfo endpoint or `true` to enable the build in endpoint. |
||||||
| 503 | * @since 1.0.0 |
||||||
| 504 | * @see $openIdConnectUserinfoPath |
||||||
| 505 | */ |
||||||
| 506 | public $openIdConnectUserinfoEndpoint = true; |
||||||
| 507 | |||||||
| 508 | /** |
||||||
| 509 | * @var string|bool A string to a custom logout endpoint or `true` to enable the build in endpoint. |
||||||
| 510 | * @since 1.0.0 |
||||||
| 511 | * @see $openIdConnectRpInitiatedLogoutPath |
||||||
| 512 | * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html |
||||||
| 513 | */ |
||||||
| 514 | public $openIdConnectRpInitiatedLogoutEndpoint = false; |
||||||
| 515 | |||||||
| 516 | /** |
||||||
| 517 | * @var bool Allow access to the "end session" endpoint without user authentication (in the form of the |
||||||
| 518 | * `id_token_hint` parameter). If enabled the "end session" endpoint will always prompt the user to verify the |
||||||
| 519 | * logout if no `id_token_hint` is provided and no redirect after logout will be performed. |
||||||
| 520 | * Note: If disabled the client's `oidc_rp_initiated_logout` will be used |
||||||
| 521 | * to determine whether to prompt the end-user for logout validation. |
||||||
| 522 | * @since 1.0.0 |
||||||
| 523 | * @see $openIdConnectRpInitiatedLogoutPath |
||||||
| 524 | * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html |
||||||
| 525 | */ |
||||||
| 526 | public $openIdConnectAllowAnonymousRpInitiatedLogout = false; |
||||||
| 527 | |||||||
| 528 | /** |
||||||
| 529 | * Warning! Enabling this setting might introduce privacy concerns since the client could poll for the |
||||||
| 530 | * online status of a user. |
||||||
| 531 | * |
||||||
| 532 | * @var bool If this setting is disabled in case of OpenID Connect Context the Access Token won't include a |
||||||
| 533 | * Refresh Token when the 'offline_access' scope is not included in the authorization request. |
||||||
| 534 | * In some cases it might be needed to always include a Refresh Token, in that case enable this setting and |
||||||
| 535 | * implement the `Oauth2OidcUserSessionStatusInterface` on the User Identity model. |
||||||
| 536 | * @since 1.0.0 |
||||||
| 537 | */ |
||||||
| 538 | public $openIdConnectIssueRefreshTokenWithoutOfflineAccessScope = false; |
||||||
| 539 | |||||||
| 540 | /** |
||||||
| 541 | * @var int The default option for "User Account Selection' when not specified for a client. |
||||||
| 542 | * @since 1.0.0 |
||||||
| 543 | */ |
||||||
| 544 | public $defaultUserAccountSelection = self::USER_ACCOUNT_SELECTION_DISABLED; |
||||||
| 545 | |||||||
| 546 | /** |
||||||
| 547 | * @var bool|null Display exception messages that might leak server details. This could be useful for debugging. |
||||||
| 548 | * In case of `null` (default) the YII_DEBUG constant will be used. |
||||||
| 549 | * Warning: Should NOT be enabled in production! |
||||||
| 550 | * @since 1.0.0 |
||||||
| 551 | */ |
||||||
| 552 | public $displayConfidentialExceptionMessages = null; |
||||||
| 553 | |||||||
| 554 | /** |
||||||
| 555 | * @var string|null The namespace with which migrations will be created (and by which they will be located). |
||||||
| 556 | * Note: The specified namespace must be defined as a Yii alias (e.g. '@app'). |
||||||
| 557 | * @since 1.0.0 |
||||||
| 558 | */ |
||||||
| 559 | public $migrationsNamespace = null; |
||||||
| 560 | /** |
||||||
| 561 | * @var string|null Optional prefix used in the name of generated migrations |
||||||
| 562 | * @since 1.0.0 |
||||||
| 563 | */ |
||||||
| 564 | public $migrationsPrefix = null; |
||||||
| 565 | /** |
||||||
| 566 | * @var string|array|int|null Sets the file ownership of generated migrations |
||||||
| 567 | * @see \yii\helpers\BaseFileHelper::changeOwnership() |
||||||
| 568 | * @since 1.0.0 |
||||||
| 569 | */ |
||||||
| 570 | public $migrationsFileOwnership = null; |
||||||
| 571 | /** |
||||||
| 572 | * @var int|null Sets the file mode of generated migrations |
||||||
| 573 | * @see \yii\helpers\BaseFileHelper::changeOwnership() |
||||||
| 574 | * @since 1.0.0 |
||||||
| 575 | */ |
||||||
| 576 | public $migrationsFileMode = null; |
||||||
| 577 | |||||||
| 578 | /** |
||||||
| 579 | * The log level for HTTP Client Errors (HTTP status code 400 - 499). Can be one of the following: |
||||||
| 580 | * - A log level of `\yii\log\Logger` => LEVEL_ERROR, LEVEL_WARNING, LEVEL_INFO, LEVEL_TRACE. |
||||||
| 581 | * - `0` => disable logging for HTTP Client Errors |
||||||
| 582 | * - null => The `YII_DEBUG` constant will be used to determine the log level. |
||||||
| 583 | * If `true` LEVEL_ERROR will be used, LEVEL_INFO otherwise. |
||||||
| 584 | * @var int|null |
||||||
| 585 | * @see \yii\log\Logger |
||||||
| 586 | */ |
||||||
| 587 | public $httpClientErrorsLogLevel = null; |
||||||
| 588 | |||||||
| 589 | /** |
||||||
| 590 | * @var Oauth2AuthorizationServerInterface|null Cache for the authorization server |
||||||
| 591 | * @since 1.0.0 |
||||||
| 592 | */ |
||||||
| 593 | protected $_authorizationServer = null; |
||||||
| 594 | |||||||
| 595 | /** |
||||||
| 596 | * @var Oauth2ResourceServerInterface|null Cache for the resource server |
||||||
| 597 | * @since 1.0.0 |
||||||
| 598 | */ |
||||||
| 599 | protected $_resourceServer = null; |
||||||
| 600 | |||||||
| 601 | /** |
||||||
| 602 | * @var Oauth2CryptographerInterface|null Cache for the Oauth2Cryptographer |
||||||
| 603 | * @since 1.0.0 |
||||||
| 604 | */ |
||||||
| 605 | protected $_cryptographer = null; |
||||||
| 606 | |||||||
| 607 | /** |
||||||
| 608 | * @var string|null The authorization header used when the authorization request was validated. |
||||||
| 609 | * @since 1.0.0 |
||||||
| 610 | */ |
||||||
| 611 | protected $_oauthClaimsAuthorizationHeader = null; |
||||||
| 612 | |||||||
| 613 | /** |
||||||
| 614 | * @inheritDoc |
||||||
| 615 | * @throws InvalidConfigException |
||||||
| 616 | */ |
||||||
| 617 | 161 | public function init() |
|||||
| 618 | { |
||||||
| 619 | 161 | parent::init(); |
|||||
| 620 | |||||||
| 621 | 161 | $app = Yii::$app; |
|||||
| 622 | |||||||
| 623 | 161 | if ($app instanceof WebApplication || $this->appType == static::APPLICATION_TYPE_WEB) { |
|||||
| 624 | 31 | $controllerMap = static::CONTROLLER_MAP[static::APPLICATION_TYPE_WEB]; |
|||||
| 625 | 161 | } elseif ($app instanceof ConsoleApplication || $this->appType == static::APPLICATION_TYPE_CONSOLE) { |
|||||
|
0 ignored issues
–
show
|
|||||||
| 626 | 161 | $controllerMap = static::CONTROLLER_MAP[static::APPLICATION_TYPE_CONSOLE]; |
|||||
| 627 | 161 | $this->defaultRoute = 'debug'; |
|||||
| 628 | } else { |
||||||
| 629 | 1 | throw new InvalidConfigException( |
|||||
| 630 | 1 | 'Unable to detect application type, configure it manually by setting `$appType`.' |
|||||
| 631 | 1 | ); |
|||||
| 632 | } |
||||||
| 633 | 161 | $controllerMap = array_filter( |
|||||
| 634 | 161 | $controllerMap, |
|||||
| 635 | 161 | fn($controllerSettings) => $controllerSettings['serverRole'] & $this->serverRole |
|||||
| 636 | 161 | ); |
|||||
| 637 | 161 | $this->controllerMap = ArrayHelper::getColumn($controllerMap, 'controller'); |
|||||
| 638 | |||||||
| 639 | 161 | if (empty($this->identityClass)) { |
|||||
| 640 | 1 | throw new InvalidConfigException('$identityClass must be set.'); |
|||||
| 641 | 161 | } elseif (!is_a($this->identityClass, Oauth2UserInterface::class, true)) { |
|||||
| 642 | 1 | throw new InvalidConfigException( |
|||||
| 643 | 1 | $this->identityClass . ' must implement ' . Oauth2UserInterface::class |
|||||
| 644 | 1 | ); |
|||||
| 645 | } |
||||||
| 646 | |||||||
| 647 | 161 | foreach (static::DEFAULT_INTERFACE_IMPLEMENTATIONS as $interface => $implementation) { |
|||||
| 648 | 161 | if (!Yii::$container->has($interface)) { |
|||||
| 649 | 161 | Yii::$container->set($interface, $implementation); |
|||||
| 650 | } |
||||||
| 651 | } |
||||||
| 652 | |||||||
| 653 | 161 | if (empty($this->urlRulesPrefix)) { |
|||||
| 654 | 161 | $this->urlRulesPrefix = $this->uniqueId; |
|||||
| 655 | } |
||||||
| 656 | |||||||
| 657 | 161 | $this->registerTranslations(); |
|||||
| 658 | } |
||||||
| 659 | |||||||
| 660 | /** |
||||||
| 661 | * @inheritdoc |
||||||
| 662 | * @throws InvalidConfigException |
||||||
| 663 | */ |
||||||
| 664 | 161 | public function bootstrap($app) |
|||||
| 665 | { |
||||||
| 666 | if ( |
||||||
| 667 | 161 | $app instanceof WebApplication |
|||||
| 668 | 161 | && $this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER |
|||||
| 669 | ) { |
||||||
| 670 | 31 | $rules = [ |
|||||
| 671 | 31 | $this->accessTokenPath => Oauth2ServerControllerInterface::CONTROLLER_NAME |
|||||
| 672 | 31 | . '/' . Oauth2ServerControllerInterface::ACTION_NAME_ACCESS_TOKEN, |
|||||
| 673 | 31 | $this->authorizePath => Oauth2ServerControllerInterface::CONTROLLER_NAME |
|||||
| 674 | 31 | . '/' . Oauth2ServerControllerInterface::ACTION_NAME_AUTHORIZE, |
|||||
| 675 | 31 | $this->jwksPath => Oauth2CertificatesControllerInterface::CONTROLLER_NAME |
|||||
| 676 | 31 | . '/' . Oauth2CertificatesControllerInterface::ACTION_NAME_JWKS, |
|||||
| 677 | 31 | ]; |
|||||
| 678 | |||||||
| 679 | 31 | if ($this->enableTokenRevocation) { |
|||||
| 680 | 31 | $rules[$this->tokenRevocationPath] = Oauth2ServerControllerInterface::CONTROLLER_NAME |
|||||
| 681 | 31 | . '/' . Oauth2ServerControllerInterface::ACTION_NAME_REVOKE; |
|||||
| 682 | } |
||||||
| 683 | |||||||
| 684 | 31 | if (empty($this->clientAuthorizationUrl)) { |
|||||
| 685 | 30 | $rules[$this->clientAuthorizationPath] = Oauth2ConsentControllerInterface::CONTROLLER_NAME |
|||||
| 686 | 30 | . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_CLIENT; |
|||||
| 687 | } |
||||||
| 688 | |||||||
| 689 | 31 | if ($this->enableOpenIdConnect && $this->openIdConnectUserinfoEndpoint === true) { |
|||||
| 690 | 31 | $rules[$this->openIdConnectUserinfoPath] = |
|||||
| 691 | 31 | Oauth2OidcControllerInterface::CONTROLLER_NAME |
|||||
| 692 | 31 | . '/' . Oauth2OidcControllerInterface::ACTION_NAME_USERINFO; |
|||||
| 693 | } |
||||||
| 694 | |||||||
| 695 | 31 | if ($this->enableOpenIdConnect && $this->openIdConnectRpInitiatedLogoutEndpoint === true) { |
|||||
| 696 | 31 | $rules[$this->openIdConnectRpInitiatedLogoutPath] = |
|||||
| 697 | 31 | Oauth2OidcControllerInterface::CONTROLLER_NAME |
|||||
| 698 | 31 | . '/' . Oauth2OidcControllerInterface::ACTION_END_SESSION; |
|||||
| 699 | |||||||
| 700 | 31 | if (empty($this->openIdConnectLogoutConfirmationUrl)) { |
|||||
| 701 | 31 | $rules[$this->openIdConnectLogoutConfirmationPath] = |
|||||
| 702 | 31 | Oauth2ConsentControllerInterface::CONTROLLER_NAME |
|||||
| 703 | 31 | . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_END_SESSION; |
|||||
| 704 | } |
||||||
| 705 | } |
||||||
| 706 | |||||||
| 707 | 31 | $urlManager = $app->getUrlManager(); |
|||||
| 708 | 31 | $urlManager->addRules([ |
|||||
| 709 | 31 | Yii::createObject([ |
|||||
| 710 | 31 | 'class' => GroupUrlRule::class, |
|||||
| 711 | 31 | 'prefix' => $this->urlRulesPrefix, |
|||||
| 712 | 31 | 'routePrefix' => $this->id, |
|||||
| 713 | 31 | 'rules' => $rules, |
|||||
| 714 | 31 | ]), |
|||||
| 715 | 31 | ]); |
|||||
| 716 | |||||||
| 717 | if ( |
||||||
| 718 | 31 | $this->enableOpenIdConnect |
|||||
| 719 | 31 | && $this->enableOpenIdConnectDiscovery |
|||||
| 720 | 31 | && $this->openIdConnectProviderConfigurationInformationPath |
|||||
| 721 | ) { |
||||||
| 722 | 31 | $urlManager->addRules([ |
|||||
| 723 | 31 | Yii::createObject([ |
|||||
| 724 | 31 | 'class' => UrlRule::class, |
|||||
| 725 | 31 | 'pattern' => $this->openIdConnectProviderConfigurationInformationPath, |
|||||
| 726 | 31 | 'route' => $this->id |
|||||
| 727 | 31 | . '/' . Oauth2WellKnownControllerInterface::CONTROLLER_NAME |
|||||
| 728 | 31 | . '/' . Oauth2WellKnownControllerInterface::ACTION_NAME_OPENID_CONFIGURATION, |
|||||
| 729 | 31 | ]), |
|||||
| 730 | 31 | ]); |
|||||
| 731 | } |
||||||
| 732 | } |
||||||
| 733 | } |
||||||
| 734 | |||||||
| 735 | /** |
||||||
| 736 | * Registers the translations for the module |
||||||
| 737 | * @param bool $force Force the setting of the translations (even if they are already defined). |
||||||
| 738 | * @since 1.0.0 |
||||||
| 739 | */ |
||||||
| 740 | 161 | public function registerTranslations($force = false) |
|||||
| 741 | { |
||||||
| 742 | 161 | if ($force || !array_key_exists('oauth2', Yii::$app->i18n->translations)) { |
|||||
| 743 | 161 | Yii::$app->i18n->translations['oauth2'] = [ |
|||||
| 744 | 161 | 'class' => PhpMessageSource::class, |
|||||
| 745 | 161 | 'sourceLanguage' => 'en-US', |
|||||
| 746 | 161 | 'basePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages', |
|||||
| 747 | 161 | 'fileMap' => [ |
|||||
| 748 | 161 | 'oauth2' => 'oauth2.php', |
|||||
| 749 | 161 | ], |
|||||
| 750 | 161 | ]; |
|||||
| 751 | } |
||||||
| 752 | } |
||||||
| 753 | |||||||
| 754 | /** |
||||||
| 755 | * @param string $identifier The client identifier |
||||||
| 756 | * @param string $name The (user-friendly) name of the client |
||||||
| 757 | * @param int $grantTypes The grant types enabled for this client. |
||||||
| 758 | * Use bitwise `OR` to combine multiple types, |
||||||
| 759 | * e.g. `Oauth2Module::GRANT_TYPE_AUTH_CODE | Oauth2Module::GRANT_TYPE_REFRESH_TOKEN` |
||||||
| 760 | * @param string|string[] $redirectURIs One or multiple redirect URIs for the client |
||||||
| 761 | * @param int $type The client type (e.g. Confidential or Public) |
||||||
| 762 | * See `\rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientInterface::TYPES` for possible values |
||||||
| 763 | * @param string|null $secret The client secret in case the client `type` is `confidential`. |
||||||
| 764 | * @param string|string[]|array[]|Oauth2ScopeInterface[]|null $scopes |
||||||
| 765 | * @param int|null $userId |
||||||
| 766 | * @return Oauth2ClientInterface |
||||||
| 767 | * @throws InvalidConfigException |
||||||
| 768 | * @throws \yii\db\Exception |
||||||
| 769 | */ |
||||||
| 770 | 5 | public function createClient( |
|||||
| 771 | $identifier, |
||||||
| 772 | $name, |
||||||
| 773 | $grantTypes, |
||||||
| 774 | $redirectURIs, |
||||||
| 775 | $type, |
||||||
| 776 | $secret = null, |
||||||
| 777 | $scopes = null, |
||||||
| 778 | $userId = null, |
||||||
| 779 | $endUsersMayAuthorizeClient = null, |
||||||
| 780 | $skipAuthorizationIfScopeIsAllowed = null |
||||||
| 781 | ) { |
||||||
| 782 | 5 | if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) { |
|||||
| 783 | 1 | throw new InvalidCallException('Oauth2 server role does not include authorization server.'); |
|||||
| 784 | } |
||||||
| 785 | |||||||
| 786 | /** @var Oauth2ClientInterface $client */ |
||||||
| 787 | 4 | $client = Yii::createObject([ |
|||||
| 788 | 4 | 'class' => Oauth2ClientInterface::class, |
|||||
| 789 | 4 | 'identifier' => $identifier, |
|||||
| 790 | 4 | 'type' => $type, |
|||||
| 791 | 4 | 'name' => $name, |
|||||
| 792 | 4 | 'redirectUri' => $redirectURIs, |
|||||
| 793 | 4 | 'grantTypes' => $grantTypes, |
|||||
| 794 | 4 | 'endUsersMayAuthorizeClient' => $endUsersMayAuthorizeClient, |
|||||
| 795 | 4 | 'skip_authorization_if_scope_is_allowed' => $skipAuthorizationIfScopeIsAllowed, |
|||||
| 796 | 4 | 'clientCredentialsGrantUserId' => $userId |
|||||
| 797 | 4 | ]); |
|||||
| 798 | |||||||
| 799 | 4 | $transaction = $client::getDb()->beginTransaction(); |
|||||
| 800 | |||||||
| 801 | try { |
||||||
| 802 | 4 | if ($type == Oauth2ClientInterface::TYPE_CONFIDENTIAL) { |
|||||
| 803 | 4 | $client->setSecret($secret, $this->getCryptographer()); |
|||||
| 804 | } |
||||||
| 805 | |||||||
| 806 | 3 | $client |
|||||
| 807 | 3 | ->persist() |
|||||
| 808 | 3 | ->syncClientScopes($scopes, $this->getScopeRepository()); |
|||||
| 809 | |||||||
| 810 | 3 | $transaction->commit(); |
|||||
| 811 | 1 | } catch (\Exception $e) { |
|||||
| 812 | 1 | $transaction->rollBack(); |
|||||
| 813 | 1 | throw $e; |
|||||
| 814 | } |
||||||
| 815 | |||||||
| 816 | 3 | return $client; |
|||||
| 817 | } |
||||||
| 818 | |||||||
| 819 | /** |
||||||
| 820 | * @return CryptKey The private key of the server. |
||||||
| 821 | * @throws InvalidConfigException |
||||||
| 822 | * @since 1.0.0 |
||||||
| 823 | */ |
||||||
| 824 | 22 | public function getPrivateKey() |
|||||
| 825 | { |
||||||
| 826 | 22 | $privateKey = $this->privateKey; |
|||||
| 827 | 22 | if (StringHelper::startsWith($privateKey, '@')) { |
|||||
| 828 | 19 | $privateKey = 'file://' . Yii::getAlias($privateKey); |
|||||
|
0 ignored issues
–
show
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
Loading history...
|
|||||||
| 829 | } |
||||||
| 830 | 22 | return Yii::createObject(CryptKey::class, [$privateKey, $this->privateKeyPassphrase]); |
|||||
| 831 | } |
||||||
| 832 | |||||||
| 833 | /** |
||||||
| 834 | * @return CryptKey The public key of the server. |
||||||
| 835 | * @throws InvalidConfigException |
||||||
| 836 | * @since 1.0.0 |
||||||
| 837 | */ |
||||||
| 838 | 9 | public function getPublicKey() |
|||||
| 839 | { |
||||||
| 840 | 9 | $publicKey = $this->publicKey; |
|||||
| 841 | 9 | if (StringHelper::startsWith($publicKey, '@')) { |
|||||
| 842 | 6 | $publicKey = 'file://' . Yii::getAlias($publicKey); |
|||||
|
0 ignored issues
–
show
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
Loading history...
|
|||||||
| 843 | } |
||||||
| 844 | 9 | return Yii::createObject(CryptKey::class, [$publicKey]); |
|||||
| 845 | } |
||||||
| 846 | |||||||
| 847 | /** |
||||||
| 848 | * @return Oauth2AuthorizationServerInterface The authorization server. |
||||||
| 849 | * @throws InvalidConfigException |
||||||
| 850 | * @since 1.0.0 |
||||||
| 851 | */ |
||||||
| 852 | 27 | public function getAuthorizationServer() |
|||||
| 853 | { |
||||||
| 854 | 27 | if (!($this->serverRole & static::SERVER_ROLE_AUTHORIZATION_SERVER)) { |
|||||
| 855 | 1 | throw new InvalidCallException('Oauth2 server role does not include authorization server.'); |
|||||
| 856 | } |
||||||
| 857 | |||||||
| 858 | 26 | if (!$this->_authorizationServer) { |
|||||
| 859 | 26 | $this->ensureProperties(static::REQUIRED_SETTINGS_AUTHORIZATION_SERVER); |
|||||
| 860 | |||||||
| 861 | 21 | if (!$this->getCryptographer()->hasKey($this->defaultStorageEncryptionKey)) { |
|||||
| 862 | 1 | throw new InvalidConfigException( |
|||||
| 863 | 1 | 'Key "' . $this->defaultStorageEncryptionKey . '" is not set in $storageEncryptionKeys' |
|||||
| 864 | 1 | ); |
|||||
| 865 | } |
||||||
| 866 | |||||||
| 867 | /** @var Oauth2EncryptionKeyFactoryInterface $keyFactory */ |
||||||
| 868 | 19 | $keyFactory = Yii::createObject(Oauth2EncryptionKeyFactoryInterface::class); |
|||||
| 869 | try { |
||||||
| 870 | 19 | $codesEncryptionKey = $keyFactory->createFromAsciiSafeString($this->codesEncryptionKey); |
|||||
| 871 | 1 | } catch (BadFormatException $e) { |
|||||
| 872 | 1 | throw new InvalidConfigException( |
|||||
| 873 | 1 | '$codesEncryptionKey is malformed: ' . $e->getMessage(), |
|||||
| 874 | 1 | 0, |
|||||
| 875 | 1 | $e |
|||||
| 876 | 1 | ); |
|||||
| 877 | } catch (EnvironmentIsBrokenException $e) { |
||||||
| 878 | throw new InvalidConfigException( |
||||||
| 879 | 'Could not instantiate $codesEncryptionKey: ' . $e->getMessage(), |
||||||
| 880 | 0, |
||||||
| 881 | $e |
||||||
| 882 | ); |
||||||
| 883 | } |
||||||
| 884 | |||||||
| 885 | 18 | if ($this->enableOpenIdConnect) { |
|||||
| 886 | 18 | $responseTypeClass = Oauth2OidcBearerTokenResponseInterface::class; |
|||||
| 887 | } else { |
||||||
| 888 | $responseTypeClass = Oauth2BearerTokenResponseInterface::class; |
||||||
| 889 | } |
||||||
| 890 | 18 | $responseType = Yii::createObject($responseTypeClass, [ |
|||||
| 891 | 18 | $this, |
|||||
| 892 | 18 | ]); |
|||||
| 893 | |||||||
| 894 | 18 | $this->_authorizationServer = Yii::createObject(Oauth2AuthorizationServerInterface::class, [ |
|||||
| 895 | 18 | $this->getClientRepository(), |
|||||
| 896 | 18 | $this->getAccessTokenRepository(), |
|||||
| 897 | 18 | $this->getScopeRepository(), |
|||||
| 898 | 18 | $this->getPrivateKey(), |
|||||
| 899 | 18 | $codesEncryptionKey, |
|||||
| 900 | 18 | $responseType |
|||||
| 901 | 18 | ]); |
|||||
| 902 | |||||||
| 903 | 18 | if (!empty($this->grantTypes)) { |
|||||
| 904 | 18 | $grantTypes = $this->grantTypes; |
|||||
| 905 | |||||||
| 906 | 18 | if (is_callable($grantTypes)) { |
|||||
| 907 | 1 | call_user_func($grantTypes, $this->_authorizationServer, $this); |
|||||
|
0 ignored issues
–
show
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
Loading history...
|
|||||||
| 908 | } else { |
||||||
| 909 | 17 | if (!is_array($grantTypes)) { |
|||||
| 910 | 2 | $grantTypes = [$grantTypes]; |
|||||
| 911 | } |
||||||
| 912 | |||||||
| 913 | 17 | foreach ($grantTypes as $grantTypeDefinition) { |
|||||
| 914 | 17 | if ($grantTypeDefinition instanceof GrantTypeInterface) { |
|||||
| 915 | 1 | $accessTokenTTL = $this->getDefaultAccessTokenTTL(); |
|||||
| 916 | 1 | $this->_authorizationServer->enableGrantType($grantTypeDefinition, $accessTokenTTL); |
|||||
| 917 | } elseif ( |
||||||
| 918 | ( |
||||||
| 919 | 16 | is_numeric($grantTypeDefinition) |
|||||
| 920 | 16 | && array_key_exists($grantTypeDefinition, static::DEFAULT_GRANT_TYPE_FACTORIES) |
|||||
| 921 | ) |
||||||
| 922 | 16 | || is_a($grantTypeDefinition, Oauth2GrantTypeFactoryInterface::class, true) |
|||||
| 923 | ) { |
||||||
| 924 | if ( |
||||||
| 925 | 15 | is_numeric($grantTypeDefinition) |
|||||
| 926 | 15 | && array_key_exists($grantTypeDefinition, static::DEFAULT_GRANT_TYPE_FACTORIES) |
|||||
| 927 | ) { |
||||||
| 928 | 15 | $grantTypeDefinition = static::DEFAULT_GRANT_TYPE_FACTORIES[$grantTypeDefinition]; |
|||||
| 929 | } |
||||||
| 930 | |||||||
| 931 | /** @var Oauth2GrantTypeFactoryInterface $factory */ |
||||||
| 932 | 15 | $factory = Yii::createObject([ |
|||||
| 933 | 15 | 'class' => $grantTypeDefinition, |
|||||
| 934 | 15 | 'module' => $this, |
|||||
| 935 | 15 | ]); |
|||||
| 936 | 15 | $accessTokenTTL = $factory->getDefaultAccessTokenTTL() |
|||||
| 937 | 15 | ?? $this->getDefaultAccessTokenTTL(); |
|||||
| 938 | 15 | $this->_authorizationServer->enableGrantType($factory->getGrantType(), $accessTokenTTL); |
|||||
| 939 | } else { |
||||||
| 940 | 1 | throw new InvalidConfigException( |
|||||
| 941 | 1 | 'Unknown grantType ' |
|||||
| 942 | 1 | . ( |
|||||
| 943 | 1 | is_scalar($grantTypeDefinition) |
|||||
| 944 | 1 | ? '"' . $grantTypeDefinition . '".' |
|||||
| 945 | 1 | : 'with data type ' . gettype($grantTypeDefinition) |
|||||
| 946 | 1 | ) |
|||||
| 947 | 1 | ); |
|||||
| 948 | } |
||||||
| 949 | } |
||||||
| 950 | } |
||||||
| 951 | } |
||||||
| 952 | } |
||||||
| 953 | |||||||
| 954 | 17 | return $this->_authorizationServer; |
|||||
| 955 | } |
||||||
| 956 | |||||||
| 957 | /** |
||||||
| 958 | * @inheritDoc |
||||||
| 959 | * @throws InvalidConfigException |
||||||
| 960 | */ |
||||||
| 961 | 6 | public function getOidcScopeCollection() |
|||||
| 962 | { |
||||||
| 963 | 6 | if ($this->_oidcScopeCollection === null) { |
|||||
| 964 | 6 | $openIdConnectScopes = $this->getOpenIdConnectScopes(); |
|||||
| 965 | 6 | if ($openIdConnectScopes instanceof Oauth2OidcScopeCollectionInterface) { |
|||||
| 966 | 1 | $this->_oidcScopeCollection = $openIdConnectScopes; |
|||||
| 967 | 5 | } elseif (is_callable($openIdConnectScopes)) { |
|||||
| 968 | 1 | $this->_oidcScopeCollection = call_user_func($openIdConnectScopes, $this); |
|||||
| 969 | 1 | if (!($this->_oidcScopeCollection instanceof Oauth2OidcScopeCollectionInterface)) { |
|||||
| 970 | 1 | throw new InvalidConfigException( |
|||||
| 971 | 1 | '$openIdConnectScopes must return an instance of ' |
|||||
| 972 | 1 | . Oauth2OidcScopeCollectionInterface::class |
|||||
| 973 | 1 | ); |
|||||
| 974 | } |
||||||
| 975 | 4 | } elseif (is_array($openIdConnectScopes) || is_string($openIdConnectScopes)) { |
|||||
| 976 | 3 | $this->_oidcScopeCollection = Yii::createObject([ |
|||||
| 977 | 3 | 'class' => Oauth2OidcScopeCollectionInterface::class, |
|||||
| 978 | 3 | 'oidcScopes' => (array)$openIdConnectScopes, |
|||||
| 979 | 3 | ]); |
|||||
| 980 | } else { |
||||||
| 981 | 1 | throw new InvalidConfigException( |
|||||
| 982 | 1 | '$openIdConnectScopes must be a callable, array, string or ' |
|||||
| 983 | 1 | . Oauth2OidcScopeCollectionInterface::class |
|||||
| 984 | 1 | ); |
|||||
| 985 | } |
||||||
| 986 | } |
||||||
| 987 | |||||||
| 988 | 5 | return $this->_oidcScopeCollection; |
|||||
| 989 | } |
||||||
| 990 | |||||||
| 991 | /** |
||||||
| 992 | * @return Oauth2ResourceServerInterface The resource server. |
||||||
| 993 | * @throws InvalidConfigException |
||||||
| 994 | * @since 1.0.0 |
||||||
| 995 | */ |
||||||
| 996 | 7 | public function getResourceServer() |
|||||
| 997 | { |
||||||
| 998 | 7 | if (!($this->serverRole & static::SERVER_ROLE_RESOURCE_SERVER)) { |
|||||
| 999 | 1 | throw new InvalidCallException('Oauth2 server role does not include resource server.'); |
|||||
| 1000 | } |
||||||
| 1001 | |||||||
| 1002 | 6 | if (!$this->_resourceServer) { |
|||||
| 1003 | 6 | $this->ensureProperties(static::REQUIRED_SETTINGS_RESOURCE_SERVER); |
|||||
| 1004 | |||||||
| 1005 | 5 | $accessTokenRepository = $this->getAccessTokenRepository() |
|||||
| 1006 | 5 | ->setRevocationValidation($this->resourceServerAccessTokenRevocationValidation); |
|||||
| 1007 | |||||||
| 1008 | 5 | $this->_resourceServer = Yii::createObject(Oauth2ResourceServerInterface::class, [ |
|||||
| 1009 | 5 | $accessTokenRepository, |
|||||
| 1010 | 5 | $this->getPublicKey(), |
|||||
| 1011 | 5 | ]); |
|||||
| 1012 | } |
||||||
| 1013 | |||||||
| 1014 | 5 | return $this->_resourceServer; |
|||||
| 1015 | } |
||||||
| 1016 | |||||||
| 1017 | /** |
||||||
| 1018 | * @return Oauth2CryptographerInterface The data cryptographer for the module. |
||||||
| 1019 | * @throws InvalidConfigException |
||||||
| 1020 | * @since 1.0.0 |
||||||
| 1021 | */ |
||||||
| 1022 | 27 | public function getCryptographer() |
|||||
| 1023 | { |
||||||
| 1024 | 27 | if (!$this->_cryptographer) { |
|||||
| 1025 | 27 | $this->_cryptographer = Yii::createObject([ |
|||||
| 1026 | 27 | 'class' => Oauth2CryptographerInterface::class, |
|||||
| 1027 | 27 | 'keys' => $this->storageEncryptionKeys, |
|||||
| 1028 | 27 | 'defaultKeyName' => $this->defaultStorageEncryptionKey, |
|||||
| 1029 | 27 | ]); |
|||||
| 1030 | } |
||||||
| 1031 | |||||||
| 1032 | 26 | return $this->_cryptographer; |
|||||
| 1033 | } |
||||||
| 1034 | |||||||
| 1035 | /** |
||||||
| 1036 | * @param string|null $newKeyName |
||||||
| 1037 | * @return array |
||||||
| 1038 | * @throws InvalidConfigException |
||||||
| 1039 | */ |
||||||
| 1040 | 1 | public function rotateStorageEncryptionKeys($newKeyName = null) |
|||||
| 1041 | { |
||||||
| 1042 | 1 | $cryptographer = $this->getCryptographer(); |
|||||
| 1043 | |||||||
| 1044 | 1 | $result = []; |
|||||
| 1045 | 1 | foreach (static::ENCRYPTED_MODELS as $modelInterface) { |
|||||
| 1046 | 1 | $modelClass = DiHelper::getValidatedClassName($modelInterface); |
|||||
| 1047 | 1 | if (!is_a($modelClass, Oauth2EncryptedStorageInterface::class, true)) { |
|||||
| 1048 | throw new InvalidConfigException($modelInterface . ' must implement ' |
||||||
| 1049 | . Oauth2EncryptedStorageInterface::class); |
||||||
| 1050 | } |
||||||
| 1051 | 1 | $result[$modelClass] = $modelClass::rotateStorageEncryptionKeys($cryptographer, $newKeyName); |
|||||
| 1052 | } |
||||||
| 1053 | |||||||
| 1054 | 1 | return $result; |
|||||
| 1055 | } |
||||||
| 1056 | |||||||
| 1057 | /** |
||||||
| 1058 | * Checks if the connection is using TLS or if the remote IP address is allowed to connect without TLS. |
||||||
| 1059 | * @return bool |
||||||
| 1060 | */ |
||||||
| 1061 | 12 | public function validateTlsConnection() |
|||||
| 1062 | { |
||||||
| 1063 | 12 | if (Yii::$app->request->getIsSecureConnection()) { |
|||||
| 1064 | 1 | return true; |
|||||
| 1065 | } |
||||||
| 1066 | |||||||
| 1067 | if ( |
||||||
| 1068 | 11 | !empty($this->nonTlsAllowedRanges) |
|||||
| 1069 | 11 | && (new IpValidator(['ranges' => $this->nonTlsAllowedRanges]))->validate(Yii::$app->request->getRemoteIP()) |
|||||
| 1070 | ) { |
||||||
| 1071 | 7 | return true; |
|||||
| 1072 | } |
||||||
| 1073 | |||||||
| 1074 | 4 | return false; |
|||||
| 1075 | } |
||||||
| 1076 | |||||||
| 1077 | /** |
||||||
| 1078 | * @return array |
||||||
| 1079 | * @throws InvalidConfigException |
||||||
| 1080 | */ |
||||||
| 1081 | public function getStorageEncryptionKeyUsage() |
||||||
| 1082 | { |
||||||
| 1083 | $cryptographer = $this->getCryptographer(); |
||||||
| 1084 | |||||||
| 1085 | $result = []; |
||||||
| 1086 | foreach (static::ENCRYPTED_MODELS as $modelInterface) { |
||||||
| 1087 | $modelClass = DiHelper::getValidatedClassName($modelInterface); |
||||||
| 1088 | if (!is_a($modelClass, Oauth2EncryptedStorageInterface::class, true)) { |
||||||
| 1089 | throw new InvalidConfigException($modelInterface . ' must implement ' |
||||||
| 1090 | . Oauth2EncryptedStorageInterface::class); |
||||||
| 1091 | } |
||||||
| 1092 | |||||||
| 1093 | $result[$modelClass] = $modelClass::getUsedStorageEncryptionKeys($cryptographer); |
||||||
| 1094 | } |
||||||
| 1095 | |||||||
| 1096 | return $result; |
||||||
| 1097 | } |
||||||
| 1098 | |||||||
| 1099 | /** |
||||||
| 1100 | * @param Oauth2ClientInterface $client |
||||||
| 1101 | * @param string[] $requestedScopeIdentifiers |
||||||
| 1102 | * @throws Oauth2ServerException |
||||||
| 1103 | */ |
||||||
| 1104 | 6 | public function validateAuthRequestScopes($client, $requestedScopeIdentifiers, $redirectUri = null) |
|||||
| 1105 | { |
||||||
| 1106 | 6 | if (!$client->validateAuthRequestScopes($requestedScopeIdentifiers, $unknownScopes, $unauthorizedScopes)) { |
|||||
| 1107 | Yii::info('Invalid scope for client "' . $client->getIdentifier() . '": ' |
||||||
| 1108 | . VarDumper::export(['unauthorizedScopes' => $unauthorizedScopes, 'unknownScopes' => $unknownScopes])); |
||||||
| 1109 | |||||||
| 1110 | if ( |
||||||
| 1111 | $client->getExceptionOnInvalidScope() === true |
||||||
| 1112 | || ( |
||||||
| 1113 | $client->getExceptionOnInvalidScope() === null |
||||||
| 1114 | && $this->exceptionOnInvalidScope === true |
||||||
| 1115 | ) |
||||||
| 1116 | ) { |
||||||
| 1117 | if ($unknownScopes) { |
||||||
| 1118 | throw Oauth2ServerException::unknownScope(array_shift($unknownScopes), $redirectUri); |
||||||
| 1119 | } |
||||||
| 1120 | throw Oauth2ServerException::scopeNotAllowedForClient(array_shift($unauthorizedScopes), $redirectUri); |
||||||
| 1121 | } |
||||||
| 1122 | } |
||||||
| 1123 | } |
||||||
| 1124 | |||||||
| 1125 | /** |
||||||
| 1126 | * Generates a redirect Response to the Client Authorization page where the user is prompted to authorize the |
||||||
| 1127 | * Client and requested Scope. |
||||||
| 1128 | * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest |
||||||
| 1129 | * @return Response |
||||||
| 1130 | * @since 1.0.0 |
||||||
| 1131 | */ |
||||||
| 1132 | 5 | public function generateClientAuthReqRedirectResponse($clientAuthorizationRequest) |
|||||
| 1133 | { |
||||||
| 1134 | 5 | $this->setClientAuthReqSession($clientAuthorizationRequest); |
|||||
| 1135 | 5 | if (!empty($this->clientAuthorizationUrl)) { |
|||||
| 1136 | 1 | $url = $this->clientAuthorizationUrl; |
|||||
| 1137 | } else { |
||||||
| 1138 | 4 | $url = $this->uniqueId |
|||||
| 1139 | 4 | . '/' . Oauth2ConsentControllerInterface::CONTROLLER_NAME |
|||||
| 1140 | 4 | . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_CLIENT; |
|||||
| 1141 | } |
||||||
| 1142 | 5 | return Yii::$app->response->redirect([ |
|||||
| 1143 | 5 | $url, |
|||||
| 1144 | 5 | 'clientAuthorizationRequestId' => $clientAuthorizationRequest->getRequestId(), |
|||||
| 1145 | 5 | ]); |
|||||
| 1146 | } |
||||||
| 1147 | |||||||
| 1148 | /** |
||||||
| 1149 | * Generates a redirect Response to the End Session Authorization page where the user is prompted to authorize the |
||||||
| 1150 | * logout. |
||||||
| 1151 | * @param Oauth2EndSessionAuthorizationRequestInterface $endSessionAuthorizationRequest |
||||||
| 1152 | * @return Response |
||||||
| 1153 | * @since 1.0.0 |
||||||
| 1154 | */ |
||||||
| 1155 | public function generateEndSessionAuthReqRedirectResponse($endSessionAuthorizationRequest) |
||||||
| 1156 | { |
||||||
| 1157 | $this->setEndSessionAuthReqSession($endSessionAuthorizationRequest); |
||||||
| 1158 | if (!empty($this->openIdConnectLogoutConfirmationUrl)) { |
||||||
| 1159 | $url = $this->openIdConnectLogoutConfirmationUrl; |
||||||
| 1160 | } else { |
||||||
| 1161 | $url = $this->uniqueId |
||||||
| 1162 | . '/' . Oauth2ConsentControllerInterface::CONTROLLER_NAME |
||||||
| 1163 | . '/' . Oauth2ConsentControllerInterface::ACTION_NAME_AUTHORIZE_END_SESSION; |
||||||
| 1164 | } |
||||||
| 1165 | return Yii::$app->response->redirect([ |
||||||
| 1166 | $url, |
||||||
| 1167 | 'endSessionAuthorizationRequestId' => $endSessionAuthorizationRequest->getRequestId(), |
||||||
| 1168 | ]); |
||||||
| 1169 | } |
||||||
| 1170 | |||||||
| 1171 | /** |
||||||
| 1172 | * Get a previously stored Client Authorization Request from the session. |
||||||
| 1173 | * @param string $requestId |
||||||
| 1174 | * @return Oauth2ClientAuthorizationRequestInterface|null |
||||||
| 1175 | * @since 1.0.0 |
||||||
| 1176 | */ |
||||||
| 1177 | 5 | public function getClientAuthReqSession($requestId) |
|||||
| 1178 | { |
||||||
| 1179 | 5 | return $this->getAuthReqSession( |
|||||
| 1180 | 5 | $requestId, |
|||||
| 1181 | 5 | static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX, |
|||||
| 1182 | 5 | Oauth2ClientAuthorizationRequestInterface::class, |
|||||
| 1183 | 5 | ); |
|||||
| 1184 | } |
||||||
| 1185 | |||||||
| 1186 | /** |
||||||
| 1187 | * Get a previously stored OIDC End Session Authorization Request from the session. |
||||||
| 1188 | * @param string $requestId |
||||||
| 1189 | * @return Oauth2EndSessionAuthorizationRequestInterface|null |
||||||
| 1190 | * @since 1.0.0 |
||||||
| 1191 | */ |
||||||
| 1192 | public function getEndSessionAuthReqSession($requestId) |
||||||
| 1193 | { |
||||||
| 1194 | return $this->getAuthReqSession( |
||||||
| 1195 | $requestId, |
||||||
| 1196 | static::END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX, |
||||||
| 1197 | Oauth2EndSessionAuthorizationRequestInterface::class, |
||||||
| 1198 | ); |
||||||
| 1199 | } |
||||||
| 1200 | |||||||
| 1201 | /** |
||||||
| 1202 | * Get a previously stored Authorization Request from the session. |
||||||
| 1203 | * @template T of Oauth2BaseAuthorizationRequestInterface |
||||||
| 1204 | * @param string $requestId |
||||||
| 1205 | * @param string $cachePrefix |
||||||
| 1206 | * @param class-string<T> $expectedInterface |
||||||
|
0 ignored issues
–
show
|
|||||||
| 1207 | * @return T|null |
||||||
| 1208 | * @since 1.0.0 |
||||||
| 1209 | */ |
||||||
| 1210 | 5 | protected function getAuthReqSession($requestId, $cachePrefix, $expectedInterface) |
|||||
| 1211 | { |
||||||
| 1212 | 5 | if (empty($requestId)) { |
|||||
| 1213 | return null; |
||||||
| 1214 | } |
||||||
| 1215 | 5 | $key = $cachePrefix . $requestId; |
|||||
| 1216 | 5 | $authorizationRequest = Yii::$app->session->get($key); |
|||||
|
0 ignored issues
–
show
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
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...
|
|||||||
| 1217 | 5 | if (!($authorizationRequest instanceof $expectedInterface)) { |
|||||
| 1218 | 2 | if (!empty($authorizationRequest)) { |
|||||
| 1219 | 1 | Yii::warning( |
|||||
| 1220 | 1 | 'Found a Authorization Request Session with key "' . $key |
|||||
| 1221 | 1 | . '", but it\'s not a ' . $expectedInterface |
|||||
| 1222 | 1 | ); |
|||||
| 1223 | } |
||||||
| 1224 | 2 | return null; |
|||||
| 1225 | } |
||||||
| 1226 | 5 | if ($authorizationRequest->getRequestId() !== $requestId) { |
|||||
| 1227 | 1 | Yii::warning( |
|||||
| 1228 | 1 | 'Found a Authorization Request Session with key "' . $key |
|||||
| 1229 | 1 | . '", but its request id does not match "' . $requestId . '".' |
|||||
| 1230 | 1 | ); |
|||||
| 1231 | 1 | return null; |
|||||
| 1232 | } |
||||||
| 1233 | 5 | $authorizationRequest->setModule($this); |
|||||
| 1234 | |||||||
| 1235 | 5 | return $authorizationRequest; |
|||||
| 1236 | } |
||||||
| 1237 | |||||||
| 1238 | /** |
||||||
| 1239 | * Stores the Client Authorization Request in the session. |
||||||
| 1240 | * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest |
||||||
| 1241 | * @since 1.0.0 |
||||||
| 1242 | */ |
||||||
| 1243 | 8 | public function setClientAuthReqSession($clientAuthorizationRequest) |
|||||
| 1244 | { |
||||||
| 1245 | 8 | $this->setAuthReqSession( |
|||||
| 1246 | 8 | $clientAuthorizationRequest, |
|||||
| 1247 | 8 | static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX |
|||||
| 1248 | 8 | ); |
|||||
| 1249 | } |
||||||
| 1250 | |||||||
| 1251 | /** |
||||||
| 1252 | * Stores the OIDC End Session Authorization Request in the session. |
||||||
| 1253 | * @param Oauth2EndSessionAuthorizationRequestInterface $endSessionAuthorizationRequest |
||||||
| 1254 | * @since 1.0.0 |
||||||
| 1255 | */ |
||||||
| 1256 | public function setEndSessionAuthReqSession($endSessionAuthorizationRequest) |
||||||
| 1257 | { |
||||||
| 1258 | $this->setAuthReqSession( |
||||||
| 1259 | $endSessionAuthorizationRequest, |
||||||
| 1260 | static::END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX |
||||||
| 1261 | ); |
||||||
| 1262 | } |
||||||
| 1263 | |||||||
| 1264 | /** |
||||||
| 1265 | * Stores the Authorization Request in the session. |
||||||
| 1266 | * @param Oauth2BaseAuthorizationRequestInterface $authorizationRequest |
||||||
| 1267 | * @param string $cachePrefix |
||||||
| 1268 | * @since 1.0.0 |
||||||
| 1269 | */ |
||||||
| 1270 | 8 | protected function setAuthReqSession($authorizationRequest, $cachePrefix) |
|||||
| 1271 | { |
||||||
| 1272 | 8 | $requestId = $authorizationRequest->getRequestId(); |
|||||
| 1273 | 8 | if (empty($requestId)) { |
|||||
| 1274 | 1 | throw new InvalidArgumentException('$authorizationRequest must return a request id.'); |
|||||
| 1275 | } |
||||||
| 1276 | 7 | $key = $cachePrefix . $requestId; |
|||||
| 1277 | 7 | Yii::$app->session->set($key, $authorizationRequest); |
|||||
| 1278 | } |
||||||
| 1279 | |||||||
| 1280 | /** |
||||||
| 1281 | * Clears a Client Authorization Request from the session storage. |
||||||
| 1282 | * @param string $requestId |
||||||
| 1283 | * @since 1.0.0 |
||||||
| 1284 | */ |
||||||
| 1285 | 2 | public function removeClientAuthReqSession($requestId) |
|||||
| 1286 | { |
||||||
| 1287 | 2 | $this->removeAuthReqSession($requestId, static::CLIENT_AUTHORIZATION_REQUEST_SESSION_PREFIX); |
|||||
| 1288 | } |
||||||
| 1289 | |||||||
| 1290 | /** |
||||||
| 1291 | * Clears an End Session Authorization Request from the session storage. |
||||||
| 1292 | * @param string $requestId |
||||||
| 1293 | * @since 1.0.0 |
||||||
| 1294 | */ |
||||||
| 1295 | public function removeEndSessionAuthReqSession($requestId) |
||||||
| 1296 | { |
||||||
| 1297 | $this->removeAuthReqSession($requestId, static::END_SESSION_AUTHORIZATION_REQUEST_SESSION_PREFIX); |
||||||
| 1298 | } |
||||||
| 1299 | |||||||
| 1300 | /** |
||||||
| 1301 | * Clears an Authorization Request from the session storage. |
||||||
| 1302 | * @param string $requestId |
||||||
| 1303 | * @param string $cachePrefix |
||||||
| 1304 | * @since 1.0.0 |
||||||
| 1305 | */ |
||||||
| 1306 | 2 | public function removeAuthReqSession($requestId, $cachePrefix) |
|||||
| 1307 | { |
||||||
| 1308 | 2 | if (empty($requestId)) { |
|||||
| 1309 | 1 | throw new InvalidArgumentException('$requestId can not be empty.'); |
|||||
| 1310 | } |
||||||
| 1311 | 1 | $key = $cachePrefix . $requestId; |
|||||
| 1312 | 1 | Yii::$app->session->remove($key); |
|||||
| 1313 | } |
||||||
| 1314 | |||||||
| 1315 | /** |
||||||
| 1316 | * Stores whether the user was authenticated during the completion of the Client Authorization Request. |
||||||
| 1317 | * @param string $clientAuthorizationRequestId |
||||||
| 1318 | * @param bool $authenticatedDuringRequest |
||||||
| 1319 | * @since 1.0.0 |
||||||
| 1320 | */ |
||||||
| 1321 | public function setUserAuthenticatedDuringClientAuthRequest( |
||||||
| 1322 | $clientAuthorizationRequestId, |
||||||
| 1323 | $authenticatedDuringRequest |
||||||
| 1324 | ) { |
||||||
| 1325 | $clientAuthorizationRequest = $this->getClientAuthReqSession($clientAuthorizationRequestId); |
||||||
| 1326 | if ($clientAuthorizationRequest) { |
||||||
| 1327 | $clientAuthorizationRequest->setUserAuthenticatedDuringRequest($authenticatedDuringRequest); |
||||||
| 1328 | $this->setClientAuthReqSession($clientAuthorizationRequest); |
||||||
| 1329 | } |
||||||
| 1330 | } |
||||||
| 1331 | |||||||
| 1332 | /** |
||||||
| 1333 | * Stores the user identity selected during the completion of the Client Authorization Request. |
||||||
| 1334 | * @param string $clientAuthorizationRequestId |
||||||
| 1335 | * @param Oauth2UserInterface $userIdentity |
||||||
| 1336 | * @since 1.0.0 |
||||||
| 1337 | */ |
||||||
| 1338 | public function setClientAuthRequestUserIdentity($clientAuthorizationRequestId, $userIdentity) |
||||||
| 1339 | { |
||||||
| 1340 | $clientAuthorizationRequest = $this->getClientAuthReqSession($clientAuthorizationRequestId); |
||||||
| 1341 | if ($clientAuthorizationRequest) { |
||||||
| 1342 | $clientAuthorizationRequest->setUserIdentity($userIdentity); |
||||||
| 1343 | $this->setClientAuthReqSession($clientAuthorizationRequest); |
||||||
| 1344 | } |
||||||
| 1345 | } |
||||||
| 1346 | |||||||
| 1347 | /** |
||||||
| 1348 | * Generates a redirect Response when the Client Authorization Request is completed. |
||||||
| 1349 | * @param Oauth2ClientAuthorizationRequestInterface $clientAuthorizationRequest |
||||||
| 1350 | * @return Response |
||||||
| 1351 | * @since 1.0.0 |
||||||
| 1352 | */ |
||||||
| 1353 | 1 | public function generateClientAuthReqCompledRedirectResponse($clientAuthorizationRequest) |
|||||
| 1354 | { |
||||||
| 1355 | 1 | $clientAuthorizationRequest->processAuthorization(); |
|||||
| 1356 | 1 | $this->setClientAuthReqSession($clientAuthorizationRequest); |
|||||
| 1357 | 1 | return Yii::$app->response->redirect($clientAuthorizationRequest->getAuthorizationRequestUrl()); |
|||||
| 1358 | } |
||||||
| 1359 | |||||||
| 1360 | /** |
||||||
| 1361 | * Generates a redirect Response when the End Session Authorization Request is completed. |
||||||
| 1362 | * @param Oauth2EndSessionAuthorizationRequestInterface $endSessionAuthorizationRequest |
||||||
| 1363 | * @return Response |
||||||
| 1364 | * @since 1.0.0 |
||||||
| 1365 | */ |
||||||
| 1366 | public function generateEndSessionAuthReqCompledRedirectResponse($endSessionAuthorizationRequest) |
||||||
| 1367 | { |
||||||
| 1368 | $endSessionAuthorizationRequest->processAuthorization(); |
||||||
| 1369 | $this->setEndSessionAuthReqSession($endSessionAuthorizationRequest); |
||||||
| 1370 | return Yii::$app->response->redirect($endSessionAuthorizationRequest->getEndSessionRequestUrl()); |
||||||
| 1371 | } |
||||||
| 1372 | |||||||
| 1373 | /** |
||||||
| 1374 | * @return IdentityInterface|Oauth2UserInterface|Oauth2OidcUserInterface|null |
||||||
| 1375 | * @throws InvalidConfigException |
||||||
| 1376 | * @since 1.0.0 |
||||||
| 1377 | */ |
||||||
| 1378 | 5 | public function getUserIdentity() |
|||||
| 1379 | { |
||||||
| 1380 | 5 | $user = Yii::$app->user->identity; |
|||||
| 1381 | 5 | if (!empty($user) && !($user instanceof Oauth2UserInterface)) { |
|||||
| 1382 | 1 | throw new InvalidConfigException( |
|||||
| 1383 | 1 | 'Yii::$app->user->identity (currently ' . get_class($user) |
|||||
| 1384 | 1 | . ') must implement ' . Oauth2UserInterface::class |
|||||
| 1385 | 1 | ); |
|||||
| 1386 | } |
||||||
| 1387 | 4 | return $user; |
|||||
| 1388 | } |
||||||
| 1389 | |||||||
| 1390 | /** |
||||||
| 1391 | * Validates a bearer token authenticated request. Note: this method does not return a result but will throw |
||||||
| 1392 | * an exception when the authentication fails. |
||||||
| 1393 | * @throws InvalidConfigException |
||||||
| 1394 | * @throws Oauth2ServerException |
||||||
| 1395 | * @since 1.0.0 |
||||||
| 1396 | */ |
||||||
| 1397 | 3 | public function validateAuthenticatedRequest() |
|||||
| 1398 | { |
||||||
| 1399 | 3 | $psr7Request = Psr7Helper::yiiToPsr7Request(Yii::$app->request); |
|||||
|
0 ignored issues
–
show
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
Loading history...
|
|||||||
| 1400 | |||||||
| 1401 | 3 | $psr7Request = $this->getResourceServer()->validateAuthenticatedRequest($psr7Request); |
|||||
| 1402 | |||||||
| 1403 | 3 | $token = substr(Yii::$app->request->headers->get('Authorization'), self::BEARER_TOKEN_OFFSET); |
|||||
|
0 ignored issues
–
show
It seems like
Yii::app->request->headers->get('Authorization') can also be of type array and null; however, parameter $string of substr() 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
Loading history...
|
|||||||
| 1404 | |||||||
| 1405 | 3 | if ($token) { |
|||||
| 1406 | 3 | $claims = $this->getAccessToken($token)->claims(); |
|||||
| 1407 | |||||||
| 1408 | 3 | foreach ($claims->all() as $claimKey => $claimValue) { |
|||||
| 1409 | 3 | if (!$this->isDefaultClaimKey($claimKey)) { |
|||||
| 1410 | $psr7Request = $psr7Request->withAttribute($claimKey, $claimValue); |
||||||
| 1411 | } |
||||||
| 1412 | } |
||||||
| 1413 | } |
||||||
| 1414 | |||||||
| 1415 | 3 | $this->_oauthClaims = $psr7Request->getAttributes(); |
|||||
| 1416 | 3 | $this->_oauthClaimsAuthorizationHeader = Yii::$app->request->getHeaders()->get('Authorization'); |
|||||
|
0 ignored issues
–
show
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 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...
|
|||||||
| 1417 | } |
||||||
| 1418 | |||||||
| 1419 | /** |
||||||
| 1420 | * Find a user identity bases on an access token. |
||||||
| 1421 | * Note: validateAuthenticatedRequest() must be called before this method is called. |
||||||
| 1422 | * @param string $token |
||||||
| 1423 | * @param string $type |
||||||
| 1424 | * @return Oauth2UserInterface|null |
||||||
| 1425 | * @throws InvalidConfigException |
||||||
| 1426 | * @throws Oauth2ServerException |
||||||
| 1427 | * @see validateAuthenticatedRequest() |
||||||
| 1428 | * @since 1.0.0 |
||||||
| 1429 | */ |
||||||
| 1430 | 4 | public function findIdentityByAccessToken($token, $type) |
|||||
| 1431 | { |
||||||
| 1432 | 4 | if (!is_a($type, Oauth2HttpBearerAuthInterface::class, true)) { |
|||||
| 1433 | 1 | throw new InvalidCallException($type . ' must implement ' . Oauth2HttpBearerAuthInterface::class); |
|||||
| 1434 | } |
||||||
| 1435 | |||||||
| 1436 | if ( |
||||||
| 1437 | 3 | !preg_match('/^Bearer\s+(.*?)$/', $this->_oauthClaimsAuthorizationHeader, $matches) |
|||||
|
0 ignored issues
–
show
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
Loading history...
|
|||||||
| 1438 | 3 | || !Yii::$app->security->compareString($matches[1], $token) |
|||||
| 1439 | ) { |
||||||
| 1440 | 1 | throw new InvalidCallException( |
|||||
| 1441 | 1 | 'validateAuthenticatedRequest() must be called before findIdentityByAccessToken().' |
|||||
| 1442 | 1 | ); |
|||||
| 1443 | } |
||||||
| 1444 | |||||||
| 1445 | 2 | $userId = $this->getRequestOauthUserId(); |
|||||
| 1446 | 2 | if (empty($userId)) { |
|||||
| 1447 | 1 | return null; |
|||||
| 1448 | } |
||||||
| 1449 | |||||||
| 1450 | 1 | return $this->identityClass::findIdentity($userId); |
|||||
| 1451 | } |
||||||
| 1452 | |||||||
| 1453 | /** |
||||||
| 1454 | * Generate a "Personal Access Token" (PAT) which can be used as an alternative to using passwords |
||||||
| 1455 | * for authentication (e.g. when using an API or command line). |
||||||
| 1456 | * |
||||||
| 1457 | * Note: Personal Access Tokens are intended to access resources on behalf users themselves. |
||||||
| 1458 | * To grant access to resources on behalf of an organization, or for long-lived integrations, |
||||||
| 1459 | * you most likely want to define an Oauth2 Client with the "Client Credentials" grant |
||||||
| 1460 | * (https://oauth.net/2/grant-types/client-credentials). |
||||||
| 1461 | * |
||||||
| 1462 | * @param string $clientIdentifier The Oauth2 client identifier for which the PAT should be generated. |
||||||
| 1463 | * @param int|string $userIdentifier The identifier (primary key) of the user for which the PAT should be generated. |
||||||
| 1464 | * @param Oauth2ScopeInterface[]|string[]|string|null $scope The Access Token scope. |
||||||
| 1465 | * @param string|true|null $clientSecret If the client is a "confidential" client the secret is required. |
||||||
| 1466 | * If the boolean value `true` is passed, the client secret is automatically injected. |
||||||
| 1467 | * @return Oauth2AccessTokenData |
||||||
| 1468 | */ |
||||||
| 1469 | 3 | public function generatePersonalAccessToken($clientIdentifier, $userIdentifier, $scope = null, $clientSecret = null) |
|||||
| 1470 | { |
||||||
| 1471 | 3 | if (is_array($scope)) { |
|||||
| 1472 | 2 | $scopeIdentifiers = []; |
|||||
| 1473 | 2 | foreach ($scope as $scopeItem) { |
|||||
| 1474 | 2 | if (is_string($scopeItem)) { |
|||||
| 1475 | 1 | $scopeIdentifiers[] = $scopeItem; |
|||||
| 1476 | 1 | } elseif ($scopeItem instanceof Oauth2ScopeInterface) { |
|||||
| 1477 | 1 | $scopeIdentifiers[] = $scopeItem->getIdentifier(); |
|||||
| 1478 | } else { |
||||||
| 1479 | throw new InvalidArgumentException('If $scope is an array its elements must be either' |
||||||
| 1480 | . ' a string or an instance of ' . Oauth2ScopeInterface::class); |
||||||
| 1481 | } |
||||||
| 1482 | } |
||||||
| 1483 | 2 | $scope = implode(' ', $scopeIdentifiers); |
|||||
| 1484 | } |
||||||
| 1485 | |||||||
| 1486 | 3 | if ($clientSecret === true) { |
|||||
| 1487 | /** @var Oauth2ClientInterface $client */ |
||||||
| 1488 | 3 | $client = $this->getClientRepository()->findModelByIdentifier($clientIdentifier); |
|||||
| 1489 | 3 | if ($client && $client->isConfidential()) { |
|||||
| 1490 | 3 | $clientSecret = $client->getDecryptedSecret($this->getCryptographer()); |
|||||
| 1491 | } else { |
||||||
| 1492 | $clientSecret = null; |
||||||
| 1493 | } |
||||||
| 1494 | } |
||||||
| 1495 | |||||||
| 1496 | 3 | $request = (new Psr7ServerRequest('POST', ''))->withParsedBody([ |
|||||
| 1497 | 3 | 'grant_type' => static::GRANT_TYPE_IDENTIFIER_PERSONAL_ACCESS_TOKEN, |
|||||
| 1498 | 3 | 'client_id' => $clientIdentifier, |
|||||
| 1499 | 3 | 'client_secret' => $clientSecret, |
|||||
| 1500 | 3 | 'user_id' => $userIdentifier, |
|||||
| 1501 | 3 | 'scope' => $scope, |
|||||
| 1502 | 3 | ]); |
|||||
| 1503 | |||||||
| 1504 | 3 | return new Oauth2AccessTokenData(Json::decode( |
|||||
|
0 ignored issues
–
show
It seems like
yii\helpers\Json::decode...etBody()->__toString()) can also be of type null; however, parameter $data of rhertogh\Yii2Oauth2Serve...okenData::__construct() does only seem to accept array, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 1505 | 3 | $this->getAuthorizationServer() |
|||||
| 1506 | 3 | ->respondToAccessTokenRequest( |
|||||
| 1507 | 3 | $request, |
|||||
| 1508 | 3 | new Psr7Response() |
|||||
| 1509 | 3 | ) |
|||||
| 1510 | 3 | ->getBody() |
|||||
| 1511 | 3 | ->__toString() |
|||||
| 1512 | 3 | )); |
|||||
| 1513 | } |
||||||
| 1514 | |||||||
| 1515 | /** |
||||||
| 1516 | * @inheritDoc |
||||||
| 1517 | */ |
||||||
| 1518 | 5 | public function getRequestOauthClaim($attribute, $default = null) |
|||||
| 1519 | { |
||||||
| 1520 | 5 | if (empty($this->_oauthClaimsAuthorizationHeader)) { |
|||||
| 1521 | // User authorization was not processed by Oauth2Module. |
||||||
| 1522 | 1 | return $default; |
|||||
| 1523 | } |
||||||
| 1524 | 4 | if (Yii::$app->request->getHeaders()->get('Authorization') !== $this->_oauthClaimsAuthorizationHeader) { |
|||||
| 1525 | 1 | throw new InvalidCallException( |
|||||
| 1526 | 1 | 'App Request Authorization header does not match the processed Oauth header.' |
|||||
| 1527 | 1 | ); |
|||||
| 1528 | } |
||||||
| 1529 | 3 | return $this->_oauthClaims[$attribute] ?? $default; |
|||||
| 1530 | } |
||||||
| 1531 | |||||||
| 1532 | /** |
||||||
| 1533 | * Helper function to ensure the required properties are configured for the module. |
||||||
| 1534 | * @param string[] $properties |
||||||
| 1535 | * @throws InvalidConfigException |
||||||
| 1536 | * @since 1.0.0 |
||||||
| 1537 | */ |
||||||
| 1538 | 32 | protected function ensureProperties($properties) |
|||||
| 1539 | { |
||||||
| 1540 | 32 | foreach ($properties as $property) { |
|||||
| 1541 | 32 | if (empty($this->$property)) { |
|||||
| 1542 | 6 | throw new InvalidConfigException(__CLASS__ . '::$' . $property . ' must be set.'); |
|||||
| 1543 | } |
||||||
| 1544 | } |
||||||
| 1545 | } |
||||||
| 1546 | |||||||
| 1547 | /** |
||||||
| 1548 | * @throws InvalidConfigException |
||||||
| 1549 | */ |
||||||
| 1550 | public function logoutUser($revokeTokens = true) |
||||||
| 1551 | { |
||||||
| 1552 | $identity = $this->getUserIdentity(); |
||||||
|
0 ignored issues
–
show
Are you sure the assignment to
$identity is correct as $this->getUserIdentity() targeting rhertogh\Yii2Oauth2Serve...dule::getUserIdentity() seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||||
| 1553 | |||||||
| 1554 | if ($identity) { |
||||||
|
0 ignored issues
–
show
|
|||||||
| 1555 | if ($revokeTokens) { |
||||||
| 1556 | $this->revokeTokensByUserId($identity->getId()); |
||||||
| 1557 | } |
||||||
| 1558 | |||||||
| 1559 | Yii::$app->user->logout(); |
||||||
| 1560 | } |
||||||
| 1561 | } |
||||||
| 1562 | |||||||
| 1563 | public function revokeTokensByUserId($userId) |
||||||
| 1564 | { |
||||||
| 1565 | $accessTokens = $this->getAccessTokenRepository()->revokeAccessTokensByUserId($userId); |
||||||
| 1566 | $accessTokenIds = array_map(fn($accessToken) => $accessToken->getPrimaryKey(), $accessTokens); |
||||||
| 1567 | $this->getRefreshTokenRepository()->revokeRefreshTokensByAccessTokenIds($accessTokenIds); |
||||||
| 1568 | } |
||||||
| 1569 | |||||||
| 1570 | 4 | public function getSupportedPromptValues() |
|||||
| 1571 | { |
||||||
| 1572 | 4 | $supportedPromptValues = [ |
|||||
| 1573 | 4 | Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_NONE, |
|||||
| 1574 | 4 | Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_LOGIN, |
|||||
| 1575 | 4 | Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_CONSENT, |
|||||
| 1576 | 4 | Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_SELECT_ACCOUNT, |
|||||
| 1577 | 4 | ]; |
|||||
| 1578 | |||||||
| 1579 | 4 | if (!empty($this->userAccountCreationUrl)) { |
|||||
| 1580 | 4 | $supportedPromptValues[] = Oauth2OidcAuthenticationRequestInterface::REQUEST_PARAMETER_PROMPT_CREATE; |
|||||
| 1581 | } |
||||||
| 1582 | |||||||
| 1583 | 4 | return $supportedPromptValues; |
|||||
| 1584 | } |
||||||
| 1585 | |||||||
| 1586 | /** |
||||||
| 1587 | * @return int |
||||||
| 1588 | */ |
||||||
| 1589 | 2 | public function getElaboratedHttpClientErrorsLogLevel() |
|||||
| 1590 | { |
||||||
| 1591 | 2 | if ($this->httpClientErrorsLogLevel === null) { |
|||||
| 1592 | 1 | return YII_DEBUG ? Logger::LEVEL_ERROR : Logger::LEVEL_INFO; |
|||||
| 1593 | } |
||||||
| 1594 | |||||||
| 1595 | 1 | return $this->httpClientErrorsLogLevel; |
|||||
| 1596 | } |
||||||
| 1597 | |||||||
| 1598 | |||||||
| 1599 | 3 | public function getJwtConfiguration(): Configuration |
|||||
| 1600 | { |
||||||
| 1601 | // Based on \League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator::initJwtConfiguration(). |
||||||
| 1602 | 3 | $jwtConfiguration = Configuration::forSymmetricSigner( |
|||||
| 1603 | 3 | new Sha256(), |
|||||
| 1604 | 3 | InMemory::plainText('empty', 'empty') |
|||||
| 1605 | 3 | ); |
|||||
| 1606 | |||||||
| 1607 | 3 | $publicKey = $this->getPublicKey(); |
|||||
| 1608 | 3 | $jwtConfiguration->setValidationConstraints( |
|||||
| 1609 | 3 | new SignedWith( |
|||||
| 1610 | 3 | new Sha256(), |
|||||
| 1611 | 3 | InMemory::plainText($publicKey->getKeyContents(), $publicKey->getPassPhrase() ?? '') |
|||||
| 1612 | 3 | ) |
|||||
| 1613 | 3 | ); |
|||||
| 1614 | |||||||
| 1615 | 3 | return $jwtConfiguration; |
|||||
| 1616 | } |
||||||
| 1617 | |||||||
| 1618 | 3 | public function getAccessToken(string $token): Token |
|||||
| 1619 | { |
||||||
| 1620 | 3 | $jwtConfiguration = $this->getJwtConfiguration(); |
|||||
| 1621 | 3 | $accessToken = $jwtConfiguration->parser()->parse($token); |
|||||
| 1622 | 3 | $jwtConfiguration->validator()->assert($accessToken, ...$jwtConfiguration->validationConstraints()); |
|||||
| 1623 | 3 | Yii::debug('Found access token: ' . $token, __METHOD__); |
|||||
| 1624 | |||||||
| 1625 | 3 | return $accessToken; |
|||||
| 1626 | } |
||||||
| 1627 | |||||||
| 1628 | 3 | protected function isDefaultClaimKey(string $claimKey): bool |
|||||
| 1629 | { |
||||||
| 1630 | 3 | return in_array( |
|||||
| 1631 | 3 | $claimKey, |
|||||
| 1632 | 3 | [ |
|||||
| 1633 | 3 | 'aud', |
|||||
| 1634 | 3 | 'jti', |
|||||
| 1635 | 3 | 'iat', |
|||||
| 1636 | 3 | 'nbf', |
|||||
| 1637 | 3 | 'exp', |
|||||
| 1638 | 3 | 'sub', |
|||||
| 1639 | 3 | 'scopes', |
|||||
| 1640 | 3 | 'client_id', |
|||||
| 1641 | 3 | ] |
|||||
| 1642 | 3 | ); |
|||||
| 1643 | } |
||||||
| 1644 | } |
||||||
| 1645 |