UNPKG

firebase-auth-cloudflare-workers

Version:

Zero-dependencies firebase auth library for Cloudflare Workers.

93 lines (92 loc) 3.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RS256Token = void 0; const base64_1 = require("./base64"); const errors_1 = require("./errors"); const utf8_1 = require("./utf8"); const validator_1 = require("./validator"); class RS256Token { rawToken; decodedToken; constructor(rawToken, decodedToken) { this.rawToken = rawToken; this.decodedToken = decodedToken; } /** * * @param token - The JWT to verify. * @param currentTimestamp - Current timestamp in seconds since the Unix epoch. * @param skipVerifyHeader - skip verification header content if true. * @throw Error if the token is invalid. * @returns */ static decode(token, currentTimestamp, skipVerifyHeader = false) { const tokenParts = token.split('.'); if (tokenParts.length !== 3) { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, 'token must consist of 3 parts'); } const header = decodeHeader(tokenParts[0], skipVerifyHeader); const payload = decodePayload(tokenParts[1], currentTimestamp); return new RS256Token(token, { header, payload, signature: (0, base64_1.decodeBase64Url)(tokenParts[2]), }); } getHeaderPayloadBytes() { const rawToken = this.rawToken; // `${token.header}.${token.payload}` const trimmedSignature = rawToken.substring(0, rawToken.lastIndexOf('.')); return utf8_1.utf8Encoder.encode(trimmedSignature); } } exports.RS256Token = RS256Token; const decodeHeader = (headerPart, skipVerifyHeader) => { const header = decodeBase64JSON(headerPart); if (skipVerifyHeader) { return header; } const kid = header.kid; if (!(0, validator_1.isString)(kid)) { throw new errors_1.JwtError(errors_1.JwtErrorCode.NO_KID_IN_HEADER, `kid must be a string but got ${kid}`); } const alg = header.alg; if ((0, validator_1.isString)(alg) && alg !== 'RS256') { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, `algorithm must be RS256 but got ${alg}`); } return header; }; const decodePayload = (payloadPart, currentTimestamp) => { const payload = decodeBase64JSON(payloadPart); if (!(0, validator_1.isNonEmptyString)(payload.aud)) { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, `"aud" claim must be a string but got "${payload.aud}"`); } if (!(0, validator_1.isNonEmptyString)(payload.sub)) { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, `"sub" claim must be a string but got "${payload.sub}"`); } if (!(0, validator_1.isNonEmptyString)(payload.iss)) { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, `"iss" claim must be a string but got "${payload.iss}"`); } if (!(0, validator_1.isNumber)(payload.iat)) { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, `"iat" claim must be a number but got "${payload.iat}"`); } if (currentTimestamp < payload.iat) { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, `Incorrect "iat" claim must be a older than "${currentTimestamp}" (iat: "${payload.iat}")`); } if (!(0, validator_1.isNumber)(payload.exp)) { throw new errors_1.JwtError(errors_1.JwtErrorCode.INVALID_ARGUMENT, `"exp" claim must be a number but got "${payload.exp}"`); } if (currentTimestamp > payload.exp) { throw new errors_1.JwtError(errors_1.JwtErrorCode.TOKEN_EXPIRED, `Incorrect "exp" (expiration time) claim must be a newer than "${currentTimestamp}" (exp: "${payload.exp}")`); } return payload; }; const decodeBase64JSON = (b64Url) => { const decoded = (0, base64_1.decodeBase64Url)(b64Url); try { return JSON.parse(utf8_1.utf8Decoder.decode(decoded)); } catch { return null; } };