HotJot
, (*1)
No-frills JWT & JWS library., (*2)
Installation
Install with composer:, (*3)
$ composer require ignislabs/hotjot
Requirements
- PHP >= 7.1
- OpenSSL extension
- JSON extension
Usage
Creating, verifying and validating tokens is really simple, let's take
a quick look at these operations before we dive to each component in
more detail., (*4)
Create a token:, (*5)
$token = $factory->create($claims, $headers);
Verify a token:, (*6)
$signer->verify($token);
Validate a token:, (*7)
$validator->validate($token);
Let's take a look at the signers first, as these are the most important
part of the library. You need a signer to create signed tokens and
verify them., (*8)
Signers
You can choose between HMAC
, RSA
or None
signers., (*9)
HMAC
Signers
HMAC are the simplest ones. It's a symmetric algorithm, which means
you only have a single private encryption key. You should try to make
this as cryptographically secure and random as possible., (*10)
You have 3 different options: HS256
, HS384
, and HS512
. All three
require only an encryption key as a constructor parameter., (*11)
$signer = new \IgnisLabs\HotJot\Signer\HMAC\HS512('encryption key');
RSA
Signers
RSA is asymmetric, which means you'll need to create a key pair:, (*12)
# create a strong, password protected private key
$ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -outform PEM -out private.pem -pass stdin
# get public key
$ openssl rsa -pubout -in private.pem -out public.pem
If you don't want to generate a password protected key, just omit
-pass stdin
., (*13)
You can, if you need/want to, make your public key publicly available
so anyone can use it to verify if the token is really signed by you
., (*14)
Again, you have 3 different options: RS256
, RS384
, and RS512
. All
three require both private and public keys, and the passphrase if your
private key is protected., (*15)
$privateKey = file_get_contents('/path/to/private.pem');
$publicKey = file_get_contents('/path/to/public.pem');
$signer = new \IgnisLabs\HotJot\Signer\RSA\RS512($privateKey, $publicKey, 'key passphrase (if any)');
The private key is used for signing and the public key for
verification., (*16)
None
Signer
Using the None
signer will result in an unsecured token with no
signature, and verification with this signer will always fail, as
unsecured tokens are not signed., (*17)
Warning! Even though you technically can create unsecured tokens,
you should be really careful and know very well what you're doing., (*18)
This signer doesn't require any parameters, as it can't sign or verify.
It will always return an empty string as a signature, and verification
will always fail., (*19)
$signer = new \IgnisLabs\HotJot\Signer\None;
Token Creation
Now that you know about signers, let's see how can you create tokens., (*20)
To create tokens you'll need the Factory
and a Signer
, and you'll
get a Token
object with a few handy methods., (*21)
Creating Secured Tokens
To crete secured tokens, use any signer except None
., (*22)
$signer = new \IgnisLabs\HotJot\Signer\HMAC\HS512('encryption key');
$factory = new \IgnisLabs\HotJot\Factory($signer);
$token = $factory->create([
'iss' => 'http://api.example.com',
'aud' => 'http://www.example.com',
'jti' => bin2hex(random_bytes(16)),
'exp' => (new DateTime('+10 days'))->getTimestamp(),
// etc...
]);
$token->getHeader('alg'); // -> 'HS512'
$token->getClaim('iss'); // -> 'http://api.example.com'
$token->getClaim('exp'); // -> DateTime object
As you can see, exp
returns a DateTime
object, and so will iat
and
nbf
., (*23)
If you need to use a different signer for some reason, you can do it
like this:, (*24)
$newFactory = $factory->setSigner($anotherSigner);
The factory is immutable, so when you do this, the current factory
instance is not modified, instead a new instance is returned with the
new signer., (*25)
This is useful when you want to temporarily change the signature for a
special use case., (*26)
Creating Unsecured Tokens
To create unsecured tokens you need to use the None
signer., (*27)
Warning! Even though you technically can create unsecured tokens,
you should be really careful and know very well what you're doing., (*28)
$signer = new \IgnisLabs\HotJot\Signer\None;
$factory = new \IgnisLabs\HotJot\Factory($signer);
$token = $factory->create([
'iss' => 'http://api.example.com',
'aud' => 'http://www.example.com',
'jti' => bin2hex(random_bytes(16)),
'exp' => (new DateTime('+10 days'))->getTimestamp(),
// etc...
]);
$token->getClaim('alg'); // -> 'none'
$token->getSignature(); // -> null
Parsing
You can parse encoded token strings with the parser. How you obtain the
encoded token is out of the scope of the library (authorization header,
query parameter, etc)., (*29)
When you parse an encoded token, you'll get back a Token
object, same
one as with the Factory
., (*30)
$parser = new \IgnisLabs\HotJot\Parser;
$token = $parser->parse($encodedTokenString);
The parser does not verify or validate the token, as long as it can
parse it and it's rfc-compliant, the parser will succeed and return the
token object. You'll need to use a Signer and the Validator to verify
and validate the token., (*31)
If the parser does fail it will throw an InvalidTokenException
with
the appropriate message., (*32)
Signature Verification
This is a critical step when receiving tokens from the outside world., (*33)
This library does not automatically set any algorithm based on the
alg
header, and you shouldn't do this either. By following this
simple rule you will avoid [known vulnerabilities][1]., (*34)
This library makes it easy not to fall for this exploits by simply
requiring you to instantiate the desired signer yourself, and making a
hard association between the keys and the signers by passing keys on
instantiation rather than on verification, leaving less room for error., (*35)
All signers will first check the token's alg
header and check if it
matches the signer's algorithm. If the algorithms don't match it will
throw a SignatureVerificationException
exception., (*36)
$signer = new \IgnisLabs\HotJot\Signer\RSA\RS512($privateKey, $publicKey, 'passphrase');
$signer->verify($token); // -> boolean โ $token most likely obtained through the parser
Validation
Once you have a verified token, you can start to validate it using the
Validator
., (*37)
The Validator
is a really simple class that takes a bunch of token
validators and uses them to validate a token. The validators don't
return eny values, but throw exceptions on failure., (*38)
This library already comes with some useful ones, but you can add as
many as you need., (*39)
use IgnisLabs\HotJot\Validators as ๐ต;
$validator = new \IgnisLabs\HotJot\Validator(
new ๐ต\IssuedAtValidator, // fails if token used before `iat`
new ๐ต\NotBeforeValidator, // fails if token used before `nbf`
new ๐ต\ExpiresAtValidator // fails if token is used after `exp`
);
$validator->validate($token);
If you want to make any of these validators be required, you can
instantiate them like this:, (*40)
use IgnisLabs\HotJot\Validators as ๐ต;
$validator = new \IgnisLabs\HotJot\Validator(
new ๐ต\IssuedAtValidator(true),
new ๐ต\NotBeforeValidator(true),
new ๐ต\ExpiresAtValidator(true)
);
$validator->validate($token);
You can create your own validators, you just need them to implement the
IgnisLabs\HotJot\Contracts\TokenValidator
contract. You also have the
\IgnisLabs\HotJot\Validators\ClaimRequiredTrait
to save you some time
when creating required validators., (*41)
Algorithms
:heavy_check_mark: none
:heavy_check_mark: HS256
:heavy_check_mark: HS384
:heavy_check_mark: HS512
:heavy_check_mark: RS256
:heavy_check_mark: RS384
:heavy_check_mark: RS512
:black_square_button: ES256
:black_square_button: ES384
:black_square_button: ES512, (*42)