Created
September 15, 2017 09:38
-
-
Save nilesolutions/43513320483f990d3fea89a57b9869df to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Created by PhpStorm. | |
* User: thiphariel | |
* Date: 15/08/17 | |
* Time: 19:40 | |
*/ | |
namespace App\Api; | |
use Exception; | |
class Token | |
{ | |
/** | |
* @var array | |
*/ | |
protected static $supportedAlgs = [ | |
'HS256' => ['hash_hmac', 'SHA256'], | |
'HS512' => ['hash_hmac', 'SHA512'], | |
'HS384' => ['hash_hmac', 'SHA384'], | |
]; | |
/** | |
* Generate a JWT | |
* @param array $payload | |
* @param string $key | |
* @param string $alg | |
* @return string | |
* @throws Exception | |
*/ | |
public static function encode(array $payload, string $key, string $alg = 'HS256'): string | |
{ | |
$header = [ | |
'typ' => 'JWT', | |
'alg' => $alg | |
]; | |
$segments = []; | |
$segments[] = static::base64($header); | |
$segments[] = static::base64($payload); | |
$msg = implode('.', $segments); | |
// Creating the signature, a hash with the s256 algorithm and the secret key. The signature is also base64 encoded. | |
$signature = static::sign($msg, $key, $alg); | |
if (empty($signature)) { | |
throw new Exception('Signature null'); | |
} | |
$segments[] = static::base64($signature); | |
return implode('.', $segments); | |
} | |
public static function decode(string $token, string $key, array $allowedAlgs = []): string | |
{ | |
// Split a string by '.' | |
$segments = explode('.', $token); | |
// Throw an exception if we don't have 3 segments | |
if (count($segments) != 3) { | |
throw new Exception('Wrong number of segments'); | |
} | |
list($headb64, $payloadb64, $signb64) = $segments; | |
$header = json_decode(base64_decode($headb64)); | |
if (empty($header->alg)) { | |
throw new Exception('Empty algorithm'); | |
} | |
if (empty(static::$supportedAlgs[$header->alg])) { | |
throw new Exception('Algorithm not supported'); | |
} | |
if (!in_array($header->alg, $allowedAlgs)) { | |
throw new Exception('Algorithm not allowed'); | |
} | |
// Check the signature | |
if (!static::verify("$headb64.$payloadb64", base64_decode($signb64), $key, $header->alg)) { | |
throw new Exception('Signature verification failed'); | |
} | |
$payload = base64_decode($payloadb64); | |
// Check if this token has expired. | |
$expired = json_decode($payload)->exp; | |
if (isset($expired) && time() >= $expired) { | |
throw new Exception('Expired token'); | |
} | |
return $payload; | |
} | |
/** | |
* Creating the signature, a hash with the s256 algorithm and the secret key. | |
* The signature is also base64 encoded. | |
* @param string $msg | |
* @param string $key | |
* @param string $alg | |
* @return string|null | |
* @throws Exception | |
*/ | |
private static function sign(string $msg, string $key, string $alg = 'HS256'): ?string | |
{ | |
if (empty(static::$supportedAlgs[$alg])) { | |
throw new Exception('Algorithm not supported'); | |
} | |
list($function, $algorithm) = static::$supportedAlgs[$alg]; | |
switch ($function) { | |
case 'hash_hmac': | |
return hash_hmac($algorithm, $msg, $key, true); | |
} | |
return null; | |
} | |
/** | |
* @param string $msg | |
* @param string $signature | |
* @param string $key | |
* @param string $alg | |
* @return string | |
* @throws Exception | |
*/ | |
private static function verify(string $msg, string $signature, string $key, string $alg): string | |
{ | |
if (empty(static::$supportedAlgs[$alg])) { | |
throw new Exception('Algorithm not supported'); | |
} | |
list($function, $algorithm) = static::$supportedAlgs[$alg]; | |
switch ($function) { | |
case 'hash_hmac': | |
default: | |
$hash = hash_hmac($algorithm, $msg, $key, true); | |
return hash_equals($signature, $hash); | |
} | |
} | |
/** | |
* @param array|string $msg | |
* @return string | |
*/ | |
private static function base64($msg): string | |
{ | |
if (is_array($msg)) { | |
$msg = json_encode($msg); | |
} | |
return base64_encode($msg); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment