: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
namespace WordfenceLS\Crypto;
use WordfenceLS\Controller_Time;
use WordfenceLS\Model_Crypto;
* @package Wordfence2FA\Crypto
* @property array $payload
* @property int $expiration
* Decodes and returns the payload of a JWT. This also validates the signature and expiration. Currently assumes HS256 JWTs.
* @return Model_JWT|bool The decoded JWT or false if the token is invalid or fails validation.
public static function decode_jwt($token) {
$components = explode('.', $token);
if (count($components) != 3) {
$key = Model_Crypto::shared_hash_secret();
$body = $components[0] . '.' . $components[1];
$signature = hash_hmac('sha256', $body, $key, true);
$testSignature = self::base64url_decode($components[2]);
if (!hash_equals($signature, $testSignature)) {
$json = self::base64url_decode($components[1]);
$payload = @json_decode($json, true);
if (isset($payload['_exp'])) {
$expiration = $payload['_exp'];
if ($payload['_exp'] < Controller_Time::time()) {
return new self($payload, $expiration);
* @param bool|int $expiration
public function __construct($payload, $expiration = false) {
$this->_payload = $payload;
$this->_expiration = $expiration;
public function __toString() {
$payload = $this->_payload;
if ($this->_expiration !== false) {
$payload['_exp'] = $this->_expiration;
$key = Model_Crypto::shared_hash_secret();
$header = '{"alg":"HS256","typ":"JWT"}';
$body = self::base64url_encode($header) . '.' . self::base64url_encode(json_encode($payload));
$signature = hash_hmac('sha256', $body, $key, true);
return $body . '.' . self::base64url_encode($signature);
public function __isset($key) {
throw new \OutOfBoundsException('Invalid key: ' . $key);
public function __get($key) {
return $this->_expiration;
throw new \OutOfBoundsException('Invalid key: ' . $key);
* Base64URL-encodes the given payload. This is identical to base64_encode except it substitutes characters
* not safe for use in URLs.
public static function base64url_encode($payload) {
return self::base64url_convert_to(base64_encode($payload));
public static function base64url_convert_to($base64) {
$intermediate = rtrim($base64, '=');
$intermediate = str_replace('+', '-', $intermediate);
$intermediate = str_replace('/', '_', $intermediate);
* Base64URL-decodes the given payload. This is identical to base64_encode except it allows for the characters
* substituted by base64url_encode.
public static function base64url_decode($payload) {
return base64_decode(self::base64url_convert_from($payload));
public static function base64url_convert_from($base64url) {
$intermediate = str_replace('_', '/', $base64url);
$intermediate = str_replace('-', '+', $intermediate);