QuanKim/JwtAuth custom plugin for CakePHP
, (*1)
Installation
You can install this plugin into your CakePHP application using composer., (*2)
The recommended way to install composer packages is:, (*3)
composer require quankim/cakephp-jwt-auth
Usage
In your app's config/bootstrap.php
add:, (*4)
// In config/bootstrap.php
Plugin::load('QuanKim/JwtAuth');
or using cake's console:, (*5)
./bin/cake plugin load QuanKim/JwtAuth
Migrate AuthToken table:, (*6)
./bin/cake migrations migrate -p QuanKim/JwtAuth
Configuration:
Setup AuthComponent
:, (*7)
// In your controller, for e.g. src/Api/AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'storage' => 'Memory',
'authenticate', [
'QuanKim/JwtAuth.Jwt' => [
'userModel' => 'Users',
'fields' => [
'username' => 'id'
],
'parameter' => 'token',
// Boolean indicating whether the "sub" claim of JWT payload
// should be used to query the Users model and get user info.
// If set to `false` JWT's payload is directly returned.
'queryDatasource' => true,
]
],
'unauthorizedRedirect' => false,
'checkAuthIn' => 'Controller.initialize',
// If you don't have a login action in your application set
// 'loginAction' to false to prevent getting a MissingRouteException.
'loginAction' => false
]);
}
Setup Config/app.php
Add in bottom of file:, (*8)
'AuthToken'=>[
'expire'=>3600
]
Working
The authentication class checks for the token in two locations:, (*9)
-
HTTP_AUTHORIZATION
environment variable:, (*10)
It first checks if token is passed using Authorization
request header.
The value should be of form Bearer <token>
. The Authorization
header name
and token prefix Bearer
can be customzied using options header
and prefix
respectively., (*11)
Note: Some servers don't populate $_SERVER['HTTP_AUTHORIZATION']
when
Authorization
header is set. So it's upto you to ensure that either
$_SERVER['HTTP_AUTHORIZATION']
or $_ENV['HTTP_AUTHORIZATION']
is set., (*12)
For e.g. for apache you could use the following:, (*13)
RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
-
The query string variable specified using parameter
config:, (*14)
Next it checks if the token is present in query string. The default variable
name is token
and can be customzied by using the parameter
config shown
above., (*15)
Token Generation
You can use \Firebase\JWT\JWT::encode()
of the firebase/php-jwt
lib, which this plugin depends on, to generate tokens., (*16)
The payload should have the "sub" (subject) claim whos value is used to query the
Users model and find record matching the "id" field., (*17)
Example:, (*18)
$access_token = JWT::encode([
'sub' => $user['id'],
'exp' => time() + $expire
],Security::salt());
$refresh_token = JWT::encode([
'sub' => $user['id'],
'ref'=>time()
],Security::salt());
$authToken = $this->Users->AuthToken->newEntity();
$authToken->user_id = $user['id'];
$authToken->access_token = $access_token;
$authToken->refresh_token = $refresh_token;
$this->Users->AuthToken->save($authToken);
$this->set([
'success' => true,
'data' => [
'access_token' => $access_token,
'refresh_token'=> $refresh_token,
'id'=>$user['id'],
'username'=> $user['username'],
'email'=> $user['email']
],
'_serialize' => ['success', 'data']
]);
You can set the queryDatasource
option to false
to directly return the token's
payload as user info without querying datasource for matching user record., (*19)
Further reading
For an end to end usage example check out this blog post by Bravo Kernel., (*20)