firebase-auth-cloudflare-workers
Version:
Zero-dependencies firebase auth library for Cloudflare Workers.
77 lines (76 loc) • 2.91 kB
JavaScript
import { JwtError, JwtErrorCode } from './errors';
import { HTTPFetcher, UrlKeyFetcher } from './jwk-fetcher';
import { isNonNullObject } from './validator';
// https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library
// https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams
export const rs256alg = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: 'SHA-256',
};
/**
* Class for verifying JWT signature with a public key.
*/
export class PublicKeySignatureVerifier {
keyFetcher;
constructor(keyFetcher) {
this.keyFetcher = keyFetcher;
if (!isNonNullObject(keyFetcher)) {
throw new Error('The provided key fetcher is not an object or null.');
}
}
static withCertificateUrl(clientCertUrl, keyStorer) {
const fetcher = new HTTPFetcher(clientCertUrl);
return new PublicKeySignatureVerifier(new UrlKeyFetcher(fetcher, keyStorer));
}
/**
* Verifies the signature of a JWT using the provided secret or a function to fetch
* the public key.
*
* @param token - The JWT to be verified.
* @throws If the JWT is not a valid RS256 token.
* @returns A Promise resolving for a token with a valid signature.
*/
async verify(token) {
const { header } = token.decodedToken;
const publicKeys = await this.fetchPublicKeys();
for (const publicKey of publicKeys) {
if (publicKey.kid !== header.kid) {
continue;
}
const verified = await this.verifySignature(token, publicKey);
if (verified) {
// succeeded
return;
}
throw new JwtError(JwtErrorCode.INVALID_SIGNATURE, 'The token signature is invalid.');
}
throw new JwtError(JwtErrorCode.NO_MATCHING_KID, 'The token does not match the kid.');
}
async verifySignature(token, publicJWK) {
try {
const key = await crypto.subtle.importKey('jwk', publicJWK, rs256alg, false, ['verify']);
return await crypto.subtle.verify(rs256alg, key, token.decodedToken.signature, token.getHeaderPayloadBytes());
}
catch (err) {
throw new JwtError(JwtErrorCode.INVALID_SIGNATURE, `Error verifying signature: ${err}`);
}
}
async fetchPublicKeys() {
try {
return await this.keyFetcher.fetchPublicKeys();
}
catch (err) {
throw new JwtError(JwtErrorCode.KEY_FETCH_ERROR, `Error fetching public keys for Google certs: ${err}`);
}
}
}
/**
* Class for verifying unsigned (emulator) JWTs.
*/
export class EmulatorSignatureVerifier {
async verify() {
// Signature checks skipped for emulator; no need to fetch public keys.
}
}