JWT Authentication with Policier
Introductionβ
Policier lets you validate requests using JWT (JSON Web Tokens).
Installationβ
To install the package, you must use composer (PHP's package manager) like this:
composer require bowphp/policier
Configurationβ
You can review all the configuration options here.
return [
/**
* Token expiration time
*/
"exp" => 3600,
/**
* The token is usable after this time
*/
"nbf" => 60,
/**
* The token was issued at
*/
"iat" => 60,
/**
* Configure the issuer
*/
"iss" => "localhost",
/**
* Configure the audience
*/
"aud" => "localhost",
/**
* Hashing algorithm used
*
* HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512,
*/
"alg" => "HS512",
/**
* Your signature; this field is required for all hashing types except RSA
*/
'signkey' => null,
/**
* Signature using your RSA, which is loaded automatically if the hashing key is of RSA type
*/
"keychain" => [
/**
* Path to your private key
*/
"private" => null,
/**
* Path to your public key
*/
"public" => null
]
];
Usageβ
Policier is very simple to use and has a clear API. The configuration returns a singleton.
use Policier\Policier;
$configure = require "/path/to/config/file.php";
$policier = Policier::configure($configure);
You can also do it like this:
use Policier\Policier;
$configure = require "/path/to/config/file.php";
Policier::configure($configure);
$policier = Policier::getInstance();
After configuration, you can use the policier helper:
policier($action, ...$args);
The action value must be one of these values: encode, decode, parse, verify, validate.
Updating or Retrieving the Configurationβ
Updating the Configurationβ
You can update the base configuration with the setConfig method.
It takes an array of keys to merge with the existing configuration:
$policier->setConfig([
'exp' => time() + 72000,
'iss' => 'api.example.com',
]);
Retrieving the Configurationβ
You can also get configuration information with the getConfig method:
$policier->getConfig('exp');
Encoding a Tokenβ
Encode a token quickly:
$id = uniqid();
$claims = [
"name" => "Franck",
"nickname" => "papac",
"logged" => true
];
$token = $policier->encode($id, $claims);
$token->expireIn(); // Expired In
$token->getToken(); // Token value
echo $token;
//=> eyJ0eX...OiJKV1.eyJpc3Mi...OiJsb2.l7v0bS0r...qnK1IeR
$token is an instance of Policier\Token and implements the magic __toString method. You can get the expiration time with expireIn and getToken to retrieve the token value.
Via the helper:
policier('encode', $id, $claims);
Decoding a Tokenβ
decode() returns an instance of Policier\Token. Use its
getHeaders() / getHeader($name) / getClaim($name) / getClaims() methods to
extract information from it:
$result = $policier->decode($token);
$result->getHeaders(); // array
echo $result->getClaim('name'); // => Franck
Via the helper:
policier('decode', $token);
Parsing a Tokenβ
$token = $policier->parse($token);
$token->hasHeader("old") // Check if the header exists
$token->getHeader("alg", $default = null); // Get a header
$token->getHeaders(); // Get all headers
$token->hasClaim("name") // Check if the claim exists
$token->getClaim("name", $default = null); // Get a claim
$token->getClaims(); // Get all claims
$token->isExpired(); // Check if the token has expired
echo $token->getClaim("name");
//=> Franck
Via the helper:
policier('parse', $token);
Verifying a Tokenβ
Check whether the token is valid with all JWT attributes.
$verified = $policier->verify($token);
if ($verified) {
echo "Token est valide";
} else {
echo "Token n'est pas valide";
}
Via the helper:
policier('verify', $token);
Validating a Tokenβ
Check both the token's signature and that it actually belongs to a given
subject (the same $id you passed to encode()). The validate method
takes this identifier as its second argument β not an array of claims.
$id = $token->getClaim('jti'); // or any other identifier you used
$validated = $policier->validate((string) $token, $id);
if ($validated) {
echo "Token valide pour le sujet {$id}";
} else {
echo "Token invalide ou expirΓ©";
}
Via the helper:
policier('validate', $token, $id);
If you also want to check application-side claims (name, role, etc.),
do the comparison yourself after decode():
$decoded = $policier->decode($token);
if ($decoded->getClaim('nickname') !== 'papac') {
// unexpected claim
}
BowPHP and Policierβ
If you use BowPHP, you can use the configuration plugin Policier\Bow\PolicierConfiguration::class and the middleware Policier\Bow\PolicierMiddleware::class.
Wire up the configuration in app\Kernel.php:
public function middlewares()
{
return [
...
'policier' => \Policier\Bow\PolicierMiddleware::class,
...
];
}
public function configurations()
{
return [
...
\Policier\Bow\PolicierConfiguration::class,
...
];
}
Use the middleware:
$app->get('/api', function () {
$token = policier()->getToken();
})->middleware('policier');
The token has been parsed into the Policier instance during the middleware process via the plug method. After the middleware runs, you can:
- Get the token with
getToken - Decode the token with
getDecodeToken - Parse the token with
getParsedToken
Customizing the Middlewareβ
You can create another middleware that extends the default middleware Policier\Bow\PolicierMiddleware::class. This gives you the ability to change the error messages by overriding the getUnauthorizedMessage, getExpirationMessage, getExpirationStatusCode, and getUnauthorizedStatusCode methods.
php bow add:middleware CustomPolicierMiddleware
And then, you can do this:
use Bow\Http\Request;
use Policier\Bow\PolicierMiddleware;
class CustomPolicierMiddleware extends PolicierMiddleware
{
/**
* Get the error message
*
* @return array
*/
public function getUnauthorizedMessage()
{
return [
'message' => 'unauthorized',
'error' => true
];
}
/**
* Get the token expiration message
*
* @return array
*/
public function getExpirationMessage()
{
return [
'message' => 'token is expired',
'expired' => true,
'error' => true
];
}
/**
* Get the unauthorized response code
*
* @return int
*/
public function getUnauthorizedStatusCode()
{
return 403;
}
/**
* Get the response code for expiration
*
* @return int
*/
public function getExpirationStatusCode()
{
return 403;
}
}
Publishing the middlewareβ
To publish the custom middleware and override Policier's default one, simply add the middleware in the app/Kernel.php file with the policier key.
public function middlewares()
{
return [
...
'policier' => \App\Middleware\CustomPolicierMiddleware::class,
...
];
}
Is something missing?
If you run into problems with the documentation or have suggestions to improve the documentation or the project in general, please open an issue for us, or send a tweet mentioning the Twitter account @bowframework or directly on github.