auth0 / node-jsonwebtoken

JsonWebToken implementation for node.js http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
MIT License
17.75k stars 1.23k forks source link

Unable to verify signature of AWS JWTs #814

Open CoranH opened 2 years ago

CoranH commented 2 years ago

Description

I have some JWTs obtained from AWS/Cognito with the Elastic Load Balancer performing authentication for me. I also have the associated public key from the documented endpoints for the Cognito userpool. However the jsonwebtoken library says the signature is invalid (JsonWebTokenError: invalid signature), except when I use the same input (same JWT and public key in PEM format) in PyJWT Python code, this decodes/verifies without issue. There is no information to point to why jsonwebtoken is unable to verify the JWT/PEM when another library (PyJWT) can. I have tried ignoring any expiration checking in case this is the cause of the "invalid signature", such as ignoreExpiration, clockTolerance, and maxAge, without success. The only discrepancy I can see is the jsonwebtoken library must remove the base64 padding (=) from the JWT first. Is there any known reason why jsonwebtoken would be unable to verify a signature which can be verified with another library? Perhaps some assumptions about the format/layout/syntax of the inputs which AWS/Cognito is not respecting.

Reproduction

Python code:

import jwt
import requests
import base64
import json

encoded_jwt = """eyJ0e
......
tE5Bg=="""

pub_key = """-----BEGIN PUBLIC KEY-----
MFkw
.....
J5og==
-----END PUBLIC KEY-----"""

payload = jwt.decode(encoded_jwt, pub_key, algorithms=['ES256'], leeway=10000)
print('Payload: '+ str(payload)) #This works fine

TypeScript code:

userJWT = userJWT.replace(/=/g, '');

// This fails, with  JsonWebTokenError: invalid signature
return jsonwebtoken.verify(userJWT, this.pem,
{algorithms: ['ES256'], ignoreExpiration: true, clockTolerance: 100000, maxAge: "1 day"});

Environment

Please provide the following:

rpf3 commented 2 years ago

I am seeing a similar error when trying to validate an KMS-signed JWT. The error message I'm getting back however is:

"ES256" signatures must be "64" bytes, saw "71"

This is the code I'm using to construct my JWT:

const header64 = base64url.encode(JSON.stringify(header));
const payload64 = base64url.encode(JSON.stringify(payload));

const message = Buffer.from(`${header64}.${payload64}`);

const command = new SignCommand({
    KeyId: 'mrk-1234567890',
    Message: message,
    SigningAlgorithm: SigningAlgorithmSpec.ECDSA_SHA_256
});

const client = new KMSClient({});
const response = await client.send(command);

const signature64 = base64url.encode(Buffer.from(response.Signature), 'base64');

const jwt = `${header64}.${payload64}.${signature64}`;

Then if I try and verify that token using jsonwebtoken and the public PEM file I get the above error.

rpf3 commented 2 years ago

@CoranH I'm not sure if this helps but I was able to fix my issue with my KMS-generated signature by using the ecdsa-sig-formatter package when creating my JWT. If you take the above code and modify it slightly to convert the AWS DER-encoded signature into a JOSE-compatible format.

const header64 = base64url.encode(JSON.stringify(header));
const payload64 = base64url.encode(JSON.stringify(payload));

const message = Buffer.from(`${header64}.${payload64}`);

const command = new SignCommand({
    KeyId: 'mrk-1234567890',
    Message: message,
    SigningAlgorithm: SigningAlgorithmSpec.ECDSA_SHA_256
});

const client = new KMSClient({});
const response = await client.send(command);

const signature64 = base64url.encode(Buffer.from(response.Signature), 'base64');
const signatureJose = derToJose(signature64, 'ES256');

const jwt = `${header64}.${payload64}.${signatureJose}`;
CoranH commented 2 years ago

I think the issue I was experiencing was ultimately because AWS was returning a URL encoded JWT (which apparently is non-standard) and jsonwebtoken strictly follows the standard and chokes on this. AWS support have said they will fix this, but there's no ETA.

shubhpixart commented 1 year ago

@CoranH I also faced the exact same issue. While getting the token from AWS it is sending with "==" in all header, payload and signature which jwt.verify() fail to verify and return invalid token. While Python library is returning the correct payload. Did you get any resolution for this ?