UNPKG

firebase-auth-cloudflare-workers

Version:

Zero-dependencies firebase auth library for Cloudflare Workers.

118 lines (117 loc) 3.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HTTPFetcher = exports.parseMaxAge = exports.UrlKeyFetcher = exports.isX509Certificates = exports.isJWKMetadata = void 0; const validator_1 = require("./validator"); const x509_1 = require("./x509"); const isJWKMetadata = (value) => { if (!(0, validator_1.isNonNullObject)(value) || !value.keys) { return false; } const keys = value.keys; if (!Array.isArray(keys)) { return false; } const filtered = keys.filter((key) => (0, validator_1.isObject)(key) && !!key.kid && typeof key.kid === 'string'); return keys.length === filtered.length; }; exports.isJWKMetadata = isJWKMetadata; const isX509Certificates = (value) => { if (!(0, validator_1.isNonNullObject)(value)) { return false; } const values = Object.values(value); if (values.length === 0) { return false; } for (const v of values) { if (typeof v !== 'string' || v === '') { return false; } } return true; }; exports.isX509Certificates = isX509Certificates; /** * Class to fetch public keys from a client certificates URL. */ class UrlKeyFetcher { fetcher; keyStorer; constructor(fetcher, keyStorer) { this.fetcher = fetcher; this.keyStorer = keyStorer; } /** * Fetches the public keys for the Google certs. * * @returns A promise fulfilled with public keys for the Google certs. */ async fetchPublicKeys() { const publicKeys = await this.keyStorer.get(); if (publicKeys === null || typeof publicKeys !== 'object') { return await this.refresh(); } return publicKeys; } async refresh() { const resp = await this.fetcher.fetch(); if (!resp.ok) { const errorMessage = 'Error fetching public keys for Google certs: '; const text = await resp.text(); throw new Error(errorMessage + text); } const json = await resp.json(); const publicKeys = await this.retrievePublicKeys(json); const cacheControlHeader = resp.headers.get('cache-control'); // store the public keys cache in the KV store. const maxAge = (0, exports.parseMaxAge)(cacheControlHeader); if (!isNaN(maxAge) && maxAge > 0) { await this.keyStorer.put(JSON.stringify(publicKeys), maxAge); } return publicKeys; } async retrievePublicKeys(json) { if ((0, exports.isX509Certificates)(json)) { const jwks = []; for (const [kid, x509] of Object.entries(json)) { jwks.push(await (0, x509_1.jwkFromX509)(kid, x509)); } return jwks; } if (!(0, exports.isJWKMetadata)(json)) { throw new Error(`The public keys are not an object or null: "${json}`); } return json.keys; } } exports.UrlKeyFetcher = UrlKeyFetcher; // parseMaxAge parses Cache-Control header and returns max-age value as number. // returns NaN when Cache-Control header is none or max-age is not found, the value is invalid. const parseMaxAge = (cacheControlHeader) => { if (cacheControlHeader === null) { return NaN; } const parts = cacheControlHeader.split(','); for (const part of parts) { const subParts = part.trim().split('='); if (subParts[0] !== 'max-age') { continue; } return Number(subParts[1]); // maxAge is a seconds value. } return NaN; }; exports.parseMaxAge = parseMaxAge; class HTTPFetcher { clientCertUrl; constructor(clientCertUrl) { this.clientCertUrl = clientCertUrl; if (!(0, validator_1.isURL)(clientCertUrl)) { throw new Error('The provided public client certificate URL is not a valid URL.'); } } fetch() { return fetch(this.clientCertUrl); } } exports.HTTPFetcher = HTTPFetcher;