TQ
dev.com

Blog about software development

Subscribe to RSS

JWT implementation in PHP

18 Mar 2016 - by 'Maurits van der Schee'

I did a basic implementation of a JWT authentication scheme in PHP. It has no dependencies, so you can simply incorporate the two functions below in any existing application. I have been writing about JavaScript Web Token security earlier this month. It is a token standard that is well described on JWT.io.

<?php

function getVerifiedClaims($token,$time,$leeway,$ttl,$algorithm,$secret) {
    $algorithms = array('HS256'=>'sha256','HS384'=>'sha384','HS512'=>'sha512');
    if (!isset($algorithms[$algorithm])) return false;
    $hmac = $algorithms[$algorithm];
    $token = explode('.',$token);
    if (count($token)<3) return false;
    $header = json_decode(base64_decode(strtr($token[0],'-_','+/')),true);
    if (!$secret) return false;
    if ($header['typ']!='JWT') return false;
    if ($header['alg']!=$algorithm) return false;
    $signature = bin2hex(base64_decode(strtr($token[2],'-_','+/')));
    if ($signature!=hash_hmac($hmac,"$token[0].$token[1]",$secret)) return false;
    $claims = json_decode(base64_decode(strtr($token[1],'-_','+/')),true);
    if (!$claims) return false;
    if (isset($claims['nbf']) && $time+$leeway<$claims['nbf']) return false;
    if (isset($claims['iat']) && $time+$leeway<$claims['iat']) return false;
    if (isset($claims['exp']) && $time-$leeway>$claims['exp']) return false;
    if (isset($claims['iat']) && !isset($claims['exp'])) {
        if ($time-$leeway>$claims['iat']+$ttl) return false;
    }
    return $claims;
}

function generateToken($claims,$time,$ttl,$algorithm,$secret) {
    $algorithms = array('HS256'=>'sha256','HS384'=>'sha384','HS512'=>'sha512');
    $header = array();
    $header['typ']='JWT';
    $header['alg']=$algorithm;
    $token = array();
    $token[0] = rtrim(strtr(base64_encode(json_encode((object)$header)),'+/','-_'),'=');
    $claims['iat'] = $time;
    $claims['exp'] = $time + $ttl;
    $token[1] = rtrim(strtr(base64_encode(json_encode((object)$claims)),'+/','-_'),'=');
    if (!isset($algorithms[$algorithm])) return false;
    $hmac = $algorithms[$algorithm];
    $signature = hash_hmac($hmac,"$token[0].$token[1]",$secret,true);
    $token[2] = rtrim(strtr(base64_encode($signature),'+/','-_'),'=');
    return implode('.',$token);
}

$algorithm = 'HS256';
$secret = 'secret';
$time = time();
$leeway = 5; // seconds
$ttl = 30; // seconds
$claims = array('sub'=>'1234567890','name'=>'John Doe','admin'=>true);

// test that the functions are working
$token = generateToken($claims,$time,$ttl,$algorithm,$secret);
echo "$token\n";
$claims = getVerifiedClaims($token,$time,$leeway,$ttl,$algorithm,$secret);
var_dump($claims);

Important notes

Note that this implementation supports "HS" (HMAC based) signature algorithm with "iat" (issued at), "nbf" (not before) and "exp" (expires) fields. It does NOT support the "RS" (RSA based) and "ES" (Eliptic Curve based) signature algorithms. It also does NOT check the "iss" (issuer), "sub" (subject), "aud" (audience), "jti" (JWT token identifier) or "kid" (key identifier) fields. Please read the documentation on JWT.io to find out whether or not that matters to you.

Better alternative

If you don't mind using a dependency (and composer) then you may prefer the lcobucci/jwt package by Luís Otávio Cobucci Oblonczyk as it is popular, well maintained and feature-complete.