passport-jwt
passport-jwt copied to clipboard
Multiple secrets/keys
With something like heroku's securekey addon, they have a rotating key, giving you the key and the previous key. I'd like to be able to check against either. Is that possible with passport-jwt?
This is not directly supported today but I see the use case. I believe Google supports a similar rotating key strategy that would require validating against multiple keys (i.e. the current key and the last used key).
You could potentially shoehorn it in by overriding the JwtVerifier. I'd be open to a pull request for this feature.
Without really digging into this, I think it could be accomplished in a backwards compatible fashion by accepting either a single secret or key or an array of secrets/keys from the secretOrKeyProvider
callback. Decoding could be tried against each key returned until successful or you run out of keys.
Decoding could be tried against each key returned until successful or you run out of keys.
Note that JWTs have a kid
(Key ID) header for this purpose. Instead of a list of secrets a map of key ids to secrets could be provided instead.
There's a nice explanation here about how key rotation is handled with JWKS: https://auth0.com/blog/navigating-rs256-and-jwks
Here is some code that i use to handle this situation.
let secretOrKeyProvider = (req, token, done) => {
const beginCert = '-----BEGIN CERTIFICATE-----'
const endCert = '-----END CERTIFICATE-----'
const keys = opts.keys
var decodedToken = decode(token, { complete: true })
if (!decodedToken) {
done(`Error: malformed token: ${decodedToken}`, null)
return
}
const key = keys.find(k => k.kid == decodedToken.header.kid)
if (!key) {
done(`Error: key not found: ${decodedToken.header.kid}`, null)
return
}
const secretOrKey = `${beginCert}\n${key.x5c[0]}\n${endCert}`
done(null, secretOrKey)
}
const opts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
issuer: process.env.IDP_URL,
passReqToCallback: true,
secretOrKeyProvider: secretOrKeyProvider,
algorithms: []
}
let idp = create({
baseURL: process.env.IDP_URL || "http://{{your.openid.connect.provider"
})
// called only once at application start.
idp.get('/.well-known/openid-configuration').then(openid => {
var openidConfig = openid.data
opts.openidConfig = openidConfig
idp.get(opts.openidConfig.jwks_uri).then(jwks => {
opts.keys = jwks.data.keys
opts.keys.forEach(key => opts.algorithms.push(key.alg))
passport.use(new JwtStrategy(opts, (req, jwt_payload, done) => {
done(null, jwt_payload)
})
)
}).catch(err => {
console.error(err)
})
}).catch(err => {
console.error(err)
})
Hope this helps!
This has already been implemented. Check out https://github.com/auth0/node-jwks-rsa