This extension allow the developper to use Oauth2 server.
If you use Packagist for installing packages, then you can update your composer.json like this :
{
"require": {
"sweelix/yii2-oauth2-server": "~1.2.0"
}
}
Add extension to your configuration :
return [
//....
'bootstrap' => [
//....
'oauth2',
//....
],
'modules' => [
//....
'oauth2' => [
'class' => 'sweelix\oauth2\server\Module',
'backend' => BACKEND,
'db' => DB,
'identityClass' => 'app\models\User', // only if you don't want to use the user identityClass
//
// Parameters
//
],
//....
],
//....
];
You also need to enable PrettyUrl:
'components' => [
//....
'urlManager' => [
'enablePrettyUrl' => true,
'rules' => [
// your rules go here
],
// ....
]
// ....
]
All the migrations needed can be found inside src/migrations. Be sure to configure the database connection before applying them.
You can find examples and explanations about every grant types here and here.
For the Jwt Bearer Grant, you will need to create a Jwt entry in your database for the given client and subject.
backend
: can be redis or mysqldb
: id of the component or connection or connection configurationidentityClass
: user class used to link oauth2 authorization system default to user component identityClass
webUserParamId
: allow separation between main app user (session) and module app user, (default to __oauth2)identityCookieName
: allow separation between main app user (cookie) and module app user, (default to oauth2)webUser
: allow full management of module web user, (default to [])baseEndPoint
: base path for token and authorize endpoints default to ''
overrideLayout
: override module layout to use another one (ex: @app/views/layouts/oauth2)overrideViewPath
: override view path to use specific one (ex: @app/views/oauth2) allowImplicit
: allow implicit grant (default to false)allowAuthorizationCode
: allow authorization code grant (default to true)allowClientCredentials
: allow client credentials grant (default to true)allowPassword
: allow user credentials / password grant (default to true)allowCredentialsInRequestBody
: allow credentials in request body (default to true)allowPublicClients
: allow public clients (default to true)alwaysIssueNewRefreshToken
: always issue refresh token (default to true)unsetRefreshTokenAfterUse
: unset refresh token after use (default to true) useJwtAccessToken
: send access tokens as JWT (default : false)allowAlgorithm
: available algorithm for JWT (default : ['RS256', 'RS384', 'RS512'])jwtAudience
: default to token endpointstoreEncryptedTokenString
: store encrypted token (default : true)idTTL
: TTL of ID Token (default to 3600)accessTokenTTL
: TTL of access token (default to 3600)refreshTokenTTL
: TTL of refresh token (default to 14 24 3600)realm
: Realm value (default to Service)tokenQueryName
: name of the access token parameter (default to access_token)tokenBearerName
: name of authorization header (default to Bearer)enforceState
: enforce state parameter (default to true)allowOnlyRedirectUri
: need exact redirect URI (default to true)allowOpenIdConnect
: enable openId connect (default : false) // not implemented yetenforceRedirect
: enforce redirect parameter (default to false)authorizationCodeTTL
: TTL of authorization code (default to 30)cors
: enable CORS
on the token endpoint (default : false) the CORS part can be defined using an array as described in Yii documentation return [
//....
'bootstrap' => [
//....
'oauth2',
//....
],
'modules' => [
//....
'oauth2' => [
'class' => 'sweelix\oauth2\server\Module',
'backend' => 'redis',
'db' => 'redis',
'identityClass' => 'app\models\User', // only if you don't want to use the user identityClass
//
// Cors parameters example :
//
'cors' => [
'Origin' => ['https://www.myowndomain.com'],
]
],
//....
],
//....
];
Configure the user component to link oauth2 system and user / identity management
return [
//....
'components' => [
//....
'user' => [
'class' => 'sweelix\oauth2\server\web\User',
'identityClass' => 'app\models\User', // Identity class must implement UserModelInterface
//
// Parameters
//
],
//....
],
//....
];
IdentityClass
must implements sweelix\oauth2\server\interfaces\UserModelInterface
. You can use the trait
sweelix\oauth2\server\traits\IdentityTrait
to automagically implement
public function getRestrictedScopes()
public function setRestrictedScopes($scopes)
public static function findIdentityByAccessToken($token, $type = null)
you will have to implement the remaining methods :
public static function findByUsernameAndPassword($username, $password)
public static function findByUsername($username)
In order to use your own views (instead of the builtin ones), you can override
layout
: module parameter overrideLayout
viewPath
: module parameter overrideViewPath
You should create a classic layout like :
<?php
/**
* @app/views/layouts/newLayout.php
* @var string $content
*/
use yii\helpers\Html;
$this->beginPage(); ?>
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><?php echo Html::encode($this->title); ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php $this->head(); ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php echo $content;?>
<?php $this->endBody(); ?>
</body>
</html>
<?php $this->endPage();
and link it to the module
return [
//....
'modules' => [
//....
'oauth2' => [
'class' => 'sweelix\oauth2\server\Module',
'overrideLayout' => '@app/views/layouts/newLayout',
//
// Additional Parameters
//
],
//....
],
//....
];
You should create 3 views to allow oauth2 module to work as expected and link them to the module
return [
//....
'modules' => [
//....
'oauth2' => [
'class' => 'sweelix\oauth2\server\Module',
// use views in folder oauth2
'overrideViewPath' => '@app/views/oauth2',
//
// Additional Parameters
//
],
//....
],
//....
];
This view is used to display a page when an error occurs
<?php
/**
* error.php
*
* @var string $type error type
* @var string $description error description
*/
use yii\helpers\Html;
?>
<h1 class="alert-heading"><?php echo ($type ? : 'Unkown error'); ?></h1>
<p><?php echo ($description ? : 'Please check your request'); ?></p>
This view is used to display a login page when needed
<?php
/**
* login.php
*
* @var \sweelix\oauth2\server\forms\User $user
*
*/
use yii\helpers\Html;
?>
<?php echo Html::beginForm('', 'post', ['novalidate' => 'novalidate']); ?>
<label>Username</label>
<?php echo Html::activeTextInput($user, 'username', [
'required' => 'required',
]); ?>
<br/>
<label>Password</label>
<?php echo Html::activePasswordInput($user, 'password', [
'required' => 'required',
]); ?>
<br/>
<button type="submit">LOGIN</button>
<?php echo Html::endForm(); ?>
This view is used to display an authorization page when needed
<?php
/**
* authorize.php
*
* @var \sweelix\oauth2\server\interfaces\ScopeModelInterface[] $requestedScopes
* @var \sweelix\oauth2\server\interfaces\ClientModelInterface $client
*
*/
use yii\helpers\Html;
?>
<h1><?php echo $client->name ?> <span>requests access</span></h1>
<?php echo Html::beginForm(); ?>
<?php if(empty($requestedScopes) === false) : ?>
<ul>
<?php foreach($requestedScopes as $scope): ?>
<li>
<h4><?php echo $scope->id; ?></h4>
<p>
<?php echo $scope->definition; ?>
</p>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<!-- name of decline button **must be** decline -->
<button type="submit" name="decline">DECLINE</button>
<!-- name of accept button **must be** accept -->
<button type="submit" name="accept">AUTHORIZE</button>
<?php echo Html::endForm(); ?>
The Oauth2 Yii2 extension expose severall models which can be used in your application. All models can be overloaded using Yii2 DI.
For example, if you want to overload the Client
model, you have to inject your own model in the DI using:
Yii::$container->set('sweelix\oauth2\server\interfaces\ClientModelInterface', [
'class' => YourClientModel::className(),
]);
Client::findOne($id)
- Find client by IDClient::findAllByUserId($id)
- Find all clients accepted by user (userId)Client::findAll()
- Find all existing clients$client->save()
- Save client$client->delete()
- Delete client$client->hasUser($userId)
- Check if user (userId) has accepted the client$client->addUser($userId)
- Attach the user (userId) to the client$client->removeUser($userId)
- Dettach the user (userId) from the clientAccessToken::findOne($id)
- Find accessToken by IDAccessToken::findAllByUserId($id)
- Find all accessTokens for user (userId)AccessToken::findAllByClientId($id)
- Find all accessTokens for client (clientId)AccessToken::deleteAllByUserId($id)
- Delete all accessTokens for user (userId)AccessToken::deleteAllByClientId($id)
- Delete all accessTokens for client (clientId)AccessToken::findAll()
- Find all existing accessTokensAccessToken::deleteAllExpired()
- Delete all expired accessTokens$accessToken->save()
- Save accessToken$accessToken->delete()
- Delete accessTokenRefreshToken::findOne($id)
- Find accessToken by IDRefreshToken::findAllByUserId($id)
- Find all refreshTokens for user (userId)RefreshToken::findAllByClientId($id)
- Find all refreshTokens for client (clientId)RefreshToken::deleteAllByUserId($id)
- Delete all refreshTokens for user (userId)RefreshToken::deleteAllByClientId($id)
- Delete all refreshTokens for client (clientId)RefreshToken::findAll()
- Find all existing refreshTokensRefreshToken::deleteAllExpired()
- Delete all expired refreshTokens$refreshToken->save()
- Save refreshToken$refreshToken->delete()
- Delete refreshTokenAuthCode::findOne($id)
- Find authCode by ID$authCode->save()
- Save authCode$authCode->delete()
- Delete authCodeScope::findOne($id)
- Find scope by IDScope::findAvailableScopeIds()
- Find all scopes IDsScope::findDefaultScopeIds()
- Find default scopes IDs$scope->save()
- Save scope$scope->delete()
- Delete scopeCypherKey::findOne($id)
- Find cypherKey by ID$cypherKey->save()
- Save cypherKey$cypherKey->delete()
- Delete cypherKey$cypherKey->generateKeys()
- Generate random keys for current cypherKeyJti::findOne($id)
- Find jti by IDJti::findAllBySubject($id)
- Find all jtis for user (userId)Jti::findAllByClientId($id)
- Find all jtis for client (clientId)Jti::deleteAllBySubject($id)
- Delete all jtis for user (userId)Jti::deleteAllByClientId($id)
- Delete all jtis for client (clientId)Jti::findAll()
- Find all existing jtisJti::deleteAllExpired()
- Delete all expired jtisJti::getFingerprint($clientId, $subject, $audience, $expires, $jti)
- Get a jti fingerprint for given params$jti->save()
- Save jti$jti->delete()
- Delete jtiJwt::findOne($id)
- Find jwt by IDJwt::getFingerprint($clientId, $subject)
- Get jwt fingerprint for given clientId and subject$jwt->save()
- Save jwt$jwt->delete()
- Delete jwtUsing sweelix\oauth2\server\web\User
class will automagically link rbac
system and oauth2
system.
Permission system will be slightly modified to allow fine grained checks :
Yii::$app->user->can('read')
will check
read
is allowed for current clientread
is allowed for current user Yii::$app->user->can('rbac:read')
will check only if rbac permission read
is allowed for current user
Yii::$app->user->can('oauth2:read')
will check only if scope read
is allowed for current client
Before running the tests, you should edit the file tests/config/BACKEND.php and change the config to match your environment.
Several commands are available to manage oauth2 system
php protected/yii.php oauth2:client/create
php protected/yii.php oauth2:client/update
php protected/yii.php oauth2:client/delete
php protected/yii.php oauth2:jwt/create
php protected/yii.php oauth2:jwt/update
php protected/yii.php oauth2:jwt/delete
php protected/yii.php oauth2:key/create
php protected/yii.php oauth2:key/update
php protected/yii.php oauth2:key/delete
php protected/yii.php oauth2:scope/create
php protected/yii.php oauth2:scope/update
php protected/yii.php oauth2:scope/delete
php protected/yii.php oauth2:cronjob/remove-expired
- Run this one with your cron managerphp protected/yii.php oauth2:migrate-redis/migrate
- Migration command for redisYii::trace()
to Yii::debug()
className()
to ::class
HHVM
and php 5.6
.gitattributes
fxp-asset
findAllByUserId()
and findAllByClientId()
for models AccessToken
and RefreshToken
hasUser()
, addUser()
, removeUser()
and findAllByUserId()
from model Client
user
to be fully decoupled from app user
AccessToken
, RefreshToken
and AuthorizationCode
CORS
support for token
endpointHttpBearerAuth
and QueryParamAuth
for method findIdentityByAccessToken
oauth2:client/create
use ,
as separatoroauth2:client/create
allowJwtAccessToken
typo (was allowJwtAccesToken
)
Comments