JWT Authentication with Policier
Introductionβ
Policier lets you validate requests using JWT (JSON Web Tokens).
Installationβ
To install the package, you need to use composer (the PHP 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
*/
"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 loads 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β
Quickly encode a token:
$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 method __toString. You can get the expiration time with expireIn and use 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 methods
getHeaders() / getHeader($name) / getClaim($name) / getClaims() to
extract its information:
$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 against 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()). As its second argument, the
validate method takes that identifier β 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-level 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 are using 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 to 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.