Search code examples
pythonoauth-2.0jwtrsajwk

token verification failing using JWK/JWT for user authentication


I am trying to verify an idToken using a public key in python.

I first convert the JWK token to PEM but when I call the "decode" function, I see a "signature verification failed" exception. What am I missing?

# Long string goes here - this is the token to verify
myToken = 'ezFraWQiXXX.YYYYYYYY.ZZZZZZZZ'

# JWK Token
webkey = {
      "alg": "RS256",
      "e": "AQAB",
      "kid": "d9FzOfniXuHf2sF3opIKZb0sW8Nuaa0d5d+AXXXXXXXX=",
      "kty": "RSA",
      "n": "nQwBvRlZKdXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4HcenyO_WASyjr6korLEHxh8XXXXXXXXXXXX",
      "use": "sig"
    }

# Converting JWK to PEM
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(webkey)
pubk_bytes = public_key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo)


# This is where I get the "signature verification failed" exception
claim = jwt.decode(myToken, pubk_bytes, algorithms=['RS256']) # <<-- ideally this should decode the token for me


Solution

  • Looks like you are using PyJWT, which is a good choice of security library. Ideally get the library to download token signing public keys for you from the JWKS endpoint of the Authorization Server, for the cleanest and simplest code:

    import jwt
    from jwt import PyJWKClient
    
    // The client will read the JWT header to get the kid field,
    // then download token signing public keys and return that matching the kid.
    // This key will then be cached for future JWTs with the same kid.
    // The client will reliably handle new kids if keys are recycled.
    jwks_client = PyJWKClient(url)
    signing_key = jwks_client.get_signing_key_from_jwt(access_token_jwt)
    
    // It is recommended to verify the signature, expiry, issuer and audience.
    // As a best practice, the API should also specify the algorithms it expects
    // to receive in JWT signatures.
    claims = jwt.decode(
      access_token_jwt,
      signing_key.key,
      algorithms=["RS256"],
      issuer="my-issuer",
      audience="my-audience")
    

    You should be able to send a JWK directly into the library, rather than dealing with RSA or byte translation. See the pyJWT docs for some examples. For further security background, you may also find the Curity article on JWT Best Practices useful.