UNPKG

next-firebase-auth-edge

Version:

Next.js Firebase Authentication for Edge and server runtimes. Compatible with latest Next.js features.

86 lines (85 loc) 3.35 kB
import { decodeJwt } from 'jose'; import { AuthError, AuthErrorCode } from '../auth/error.js'; class ClientTokenCache { cacheMap = {}; constructor() { } get(value) { if (!this.cacheMap[value]) { return value; } return this.cacheMap[value]; } set(originalValue, value) { this.cacheMap = { [originalValue]: value }; } } const idTokenCache = new ClientTokenCache(); const customTokenCache = new ClientTokenCache(); export async function getValidIdToken({ serverIdToken, refreshTokenUrl, checkRevoked }) { if (!serverIdToken) { return null; } const token = idTokenCache.get(serverIdToken); const payload = decodeJwt(token); const exp = payload?.exp ?? 0; if (!checkRevoked && exp > Date.now() / 1000) { return token || serverIdToken; } const response = await fetchApi(refreshTokenUrl); if (!response?.idToken) { throw new AuthError(AuthErrorCode.INTERNAL_ERROR, 'Refresh token endpoint returned invalid response. This URL should point to endpoint exposed by the middleware and configured using refreshTokenPath option'); } idTokenCache.set(serverIdToken, response.idToken); return response.idToken; } export async function getValidCustomToken({ serverCustomToken, refreshTokenUrl, checkRevoked }) { if (!serverCustomToken) { return null; } const token = customTokenCache.get(serverCustomToken); const payload = decodeJwt(token); const exp = payload?.exp ?? 0; if (!checkRevoked && exp > Date.now() / 1000) { return token || serverCustomToken; } const response = await fetchApi(refreshTokenUrl); if (!response) { throw new AuthError(AuthErrorCode.INTERNAL_ERROR, 'Refresh token endpoint returned invalid response. This URL should point to endpoint exposed by the middleware and configured using refreshTokenPath option.'); } if (!response.customToken) { throw new AuthError(AuthErrorCode.INTERNAL_ERROR, 'Refresh token endpoint returned empty custom token. Make sure you have set `enableCustomToken` option to `true` in `authMiddleware`'); } customTokenCache.set(serverCustomToken, response.customToken); return response.customToken; } async function mapResponseToAuthError(response, input, init) { if (response.status === 401) { const data = await safeResponse(response); return new AuthError(AuthErrorCode.INVALID_CREDENTIAL, data?.message); } const text = await safeResponse(response); return new AuthError(AuthErrorCode.INTERNAL_ERROR, `next-firebase-auth-edge: Internal request to ${init?.method ?? 'GET'} ${input.toString()} has failed: ${text}`); } function safeResponse(response) { const contentType = response.headers.get('content-type'); if (contentType && contentType.indexOf('application/json') !== -1) { return response.json(); } else { return response.text(); } } async function fetchApi(input, init) { const response = await fetch(input, { ...init, headers: { ...init?.headers, Accept: 'application/json', 'Content-Type': 'application/json' } }); if (!response.ok) { throw await mapResponseToAuthError(response, input, init); } return safeResponse(response); }