I have created a few tutorials on how to use Firebase JWT to create and use JSON Web Tokens, but I decided to try to do it without a framework, using just PHP. In this article, you can see how I created my own simple JWT generator.
View This On YouTube
Creating Our Class
class JWT {
private $headers;
private $secret;
public function __construct()
{
$this->headers = [
'alg' => 'HS256', // we are using a SHA256 algorithm
'typ' => 'JWT', // JWT type
'iss' => 'jwt.local', // token issuer
'aud' => 'example.com' // token audience
];
$this->secret = 'thisIsASecret';
}
}
Generate A Token
Within this class, we will create our generate function. this function will generate our token to be used against our validator.
public function generate(array $payload): string
{
$headers = $this->encode(json_encode($this->headers)); // encode headers
$payload["exp"] = time() + 60; // add expiration to payload
$payload = $this->encode(json_encode($payload)); // encode payload
$signature = hash_hmac('SHA256', "$headers.$payload", $this->secret, true); // create SHA256 signature
$signature = $this->encode($signature); // encode signature
return "$headers.$payload.$signature";
}
Within our generate function, we reference a encode function. This function will simply base64 encode our strings to be used by the token.
private function encode(string $str): string
{
return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); // base64 encode string
}
Validate Our Token
In this function, we take the generated token, and validate the strings, the encoding, and finally the time. I have it set for 1 minute so the token will expire after 1 minute to ensure it is used for a purpose.
public function is_valid(string $jwt): bool
{
$token = explode('.', $jwt); // explode token based on JWT breaks
if (!isset($token[1]) && !isset($token[2])) {
return false; // fails if the header and payload is not set
}
$headers = base64_decode($token[0]); // decode header, create variable
$payload = base64_decode($token[1]); // decode payload, create variable
$clientSignature = $token[2]; // create variable for signature
if (!json_decode($payload)) {
return false; // fails if payload does not decode
}
if ((json_decode($payload)->exp - time()) < 0) {
return false; // fails if expiration is greater than 0, setup for 1 minute
}
if (isset(json_decode($payload)->iss)) {
if (json_decode($headers)->iss != json_decode($payload)->iss) {
return false; // fails if issuers are not the same
}
} else {
return false; // fails if issuer is not set
}
if (isset(json_decode($payload)->aud)) {
if (json_decode($headers)->aud != json_decode($payload)->aud) {
return false; // fails if audiences are not the same
}
} else {
return false; // fails if audience is not set
}
$base64_header = $this->encode($headers);
$base64_payload = $this->encode($payload);
$signature = hash_hmac('SHA256', $base64_header . "." . $base64_payload, $this->secret, true);
$base64_signature = $this->encode($signature);
return ($base64_signature === $clientSignature);
}
Putting It All Together
class JWT
{
private $headers;
private $secret;
public function __construct()
{
$this->headers = [
'alg' => 'HS256', // we are using a SHA256 algorithm
'typ' => 'JWT', // JWT type
'iss' => 'jwt.local', // token issuer
'aud' => 'example.com' // token audience
];
$this->secret = 'thisIsASecret'; // change this to your secret code
}
public function generate(array $payload): string
{
$headers = $this->encode(json_encode($this->headers)); // encode headers
$payload["exp"] = time() + 60; // add expiration to payload
$payload = $this->encode(json_encode($payload)); // encode payload
$signature = hash_hmac('SHA256', "$headers.$payload", $this->secret, true); // create SHA256 signature
$signature = $this->encode($signature); // encode signature
return "$headers.$payload.$signature";
}
private function encode(string $str): string
{
return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); // base64 encode string
}
public function is_valid(string $jwt): bool
{
$token = explode('.', $jwt); // explode token based on JWT breaks
if (!isset($token[1]) && !isset($token[2])) {
return false; // fails if the header and payload is not set
}
$headers = base64_decode($token[0]); // decode header, create variable
$payload = base64_decode($token[1]); // decode payload, create variable
$clientSignature = $token[2]; // create variable for signature
if (!json_decode($payload)) {
return false; // fails if payload does not decode
}
if ((json_decode($payload)->exp - time()) < 0) {
return false; // fails if expiration is greater than 0, setup for 1 minute
}
if (isset(json_decode($payload)->iss)) {
if (json_decode($headers)->iss != json_decode($payload)->iss) {
return false; // fails if issuers are not the same
}
} else {
return false; // fails if issuer is not set
}
if (isset(json_decode($payload)->aud)) {
if (json_decode($headers)->aud != json_decode($payload)->aud) {
return false; // fails if audiences are not the same
}
} else {
return false; // fails if audience is not set
}
$base64_header = $this->encode($headers);
$base64_payload = $this->encode($payload);
$signature = hash_hmac('SHA256', $base64_header . "." . $base64_payload, $this->secret, true);
$base64_signature = $this->encode($signature);
return ($base64_signature === $clientSignature);
}
}
Conclusion
It is a simple project, but works as intended. I have used Firebase JWT in the past but sometimes you just need a simple solution without having to import and entire library. I hope this helps for your next small project. You can download the class on my GitHub or subscribe on YouTube for more tutorials.
Read more articles on DevDrawer
Top comments (2)
This is a good minified JWT authentication good job.
Will be grate if you could explain how to connect it to a CRUD REST API for authorisation purposes.
I think one of my previous video tutorials may be helpful with this:
Create JSON Using PHP and MySQL, A Simple API youtu.be/m8rnFHd2zcY
JS Fetch with PHP RESTful API, Login Authentication, youtu.be/5ujpgk4oe3c
I don't have a written tutorial, but those 2 videos should go over creating a simple API then using it for authentication.
I hope that helps.