Reputation: 116
When I download the JWT set for a particular User Pool available at: https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
The JSON contains 2 keys. All the users that I have created for the pool seem to use just one of these keys.
What is the reason for having multiple keys per User Pool?
Upvotes: 10
Views: 5134
Reputation: 11650
According to the documentation:
Amazon Cognito generates two pairs of RSA cryptograpic keys for each user pool. One of the private keys is used to sign the token.
Presumably this is for security reasons. From limited trial-and-error it appears that one is used to encrypt id tokens and the other is used to encrypt access tokens. Or perhaps the purpose is to support key rotation (as suggested by @Michael-sqlbot).
Once you understand the why (or don't understand), the question becomes what to do with the two keys.
Again referring to the documentation, to validate the JWT signature you need to:
Steps 1 and 2 are necessary to figure out which RSA cryptograpic key was used to encrypt your JWT.
import jsonwebtoken from 'jsonwebtoken'
import jwkToPem from 'jwk-to-pem'
const jsonWebKeys = [ // from https://cognito-idp.us-west-2.amazonaws.com/<UserPoolId>/.well-known/jwks.json
{
"alg": "RS256",
"e": "AQAB",
"kid": "ABCDEFGHIJKLMNOPabc/1A2B3CZ5x6y7MA56Cy+6ubf=",
"kty": "RSA",
"n": "...",
"use": "sig"
},
{
"alg": "RS256",
"e": "AQAB",
"kid": "XYZAAAAAAAAAAAAAAA/1A2B3CZ5x6y7MA56Cy+6abc=",
"kty": "RSA",
"n": "...",
"use": "sig"
}
]
function validateToken(token) {
const header = decodeTokenHeader(token) // {"kid":"XYZAAAAAAAAAAAAAAA/1A2B3CZ5x6y7MA56Cy+6abc=", "alg": "RS256"}
const jsonWebKey = getJsonWebKeyWithKID(header.kid)
verifyJsonWebTokenSignature(token, jsonWebKey, function(err, decodedToken) {
if (err) {
console.error(err)
} else {
console.log(decodedToken)
}
})
}
function decodeTokenHeader(token) {
const [headerEncoded] = token.split('.')[0]
const buff = new Buffer(headerEncoded, 'base64')
const text = buff.toString('ascii')
return JSON.parse(text)
}
func getJsonWebKeyWithKID(kid) {
for (let jwk of jsonWebKeys) {
if (jwk.kid == kid) {
return jwk
}
}
return null
}
function verifyJsonWebTokenSignature(token, jsonWebKey, clbk) {
const pem = jwkToPem(jsonWebKey)
jsonwebtoken.verify(token, pem, { algorithms: ['RS256'] }, function(err, decodedToken) {
return clbk(err, decodedToken)
})
}
validateToken('xxxxxxxxx.XXXXXXXX.xxxxxxxx')
Upvotes: 9
Reputation: 9
"Firstly, get the JSON Web Key Set (JWKS) file from the url below. Replace the region and the userPoolId with your Cognito user pool’s configurations.
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
This JSON hash is a set of JWKs, hence the name JWKS. It may or may not consist of more than 1 JWK. In the event where there are multiple JWKs, which one should we use to do the decoding?
In the JWT, there is a key called kid in header section of the token, that is the first hash of the JWT. Whereas in each JWK, there is also a key called kid. Hence, we should use the JWK with the matching kid value to decode the JWT."
Upvotes: -1