How to verify a JWT signature using Node-jose

9.2k views Asked by At

I am trying to use node-jose to verify signatures of my JWTs. I know the secret, but am having trouble converting this secret into a JWK used for the verification.

Here is an example of how I am trying to create my key with my secret and verify my token. This results in Error: no key found.

let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXJpYWxfbnVtYmVyIjoiNWYxMGExNjMtMjk2OC00ZDZkLWIyZDgtOGQxNjQwMDNlMmQ0Iiwic2VxIjo1MTI4MTYsIm5hbWUiOiJOYW1lMSIsImlkIjo2NTQsImRlc2NyaXB0aW9uIjoiVGVzdCBEZWNvZGluZyJ9.ahLaTEhdgonxb8rfLG6NjcIg6rqbGzcHkwwFtvb9KTE"
let secret = "SuperSecretKey"
let props = {
    kid: "test-key",
    alg: "HS256",
    use: "sig",
    k: secret,
    kty: "oct"
}
let key;
jose.JWK.asKey(props).then(function(result) {key = result})
jose.JWS.createVerify(key).verify(token).then(function(result){console.log(result)})

Do I need to modify my token to include the kid header somewhere? Am I generating the key correctly from the known secret for this library?

1

There are 1 answers

5
jps On BEST ANSWER

You have three problems with your code.

  1. due to the asynchronous nature of the promises, key gets a value when the promise is fulfilled (in the .then part), but that happens after the next line gets called.

    Place a console.log(key) directly after the line jose.JWK.asKey(... and you see you get "undefined" as a result. So there is actually no key.

  2. the k value in a JWK is treated as a Base64Url encoded octet. When you sign the token, you have to use the base64url decoded value of k, but not k directly.

  3. the secret "SuperSecretKey" is too short for node.jose. For the HS256 algorithm, the secret has to be 256 bits long. node.jose seems to be quite strict, compared to other libs.

To solve the first problem, you can either nest the calls (which quickly becomes hard to read, or use the async/await syntax like shown below:

var jose = require('node-jose')

async function tokenVerifyer() 
{
    let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXJpYWxfbnVtYmVyIjoiNWYxMGExNjMtMjk2OC00ZDZkLWIyZDgtOGQxNjQwMDNlMmQ0Iiwic2VxIjo1MTI4MTYsIm5hbWUiOiJOYW1lMSIsImlkIjo2NTQsImRlc2NyaXB0aW9uIjoiVGVzdCBEZWNvZGluZyJ9.KK9F14mwi8amhsPT7ppqp_yCYwwOGcHculKByNPlDB8"
    let secret = "SuperSecretKeyThatIsLongEnough!!" // A 32 character long secret to get 256 bits.
    let props = {
        kid: "test-key",
        alg: "HS256",
        use: "sig",
        k: "cynZGe3BenRNOV2AY__-hwxraC9CkBoBMUdaDHgj5bQ",
        //k : jose.util.base64url.encode(secret), // alternatively use above secret
        kty: "oct"
    }

    let key = await jose.JWK.asKey(props)

    let result = await jose.JWS.createVerify(key).verify(token)
} 

tokenVerifyer()

In the above example, k is a key generated on https://mkjwk.org/ and the token was created with that key on https://jwt.io (check 'secret base64 encoded'). Alternatively, you can use your own secret, but have to make sure it's long enough.

Do I need to modify my token to include the kid header somewhere?

The small example above works without putting the kid in the token. For any real applications, you would usually add the kid into the token header. Your keystore could have more keys or rotating keys and the kidhelps to select the correct one.