Search code examples
pythonpython-3.xjwtsignaturepyjwt

Verify the JWT token signature without decoding it in the PyJWT library


Is it possible to verify the JWT token signature without decoding it in the PyJWT library?


Solution

  • I don't know if I got what you are asking but I am going to try anyway.

    The whole JWT standard is well explained at this link. Essentially, if I understood what you are asking, you want to check the signature without decoding the token itself. To answer your question I am going to use some of the stuff found at the link.

    In its compact form, a JSON Web Token consist of three parts separated by dots (.), which are:

    • Header: typically consists of two parts, the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA (you are using RSA).

    • Payload: which contains the claims. Claims are statements about an entity (this is what you are decoding to find the link for the public key I presume)

    • Signature: To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that. For example if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:

      HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

    The signature is used to verify the message wasn't changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.

    These three elements are base64 encoded and connected by "." and thus the JWT is something like this: akuhsdk.wqkhkwje.sajgdhwqj

    So, if I want to sign a message to certify that it came from me, I can generate a signature with my own private key. Anybody interested in verifying the message can use my public key to confirm that the signature is valid.

    At this point, the receiver could just verify the signature by decrypting it and checking if it works (most cryptographic libraries throw errors). If not, then the private key used to encrypt it wasn't the one from the key-pair.

    Example:

    import jwt
    
    token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjoxMjN9.HT1kBSdGFAznrhbs2hB6xjVDilMUmKA-_36n1pLLtFTKHoO1qmRkUcy9bJJwGuyfJ_dbzBMyBwpXMj-EXnKQQmKlXsiItxzLVIfC5qE97V6l6S0LzT9bzixvgolwi-qB9STp0bR_7suiXaON-EzBWFh0PzZi7l5Tg8iS_0_iSCQQlX5MSJW_-bHESTf3dfj5GGbsRBRsi1TRBzvxMUB6GhNsy6rdUhwoTkihk7pljISTYs6BtNoGRW9gVUzfA2es3zwBaynyyMeSocYet6WJri97p0eRnVGtHSWwAmnzZ-CX5-scO9uYmb1fT1EkhhjGhnMejee-kQkMktCTNlPsaUAJyayzdgEvQeo5M9ZrfjEnDjF7ntI03dck1t9Bgy-tV1LKH0FWNLq3dCJJrYdQx--A-I7zW1th0C4wNcDe_d_GaYopbtU-HPRG3Z1SPKFuX1m0uYhk9aySvkec66NBfvV2xEgo8lRZyNxntXkMdeJCEiLF1UhQvvSvmWaWC-0uRulYACn4H-tZiaK7zvpcPkrsfJ7iR_O1bxMPziKpsM4b7c7tmsEcOUZY-IHEI9ibd54_A1O72i08sCWKT5CXyG70MAPqyR0MFlcV7IuDtBW3LCqyvfsDVk4eIj8VcSU1OKQJ1Gl-CTOHEyN-ncV3NslVLaT9Q1C4E7uK2QpS8z0'
    public_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCcjWidoIPNRc3IN1hoGeOdSvDkBDK3W3P7/4HxLf62nvUQVczL3FG+dG9KSRnzuvRoUi1o3TASO3Yn72FSfaLPE/JmOtpu/IGuB/oF/CrJqEHA/08n0xkNQK8kwdIqayKPS84PVOm8XomNijMpUCahqu9cGZDPhlgqD8PAxw4e1ZQSizWj0hTSCR78dmHAEr5oXryP6uD0Mw/KGKYel/KTMu00dShWPzHnJeLaYvKgMJKPN6pqhsWFQsNUDnKd9tgn3NSPeHECnnBbUxB2BeuVz72+HnyFWah3mpGH4Dr+9rjRXiPg2AYxgR3U93AEQ6osefxeIKUSCXWx1txNV07QzwFVag4vPBmrA9XktC7i5EP91wxUOsyzhG8geXKuDHmE+/7U3AsExHYFkBLqMnW92CaTeQ408xsRXjxWjSNHpfqhZVxGY5Eh8L3NVqgRg1LdnZYHpovi1iP4Zx2Z7Nb5F9ejuMsA+v/D0WL3c6bhwU8BKdD7YZDG2tpzq6PHt+NarGkcWWh9/p/SIJoZi+e35mjcUMfnRD8w/ouL0sTnxebT7xBCVucfRoMPA67USoChDpc+pNsdtsqlQOZMgpPZYfjIyCThv5mwjEKHnytBq46ULOFlHt0opplDANnDsvWwqEobhACZM+n2ZNtu36eoc3bC/Hak8ACEi5DixirF0w== miguel@MS90J8G8WL'
    
    payload = jwt.decode(token, public_key, algorithms=['RS256'])
    
    print(payload)
    

    So, yes, you can do it.

    But to accomplish what you are asking with code, you would have to use some other library (crypto), because pyJWT only provides the decode method, which needs a full token (header+payload+signature) like in the example. Also, you would have to store the header you got during the first read of the token to get the encryption algorithm used (if changing).

    But still, why wouldn't you want to decode it again? This way you can even check that it is the same as the one signed and no one tampered with the message.