5

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?

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
plum 0
  • 652
  • 9
  • 21

1 Answers1

4

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.

jps
  • 20,041
  • 15
  • 75
  • 79
  • So with this information, would it suffice to set k as `jose.util.base64url.encode("SuperSecretKey")` since then the key will be supplied in the correct format? Trying this with your token and secret works. However with my own generated token from https://jwt.io using the base64URL secret still returns `No Key Found`. JWT.io shows the signature valid in the b64 encoded phrase when checked, and when not checked with the normal phrase. – plum 0 Dec 28 '21 at 19:12
  • 1
    Basically yes, but actually it does not work with the actual secret "SuperSecretKey", because that is too short. The secret is supposed to be 256 bit or 32 bytes long, so when you try the same with "SuperSecretKeyThatIsLongEnough!!" it works. node.jose seems to be rather strict while jwt.io and most libs I have worked with seem to not really care about the key length. – jps Dec 28 '21 at 19:40
  • Thank you so much! I actually just finished my own verification of this as a classic "bad example" issue under the assumption the key was too short to test practically. Testing it with real data showed that the encoding worked. – plum 0 Dec 28 '21 at 19:48
  • 1
    It was an interesting question. I also got stuck at first with the short key. Even knowing it's too short, most libs have no issue. And the only error I got (for all the problems mentioned in the answer) is always "Error: no key found". – jps Dec 28 '21 at 19:56
  • Getting error "TypeError: Cannot read properties of undefined (reading 'asKey')" jose.JWK.asKey is not working properly – compski Apr 02 '23 at 09:43