deadmantfa / yii2-oauth2-server
OAuth 2.0 server for Yii 2.0 with MAC tokens support.
Requires
- php: >=8.1
- guzzlehttp/guzzle: ^7.0
- lcobucci/jwt: ^5.4.2
- league/oauth2-server: ^9.1
- yiisoft/yii2: ^2.0
This package is auto-updated.
Last update: 2025-01-07 15:35:08 UTC
README
This is a fork of the original chervand/yii2-oauth2-server
plugin, updated for PHP 8+, league/oauth2-server ^9.1, and additional custom grants (including optional support for MAC tokens, token revocation, and more).
It provides:
- A standards-compliant OAuth2.0 Server for Yii2
- Refresh token support (RFC6749 / RFC7009)
- MAC token support (experimental, draft-ietf-oauth-v2-http-mac-05)
- Token revocation endpoints
- Flexible DB-based migrations for storing OAuth2 clients, tokens, scopes, etc.
- Easy integration with your Yii2
user
component so you can unify login states and roles.
1. Installation
Add this package to your project:
composer require deadmantfa/yii2-oauth2-server
1.1 Apply DB Migrations
Run:
./yii migrate --migrationPath="@vendor/deadmantfa/yii2-oauth2-server/migrations"
This creates the default OAuth2-related tables (clients, tokens, scopes, etc.).
1.2 Generate Public & Private Keys
Follow League’s official instructions for generating private/public keys. Typically:
openssl genrsa -out private.key 2048 openssl rsa -in private.key -pubout -out public.key
Store them somewhere safe (e.g. @app/oauth2/private.key, @app/oauth2/public.key).
2. Basic Configuration
Below is a simple example of how to configure the module in your config/main.php (or similar file).
return [ // ... 'bootstrap' => [ 'oauth2', // ensures module is bootstrapped // ... ], 'modules' => [ 'oauth2' => [ 'class' => \deadmantfa\yii2\oauth2\server\Module::class, // Your key file paths: 'privateKey' => __DIR__ . '/../oauth2/private.key', 'publicKey' => __DIR__ . '/../oauth2/public.key', // Encryption key used for JWT, typically 32+ bytes 'encryptionKey' => 'some-random-binary-string', // Example: define any custom Repositories or model classes 'components' => [ 'accessTokenRepository' => [ 'class' => \deadmantfa\yii2\oauth2\server\components\Repositories\AccessTokenRepository::class, ], 'refreshTokenRepository' => [ 'class' => \deadmantfa\yii2\oauth2\server\components\Repositories\RefreshTokenRepository::class, ], 'clientRepository' => [ 'class' => \deadmantfa\yii2\oauth2\server\components\Repositories\ClientRepository::class, ], 'scopeRepository' => [ 'class' => \deadmantfa\yii2\oauth2\server\components\Repositories\ScopeRepository::class, ], // ... ], // Optional caching logic for repositories 'cache' => [ \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface::class => [ 'cacheDuration' => 3600, 'cacheDependency' => new \yii\caching\FileDependency(['fileName' => 'AccessTokenRepoCache.txt']), ], // ... ], // Register the grant types you want 'enableGrantTypes' => static function (\deadmantfa\yii2\oauth2\server\Module $module) { $server = $module->authorizationServer; // Password + Refresh Token Grant $passwordGrant = new \League\OAuth2\Server\Grant\PasswordGrant( $module->getComponent('userRepository'), $module->getComponent('refreshTokenRepository') ); $passwordGrant->setRefreshTokenTTL(new \DateInterval('P1M')); $server->enableGrantType($passwordGrant, new \DateInterval('PT1H')); // Client Credentials $server->enableGrantType(new \League\OAuth2\Server\Grant\ClientCredentialsGrant()); // Refresh Token $refreshGrant = new \League\OAuth2\Server\Grant\RefreshTokenGrant( $module->getComponent('refreshTokenRepository') ); $refreshGrant->setRefreshTokenTTL(new \DateInterval('P1M')); $server->enableGrantType($refreshGrant, new \DateInterval('PT1H')); // (Optional) Revoke Grant $server->enableGrantType(new \deadmantfa\yii2\oauth2\server\components\Grant\RevokeGrant( $module->getComponent('refreshTokenRepository'), $module->publicKey )); }, ], ], // ... ];
That’s enough to spin up the Authorization Server portion. You’ll have endpoints like:
POST /oauth2/token
(to exchange credentials for tokens)POST /oauth2/revoke
(to revoke a token, if you enabled RevokeGrant)
3. Integrating with Your user Component
3.1 Provide a UserRepositoryInterface
In League OAuth2, you need a repository that implements UserRepositoryInterface
. Typically, you can have your user model implement UserEntityInterface
and provide a getUserEntityByUserCredentials()
method:
class MyUserRepository implements UserRepositoryInterface { public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity) { $user = User::findOne(['username' => $username]); if (!$user || !Yii::$app->security->validatePassword($password, $user->password_hash)) { return null; } return $user; // which implements UserEntityInterface } }
3.2 Linking to Your Identity
in Yii2
If you want to unify your “OAuth2 user ID” with your standard Yii user identity:
- In
config/main.php
, set'identityClass' => MyUser::class
in your user component. - Ensure your model has
getId()
returning the correct user identifier. - Make sure the above repository uses that same model.
4. Resource Server (Protecting Your API)
To validate tokens in your controllers, attach an authenticator that checks the Authorization: Bearer <token>
header. For example:
class MyApiController extends \yii\rest\ActiveController { public function behaviors() { $behaviors = parent::behaviors(); unset($behaviors['authenticator']); unset($behaviors['rateLimiter']); /** @var \deadmantfa\yii2\oauth2\server\Module $auth */ $auth = Yii::$app->getModule('oauth2'); $behaviors['authenticator'] = [ 'class' => \yii\filters\auth\CompositeAuth::class, 'authMethods' => [ [ 'class' => \deadmantfa\yii2\oauth2\server\components\AuthMethods\HttpBearerAuth::class, 'publicKey' => $auth->publicKey, 'cache' => $auth->cache, ], // or add Mac tokens: // [ // 'class' => \deadmantfa\yii2\oauth2\server\components\AuthMethods\HttpMacAuth::class, // ], ], ]; $behaviors['rateLimiter'] = [ 'class' => \yii\filters\RateLimiter::class, ]; return $behaviors; } }
Any requests with an invalid or no token get a 401 response.
5. MAC Tokens (Optional)
If you want MAC tokens (still a draft), you can enable them by adding:
use \deadmantfa\yii2\oauth2\server\components\AuthMethods\HttpMacAuth; $behaviors['authenticator'] = [ 'class' => \yii\filters\auth\CompositeAuth::class, 'authMethods' => [ [ 'class' => HttpMacAuth::class, 'publicKey' => $auth->publicKey, 'cache' => $auth->cache, ], ], ];
And in your module’s enableGrantTypes
, you can enable a MacTokenResponse
instead of BearerTokenResponse
.
6. Revoke Tokens (RFC7009)
Add the RevokeGrant
in enableGrantTypes
:
$server->enableGrantType( new \deadmantfa\yii2\oauth2\server\components\Grant\RevokeGrant( $module->refreshTokenRepository, $module->publicKey ) );
Then request:
POST /oauth2/revoke Authorization: Basic <client_id:client_secret base64> Content-Type: application/json { "token": "<refresh_token or access_token>" }
The plugin revokes tokens in the DB. The next time the resource server checks that token, it’s marked invalid.
7. Optional CORS Support
If you want to handle CORS within this plugin (e.g. for /oauth2/token calls in front-end apps), you can either:
- (A) Configure CORS globally in your Yii2 app (recommended for most setups), or
- (B) Enable a built-in CORS filter inside this module’s controllers.
7.1 Sample Global CORS
In your main config/main.php
:
'on beforeRequest' => function ($event) { Yii::$app->attachBehavior('globalCors', [ 'class' => \yii\filters\Cors::class, 'cors' => [ 'Origin' => ['https://yourfrontend.com'], 'Access-Control-Allow-Credentials' => true, 'Access-Control-Allow-Headers' => ['Authorization', 'Content-Type', 'X-Requested-With', 'Accept', 'Origin', 'Cache-Control', 'Pragma'], 'Access-Control-Allow-Methods' => ['GET','POST','OPTIONS','PUT','DELETE'], 'Access-Control-Max-Age' => 3600, ], ]); },
7.2 Built-in CORS in Module
If you want the plugin to handle it internally, you can define a property like:
'modules' => [ 'oauth2' => [ 'class' => \deadmantfa\yii2\oauth2\server\Module::class, 'enableCors' => true, 'corsConfig' => [ 'class' => \yii\filters\Cors::class, 'cors' => [ 'Origin' => ['*'], 'Access-Control-Allow-Credentials' => true, 'Access-Control-Allow-Methods' => ['POST','OPTIONS'], 'Access-Control-Allow-Headers' => ['Authorization','Content-Type','X-Requested-With','Accept','Origin','Cache-Control','Pragma'], 'Access-Control-Max-Age' => 3600, ], ], // ... ], ],
8. Additional Notes
- For custom grants (like a
FirebaseGrant
or aPasswordlessGrant
), implementGrantTypeInterface
or extendAbstractGrant
and register it inenableGrantTypes
. - You can store user IDs as UUID strings or integers. Just ensure your
UserEntityInterface::getIdentifier()
returns a string that the library can embed in the token. - The library handles JWT signing/verification, so you just need to provide your private/public RSA keys.