UNPKG

next-firebase-auth-edge

Version:

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

82 lines (81 loc) 2.98 kB
import { getSdkVersion } from '../auth/auth-request-handler.js'; import { formatString } from '../auth/utils.js'; const FIREBASE_APP_CHECK_V1_API_URL_FORMAT = 'https://firebaseappcheck.googleapis.com/v1/projects/{projectId}/apps/{appId}:exchangeCustomToken'; const FIREBASE_APP_CHECK_CONFIG_HEADERS = { 'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}` }; export class AppCheckApiClient { credential; constructor(credential) { this.credential = credential; } async exchangeToken(customToken, appId) { const url = await this.getUrl(appId); const token = await this.credential.getAccessToken(false); const response = await fetch(url, { method: 'POST', headers: { ...FIREBASE_APP_CHECK_CONFIG_HEADERS, Authorization: `Bearer ${token.accessToken}` }, body: JSON.stringify({ customToken }) }); if (response.ok) { return this.toAppCheckToken(response); } throw await this.toFirebaseError(response); } async getUrl(appId) { const projectId = await this.credential.getProjectId(); const urlParams = { projectId, appId }; const baseUrl = formatString(FIREBASE_APP_CHECK_V1_API_URL_FORMAT, urlParams); return formatString(baseUrl); } async toFirebaseError(response) { const data = (await response.json()); const error = data.error || {}; let code = 'unknown-error'; if (error.status && error.status in APP_CHECK_ERROR_CODE_MAPPING) { code = APP_CHECK_ERROR_CODE_MAPPING[error.status]; } const message = error.message || `Unknown server error: ${response.text}`; return new FirebaseAppCheckError(code, message); } async toAppCheckToken(response) { const data = await response.json(); const token = data.token; const ttlMillis = this.stringToMilliseconds(data.ttl); return { token, ttlMillis }; } stringToMilliseconds(duration) { if (!duration.endsWith('s')) { throw new FirebaseAppCheckError('invalid-argument', '`ttl` must be a valid duration string with the suffix `s`.'); } const seconds = duration.slice(0, -1); return Math.floor(Number(seconds) * 1000); } } export const APP_CHECK_ERROR_CODE_MAPPING = { ABORTED: 'aborted', INVALID_ARGUMENT: 'invalid-argument', INVALID_CREDENTIAL: 'invalid-credential', INTERNAL: 'internal-error', PERMISSION_DENIED: 'permission-denied', UNAUTHENTICATED: 'unauthenticated', NOT_FOUND: 'not-found', UNKNOWN: 'unknown-error' }; export class FirebaseAppCheckError extends Error { code; constructor(code, message) { super(`(${code}): ${message}`); this.code = code; Object.setPrototypeOf(this, FirebaseAppCheckError.prototype); } }