Skip to content

Instantly share code, notes, and snippets.

@ivangabriele
Created September 17, 2021 12:08
Show Gist options
  • Save ivangabriele/dbe219be6366e28229aa0cde8f872fb8 to your computer and use it in GitHub Desktop.
Save ivangabriele/dbe219be6366e28229aa0cde8f872fb8 to your computer and use it in GitHub Desktop.
Verify a PS256 JWT in a browser environnent with a public RSA key.
import { parseJwk } from 'jose-browser-runtime/jwk/parse'
import { jwtVerify } from 'jose-browser-runtime/jwt/verify'
import { pem2jwk } from 'pem-jwk'
import handleError from './handleError'
/**
* Validate a JWT
*
* @param {string} token
* @param {string} expectedAction
* @returns {string}
*/
export default async function isJwtValid(token, expectedAction) {
try {
// Convert RSA Public Key format from PEM to JWK
const rsaPublicKeyJwk = pem2jwk(process.env.NEXT_PUBLIC_RSA_PUBLIC_KEY)
// Convert RSA Public Key format from JWK to internal KeyLike Jose format:
// PS256 = signed with RSASSA-PSS algo and hashed via SHA-256
// https://www.scottbrady91.com/JOSE/JWTs-Which-Signing-Algorithm-Should-I-Use
const rsaPublicKeyLike = await parseJwk(rsaPublicKeyJwk, 'PS256')
const {
payload: { action },
} = await jwtVerify(token, rsaPublicKeyLike, {})
return action === expectedAction
} catch (err) {
// https://github.com/panva/jose/blob/main/test/jwt/verify.test.mjs
// Ignore IAT errors to skip client device time synchronization issues
if (
err?.code === 'ERR_JWT_CLAIM_VALIDATION_FAILED' &&
err.message === `"iat" claim timestamp check failed (it should be in the past)`
) {
return true
}
// Handle unexpected errors
// ERR_JWS_SIGNATURE_VERIFICATION_FAILED just mean it's an invalid JWT
if (err?.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
handleError(err, 'libs/isJwtValid()')
}
return false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment