@pedwise/next-firebase-auth-edge
Version:
Next.js 13 Firebase Authentication for Edge and server runtimes. Dedicated for Next 13 server components. Compatible with Next.js middleware.
109 lines • 4.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.verify = exports.getPublicCryptoKey = void 0;
const error_1 = require("./error");
const decode_1 = require("./decode");
const utils_1 = require("./utils");
const consts_1 = require("./consts");
const pem_to_public_key_1 = require("../pem-to-public-key");
const firebase_1 = require("../firebase");
const keyMap = {};
async function getCachedPublicKeyFromCertificate(pem) {
if (keyMap[pem]) {
return keyMap[pem];
}
return (keyMap[pem] = await (0, pem_to_public_key_1.pemToPublicKey)(pem));
}
function createKeyFromCertificatePEM(pem) {
return getCachedPublicKeyFromCertificate(pem);
}
async function getPublicCryptoKey(publicKey, options) {
if (publicKey.startsWith("-----BEGIN CERTIFICATE-----")) {
return createKeyFromCertificatePEM(publicKey
.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", "")
.replace(/\n/g, ""));
}
const base64 = publicKey
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace(/\n/g, "");
const buffer = (0, utils_1.base64StringToArrayBuffer)(base64);
return crypto.subtle.importKey(options.format, buffer, consts_1.ALGORITHMS.RS256, false, ["verify"]);
}
exports.getPublicCryptoKey = getPublicCryptoKey;
async function verify(jwtString, secretOrPublicKey, options = {
format: "spki",
algorithm: "RS256",
}) {
if (options.nonce !== undefined && !options.nonce.trim()) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "nonce must be a non-empty string");
}
const clockTimestamp = options.clockTimestamp || Math.floor(Date.now() / 1000);
if (!jwtString) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "jwt must be valid");
}
const parts = jwtString.split(".");
if (parts.length !== 3) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "jwt malformed");
}
const decodedToken = (0, decode_1.decode)(jwtString, { complete: true });
if (!decodedToken) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "invalid token");
}
const header = decodedToken.header;
const signature = parts[2].trim();
const hasSignature = signature !== "";
if (!(0, firebase_1.useEmulator)() && !hasSignature && secretOrPublicKey) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_SIGNATURE, "jwt signature is required");
}
if (!(0, firebase_1.useEmulator)() && hasSignature && !secretOrPublicKey) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_CREDENTIAL, "secret or public key must be provided");
}
if (!(0, firebase_1.useEmulator)() && decodedToken.header.alg !== options.algorithm) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "unsupported algorithm: " + decodedToken.header.alg);
}
if (!(0, firebase_1.useEmulator)()) {
const data = parts.slice(0, 2).join(".");
const key = await getPublicCryptoKey(secretOrPublicKey, options);
const jwtBuffer = (0, utils_1.stringToArrayBuffer)(data);
const sigBuffer = (0, utils_1.base64StringToArrayBuffer)(signature);
const result = await crypto.subtle.verify(consts_1.ALGORITHMS[options.algorithm], key, sigBuffer, jwtBuffer);
if (!result) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_SIGNATURE, "invalid signature");
}
}
const payload = decodedToken.payload;
if (typeof payload.nbf !== "undefined") {
if (typeof payload.nbf !== "number") {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "invalid nbf value");
}
if (payload.nbf > clockTimestamp) {
throw new error_1.JwtError(error_1.JwtErrorCode.TOKEN_EXPIRED, "jwt not active: " + new Date(payload.nbf * 1000).toISOString());
}
}
if (typeof payload.exp !== "undefined") {
if (typeof payload.exp !== "number") {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "invalid exp value");
}
if (clockTimestamp >= payload.exp) {
throw new error_1.JwtError(error_1.JwtErrorCode.TOKEN_EXPIRED, "token expired: " + new Date(payload.exp * 1000).toISOString());
}
}
if (options.nonce) {
if (payload.nonce !== options.nonce) {
throw new error_1.JwtError(error_1.JwtErrorCode.INVALID_ARGUMENT, "jwt nonce invalid. expected: " + options.nonce);
}
}
if (options.complete === true) {
const signature = decodedToken.signature;
return {
header: header,
payload: payload,
signature: signature,
};
}
return payload;
}
exports.verify = verify;
//# sourceMappingURL=verify.js.map