Verify Access_Token JWT with jose-php

Note: I read this topic and went through the Laravel quick start: Laravel API. Should I manually verify access token's signature?

Hello everyone,

I was seeking a dynamic jwt verification solution for PHP, I came across jose-php which allowed me to:

convert JWKS to PEM => Decode JWT => verify Data against PEM

I will then apply other custom middlewares:

  • Expiration check on the token
  • Validation of Scope & Audience

Here is a sample code of verification middleware with the dummy data:

# public key retrieved from JWKS endpoint
$components = array(
    'kty' => 'RSA',
    'e' => 'AQAB',
    'n' => 'x9vNhcvSrxjsegZAAo4OEuo...'
);


$public_key= JOSE_JWK::decode($components);



$jwt_string = 'eyJ...'; // Access_token
$jws = JOSE_JWT::decode($jwt_string);
$result = $jws->verify($public_key, 'RS256');

I am currently getting undefined on $result, Which I am also looking into it.

My question is, is this a valid way or I am exposing a vulnerability by doing such a thing, and if there is any other potential better way that does not use the Client SDKs?

Just an update on the process, I switched to firebase/php-jwt as it is more convenient and straightforward to use and it was fairly easier to go quickly through its code and it does not return undefined anymore. Now the middleware code for validation looks like below:

$jwks = ['keys' => [[], []]; 

// JWK::parseKeySet($jwks) returns an associative array of **kid** to private

// key. Pass this as the second parameter to JWT::decode. 
// Instead of RS256 use your own algo
// $data can return error so wrap it in try catch and do as you desire afterward
$data= (array) JWT::decode("YOUR_ACCESS_TOKEN", JWK::parseKeySet($jwks), ['RS256', 'RS256']);

For those who are willing to test a sample encoding and decoding process, feel free to use the private key and public key below: (Credit to firebase documentation with a bit of tweaking on my side to convert it to a simple Laravel controller)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use \Firebase\JWT\JWT;

use \Firebase\JWT\JWK;

use Illuminate\Support\Facades\Http;

class JWTValidation extends Controller

{

    public function bundle(){

        

        $privateKey = <<<EOD

        -----BEGIN RSA PRIVATE KEY-----

        MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn

        vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9

        5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB

        AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz

        bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J

        Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1

        cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5

        5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck

        ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe

        k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb

        qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k

        eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm

        B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM=

        -----END RSA PRIVATE KEY-----

        EOD;

        $publicKey = <<<EOD

        -----BEGIN PUBLIC KEY-----

        MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H

        4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t

        0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4

        ehde/zUxo6UvS7UrBQIDAQAB

        -----END PUBLIC KEY-----

        EOD;

        

        $payload = array(

            "iss" => "example.org",

            "aud" => "example.com",

            "iat" => 1356999524,

            "nbf" => 1357000000

        );

        

        $jwt = JWT::encode($payload, $privateKey, 'RS256');

        //echo "Encode:\n" . print_r($jwt, true) . "\n";

        

        $decoded = JWT::decode($jwt, $publicKey, array('RS256'));

        

        /*

         NOTE: This will now be an object instead of an associative array. To get

         an associative array, you will need to cast it as such:

        */

        

        $decoded_array = (array) $decoded;

        return response()->json(['jwt' => $jwt, 'decoded' => $decoded]);

        //echo "Decode:\n" . print_r($decoded_array, true) . "\n";

    }
}

Now back to my first question again :slight_smile:

In case that I validate the key with the help of this library as the first piece of the code, am I exposing any vulnerability? or will it be a time-consuming task in long run to maintain a custom verification flow like this?