Subversion Repositories php_utils

Compare Revisions

Regard whitespace Rev 4 → Rev 5

/trunk/decode_jwt_token.inc.php
0,0 → 1,122
<?php
 
/*
* JWT Decoder for PHP
* Copyright 2021 Daniel Marschall, ViaThinkSoft
* Version 2021-05-15
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
 
function decode_idtoken($id_token, $verification_certs=null, $allowed_algorithms = array()) {
// Parts taken and simplified from https://github.com/firebase/php-jwt , licensed by BSD-3-clause
// Here is a great page for encode and decode tokens for testing: https://jwt.io/
 
$parts = explode('.', $id_token);
if (count($parts) === 5) return false; // encrypted JWT not yet supported
if (count($parts) !== 3) return false;
list($header_base64, $payload_base64, $signature_base64) = $parts;
 
$header_ary = json_decode(urlsafeB64Decode($header_base64),true);
if ($header_ary['typ'] !== 'JWT') return false;
 
if ($verification_certs) {
$key = isset($header_ary['kid']) ? $verification_certs[$header_ary['kid']] : $verification_certs;
 
$msg = $header_base64.'.'.$payload_base64;
$signature = urlsafeB64Decode($signature_base64);
 
$jwt_algo = $header_ary['alg'];
 
// see https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
// https://datatracker.ietf.org/doc/html/rfc8725#section-3.1
if (!in_array($jwt_algo, $allowed_algorithms)) return false;
 
if ($jwt_algo != 'none') {
$php_algo = 'SHA'.substr($jwt_algo,2,3);
switch (substr($jwt_algo,0,2)) {
case 'ES':
// OpenSSL expects an ASN.1 DER sequence for ES256 signatures
$signature = signatureToDER($signature);
if (!function_exists('openssl_verify')) break; // if OpenSSL is not installed, we just accept the JWT
if (!openssl_verify($msg, $signature, $key, $php_algo)) return false;
break;
case 'RS':
if (!function_exists('openssl_verify')) break; // if OpenSSL is not installed, we just accept the JWT
if (!openssl_verify($msg, $signature, $key, $php_algo)) return false;
break;
case 'HS':
$hash = @hash_hmac($php_algo, $msg, $key, true);
if (!$hash) break; // if the hash algo is not available, we just accept the JWT
if (!hash_equals($signature, $hash)) return false;
break;
case 'PS':
// This feature is new and not yet available in php-jwt
file_put_contents($msg_file = tempnam("/tmp", ""), $msg);
file_put_contents($sig_file = tempnam("/tmp", ""), $signature);
file_put_contents($key_file = tempnam("/tmp", ""), $key);
$ec = -1;
$out = array();
$cmd = "openssl dgst -".strtolower($php_algo)." -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -verify ".escapeshellarg($key_file)." -signature ".escapeshellarg($sig_file)." ".escapeshellarg($msg_file);
$cmd .= (strtoupper(substr(PHP_OS,0,3)) === 'WIN') ? ' 2> NUL' : ' 2> /dev/null';
exec($cmd, $out, $ec);
unlink($msg_file);
unlink($sig_file);
unlink($key_file);
if (($ec !== 0) && (count($out) === 0)) break; // If OpenSSL is not found, we just accept the JWT
if (($ec !== 0) || (strpos(implode("\n",$out),"Verified OK") === false)) return false;
break;
default:
return false;
}
}
}
 
$payload_ary = json_decode(urlsafeB64Decode($payload_base64), true);
 
$leeway = 60; // 1 Minute
if (isset($payload_ary['nbf']) && (time()+$leeway<$payload_ary['nbf'])) return false;
if (isset($payload_ary['exp']) && (time()-$leeway>$payload_ary['exp'])) return false;
 
return $payload_ary;
}
 
function urlsafeB64Decode($input) {
// Taken from https://github.com/firebase/php-jwt , licensed by BSD-3-clause
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
 
function signatureToDER($sig) {
// Taken from https://github.com/firebase/php-jwt , licensed by BSD-3-clause, modified
 
// Separate the signature into r-value and s-value
list($r, $s) = str_split($sig, (int) (strlen($sig) / 2));
 
// Trim leading zeros
$r = ltrim($r, "\x00");
$s = ltrim($s, "\x00");
 
// Convert r-value and s-value from unsigned big-endian integers to signed two's complement
if (ord($r[0]) > 0x7f) $r = "\x00" . $r;
if (ord($s[0]) > 0x7f) $s = "\x00" . $s;
 
$der_r = chr(0x00/*primitive*/ | 0x02/*INTEGER*/).chr(strlen($r)).$r;
$der_s = chr(0x00/*primitive*/ | 0x02/*INTEGER*/).chr(strlen($s)).$s;
$der = chr(0x20/*constructed*/ | 0x10/*SEQUENCE*/).chr(strlen($der_r.$der_s)).$der_r.$der_s;
return $der;
}