Hi Yiiers,
I am implementing a Yii2 basic template to become a REST API Resource server
in the Oauth2 schema, such as:
.
The client app using Angular17, where after successfully logging in from the Authorization Server, it gets a JWT
which will be used as a bearer token to the Yii2 application that I develop now.
In those JWT, there is a sub
as a user ID and roles
in the form of a string array.
This is those JWT payload.
{
"roles": [
"frontend-developer",
"admin"
],
"id": "8f68d4f824a8c4836244ba8e9ec1d75b5ca13a21",
"jti": "8f68d4f824a8c4836244ba8e9ec1d75b5ca13a21",
"iss": "http://localhost:1209",
"aud": "main-frontend",
"sub": 10,
"exp": 1708067453,
"iat": 1707981053,
"token_type": "bearer",
"scope": "openid profile offline_access email username"
}
I use yii\rest\controller
to handle resources, not yii\rest\ActiveController
for OpenAPI support. So the code looks like this:
class RestController extends Controller
{
/*
* Use function apache_request_headers() to see all headers
* */
use RestTrait;
}
trait RestTrait
{
/**
* @return array
*/
public function behaviors(): array
{
$behaviors = parent::behaviors();
// remove authentication filter,
unset($behaviors['authenticator']);
// add CORS filter
$behaviors['corsFilter'] = [
'class' => yii\filters\Cors::class,
'cors' => [
// 'Origin' => ['*'], // Already defined in 000.default.conf virtualhost
// 'Access-Control-Request-Headers' => ['*'], // Already defined in 000.default.conf virtualhost
// 'Access-Control-Allow-Credentials' => true, // Already defined in 000.default.conf virtualhost
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
'Access-Control-Max-Age' => 86400,
'Access-Control-Expose-Headers' => ['*'],
]
];
// re-add authentication filter
$behaviors['authenticator'] = [
'class' => bizley\jwt\JwtHttpBearerAuth::class,
];
// avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
$behaviors['authenticator']['except'] = ['options'];
return $behaviors;
}
public function actionOptions(): bool
{
\Yii::$app->response->setStatusCode(200);
return true;
}
}
Now, The following controller is use of the controller above.
class AgamaController extends RestController
{
/**
* {@inheritdoc}
*/
protected function verbs(): array
{
return [
'index' => ['GET', 'HEAD', 'OPTIONS'],
'view' => ['GET', 'HEAD', 'OPTIONS'],
'create' => ['POST'],
'update' => ['POST', 'PUT'],
'delete' => ['DELETE'],
];
}
/**
* List of Agama, docs by OpenAPI - Swagger
* @return ActiveDataProvider
*/
#[OA\Get(
path: "/v1/agama",
description: "By default, It will return all Agama records.",
summary: "Retrieves the collection of Agama resources",
security: [
new OA\SecurityScheme(
ref: "#/components/securitySchemes/AgamaAuth"
)
],
tags: ["agama"],
parameters: [
new OA\QueryParameter(ref: '#/components/parameters/page'),
new OA\QueryParameter(ref: '#/components/parameters/per-page'),
new OA\QueryParameter(ref: '#/components/parameters/sort'),
new OA\QueryParameter(ref: '#/components/parameters/AgamaSearch[id]'),
new OA\QueryParameter(ref: '#/components/parameters/AgamaSearch[nama]'),
new OA\QueryParameter(ref: '#/components/parameters/AgamaSearch[alias]'),
],
responses: [
new OA\Response(
ref: '#/components/responses/AgamaArray',
response: HttpCode::OK,
)
]
)]
public function actionIndex(): ActiveDataProvider
{
// THIS ACTION ONLY FOR `admin` roles to pass. The others must be FORBIDDEN.
$searchModel = new AgamaSearch();
return $searchModel->search(Yii::$app->request->queryParams);
}
}
So to set and get User
data in Yii2 as beforeAction
from Authenticator
, we can use the User
model as follows.
class User extends BaseObject implements IdentityInterface
{
/**
* {@inheritdoc}
* @param $token
* @param null $type
* @return IdentityInterface|User|null
* @throws InvalidConfigException
*/
public static function findIdentityByAccessToken($token, $type = null): User|IdentityInterface|null
{
if (Yii::$app->jwt->validate($token)) {
$user = new static();
$user->setId( Yii::$app->jwt->parse($token)->claims()->get('sub'));
$user->setAccessToken($token);
// $auth = Yii::$app->authManager;
// $auth->createRole('super-admin');
// $authorRole = $auth->getRole('super-admin');
// $auth->assign($authorRole, $user->getId());
return $user;
}
return null;
}
What we want to achieve is, with data from the JWT payload
from the frontend which contains role
, we can immediately check whether the user
can carry out a certain action in a simple way, of course with Yii2 way
.
My Question, What do you suggest to handle the role I get from the JWT payload sent from the frontend.
- Is it possible to use an RBAC role, such as
Yii::$app->user->can('admin')
, even without having a role for the user in the database, or is it necessary to use ` ‘yii\rbac\PhpManager’? - Or use
Access Control Filter
on a controller that extends theRestController
class that I defined above? - Or use
checkAccess
method ? , then how to implement it in code that extends fromyii\rest\controller
?
Or do I need to check again on the Authentication server regarding the authorization?
Please guide.
Thank You