UNPKG

@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.

227 lines 10.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var _a, _b; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFirebaseAuth = exports.handleExpiredToken = exports.isInvalidCredentialError = exports.isUserNotFoundError = exports.customTokenToIdAndRefreshTokens = void 0; const firebase_1 = require("./firebase"); const token_verifier_1 = require("./token-verifier"); const error_1 = require("./error"); const auth_request_handler_1 = require("./auth-request-handler"); const credential_1 = require("./credential"); const user_record_1 = require("./user-record"); const token_generator_1 = require("./token-generator"); const runtime = __importStar(require("@edge-runtime/ponyfill")); if ((typeof crypto === "undefined" || typeof global.crypto === "undefined") && Boolean((_a = runtime === null || runtime === void 0 ? void 0 : runtime.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) { global.crypto = runtime.crypto; } if ((typeof caches === "undefined" || typeof global.caches === "undefined") && Boolean((_b = runtime === null || runtime === void 0 ? void 0 : runtime.caches) === null || _b === void 0 ? void 0 : _b.open)) { global.caches = runtime.caches; } const getCustomTokenEndpoint = (apiKey) => { if ((0, firebase_1.useEmulator)()) { return `http://${process.env .FIREBASE_AUTH_EMULATOR_HOST}/identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`; } return `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apiKey}`; }; const getRefreshTokenEndpoint = (apiKey) => { if ((0, firebase_1.useEmulator)()) { return `http://${process.env .FIREBASE_AUTH_EMULATOR_HOST}/securetoken.googleapis.com/v1/token?key=${apiKey}`; } return `https://securetoken.googleapis.com/v1/token?key=${apiKey}`; }; async function customTokenToIdAndRefreshTokens(customToken, firebaseApiKey) { const refreshTokenResponse = await fetch(getCustomTokenEndpoint(firebaseApiKey), { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ token: customToken, returnSecureToken: true, }), }); const refreshTokenJSON = (await refreshTokenResponse.json()); if (!refreshTokenResponse.ok) { throw new Error(`Problem getting a refresh token: ${JSON.stringify(refreshTokenJSON)}`); } return { idToken: refreshTokenJSON.idToken, refreshToken: refreshTokenJSON.refreshToken, }; } exports.customTokenToIdAndRefreshTokens = customTokenToIdAndRefreshTokens; const isUserNotFoundResponse = (data) => { var _a, _b; return (((_a = data === null || data === void 0 ? void 0 : data.error) === null || _a === void 0 ? void 0 : _a.code) === 400 && ((_b = data === null || data === void 0 ? void 0 : data.error) === null || _b === void 0 ? void 0 : _b.message) === "USER_NOT_FOUND"); }; const refreshExpiredIdToken = async (refreshToken, apiKey) => { // https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token const response = await fetch(getRefreshTokenEndpoint(apiKey), { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: `grant_type=refresh_token&refresh_token=${refreshToken}`, cache: "no-store", }); if (!response.ok) { const data = await response.json(); const errorMessage = `Error fetching access token: ${JSON.stringify(data.error)} ${data.error_description ? `(${data.error_description})` : ""}`; if (isUserNotFoundResponse(data)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.USER_NOT_FOUND); } throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CREDENTIAL, errorMessage); } const data = await response.json(); return data.id_token; }; function isUserNotFoundError(error) { return ((error === null || error === void 0 ? void 0 : error.code) === `auth/${error_1.AuthClientErrorCode.USER_NOT_FOUND.code}`); } exports.isUserNotFoundError = isUserNotFoundError; function isInvalidCredentialError(error) { return ((error === null || error === void 0 ? void 0 : error.code) === `auth/${error_1.AuthClientErrorCode.INVALID_CREDENTIAL.code}`); } exports.isInvalidCredentialError = isInvalidCredentialError; async function handleExpiredToken(verifyIdToken, onExpired, onError) { try { return await verifyIdToken(); } catch (e) { // https://firebase.google.com/docs/reference/node/firebase.auth.Error switch (e.code) { case "auth/invalid-user-token": case "auth/user-token-expired": case "auth/user-disabled": return onError(e); case "auth/id-token-expired": case "auth/argument-error": try { return await onExpired(e); } catch (e) { return onError(e); } default: return onError(e); } } } exports.handleExpiredToken = handleExpiredToken; function getFirebaseAuth(serviceAccount, apiKey) { const authRequestHandler = new auth_request_handler_1.AuthRequestHandler(serviceAccount); const credential = new credential_1.ServiceAccountCredential(serviceAccount); const tokenGenerator = (0, token_generator_1.createFirebaseTokenGenerator)(credential); const handleTokenRefresh = async (refreshToken, firebaseApiKey) => { const newToken = await refreshExpiredIdToken(refreshToken, firebaseApiKey); const decodedToken = await verifyIdToken(newToken); return { decodedToken: decodedToken, token: newToken, }; }; async function getUser(uid) { return authRequestHandler.getAccountInfoByUid(uid).then((response) => { // Returns the user record populated with server response. return new user_record_1.UserRecord(response.users[0]); }); } async function verifyDecodedJWTNotRevokedOrDisabled(decodedIdToken, revocationErrorInfo) { // Get tokens valid after time for the corresponding user. return getUser(decodedIdToken.sub).then((user) => { if (user.disabled) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.USER_DISABLED, "The user record is disabled."); } // If no tokens valid after time available, token is not revoked. if (user.tokensValidAfterTime) { // Get the ID token authentication time and convert to milliseconds UTC. const authTimeUtc = decodedIdToken.auth_time * 1000; // Get user tokens valid after time in milliseconds UTC. const validSinceUtc = new Date(user.tokensValidAfterTime).getTime(); // Check if authentication time is older than valid since time. if (authTimeUtc < validSinceUtc) { throw new error_1.FirebaseAuthError(revocationErrorInfo); } } // All checks above passed. Return the decoded token. return decodedIdToken; }); } async function verifyIdToken(idToken, checkRevoked = false) { const isEmulator = (0, firebase_1.useEmulator)(); const idTokenVerifier = (0, token_verifier_1.createIdTokenVerifier)(serviceAccount.projectId); const decodedIdToken = await idTokenVerifier.verifyJWT(idToken, isEmulator); if (checkRevoked) { return verifyDecodedJWTNotRevokedOrDisabled(decodedIdToken, error_1.AuthClientErrorCode.ID_TOKEN_REVOKED); } return decodedIdToken; } async function verifyAndRefreshExpiredIdToken(token, refreshToken) { return await handleExpiredToken(async () => { const decodedToken = await verifyIdToken(token); return { token, decodedToken }; }, async () => { if (refreshToken) { return handleTokenRefresh(refreshToken, apiKey); } return null; }, async () => { return null; }); } function createCustomToken(uid, developerClaims) { return tokenGenerator.createCustomToken(uid, developerClaims); } async function getCustomIdAndRefreshTokens(idToken, firebaseApiKey) { const tenant = await verifyIdToken(idToken); const customToken = await createCustomToken(tenant.uid); return customTokenToIdAndRefreshTokens(customToken, firebaseApiKey); } async function deleteUser(uid) { await authRequestHandler.deleteAccount(uid); } async function setCustomUserClaims(uid, customUserClaims) { await authRequestHandler.setCustomUserClaims(uid, customUserClaims); } return { verifyAndRefreshExpiredIdToken, verifyIdToken, createCustomToken, getCustomIdAndRefreshTokens, handleTokenRefresh, deleteUser, setCustomUserClaims, getUser, }; } exports.getFirebaseAuth = getFirebaseAuth; //# sourceMappingURL=index.js.map