UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

231 lines 8.02 kB
// src/lib/auth/providers/firebase.ts import { BaseAuthProvider } from "./BaseAuthProvider.js"; import { AuthError } from "../errors.js"; import { logger } from "../../utils/logger.js"; import { createProxyFetch } from "../../proxy/proxyFetch.js"; import * as jose from "jose"; /** * Firebase Authentication Provider * * Supports Firebase ID token validation using Google's public keys. * Can validate tokens locally or via Firebase REST API. * * Features: * - JWT validation using Google's public keys * - Token verification via Firebase REST API * - Custom claims extraction for roles/permissions * * @example * ```typescript * const firebase = new FirebaseAuthProvider({ * type: "firebase", * projectId: "your-project-id" * }); * * const result = await firebase.authenticateToken(idToken); * if (result.valid) { * console.log("Authenticated user:", result.user); * } * ``` */ export class FirebaseAuthProvider extends BaseAuthProvider { type = "firebase"; projectId; apiKey; serviceAccount; jwks = null; constructor(config) { super(config); if (!config.projectId) { throw AuthError.create("CONFIGURATION_ERROR", "Firebase projectId is required", { details: { missingFields: ["projectId"] } }); } this.projectId = config.projectId; this.apiKey = config.apiKey; this.serviceAccount = config.serviceAccount; } /** * Initialize JWKS for Firebase token verification */ async initialize() { // Firebase uses Google's secure token service public keys const jwksUrl = new URL("https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"); this.jwks = jose.createRemoteJWKSet(jwksUrl); logger.debug(`Firebase provider initialized for project: ${this.projectId}`); } /** * Validate Firebase ID token */ async authenticateToken(token, _context) { if (!this.jwks) { await this.initialize(); } try { const jwks = this.jwks; if (!jwks) { throw AuthError.create("PROVIDER_INIT_FAILED", "Firebase JWKS was not initialized", { details: { provider: "firebase" } }); } // Verify the token using Google's public keys const { payload } = await jose.jwtVerify(token, jwks, { issuer: `https://securetoken.google.com/${this.projectId}`, audience: this.projectId, }); // Extract user info from Firebase token claims const user = this.payloadToUser(payload); return { valid: true, payload: payload, user, expiresAt: payload.exp ? new Date(payload.exp * 1000) : undefined, tokenType: "jwt", }; } catch (error) { // If local validation fails and API key is available, try REST API if (this.apiKey) { return this.validateViaApi(token); } return { valid: false, error: error instanceof Error ? error.message : String(error), }; } } /** * Validate token via Firebase REST API */ async validateViaApi(token) { try { const proxyFetch = createProxyFetch(); const response = await proxyFetch(`https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${this.apiKey}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ idToken: token }), signal: AbortSignal.timeout(5000), }); if (!response.ok) { const error = (await response.json()); return { valid: false, error: error.error?.message || `Firebase API returned ${response.status}`, }; } const data = (await response.json()); const users = data.users || []; if (users.length === 0) { return { valid: false, error: "User not found", }; } const userData = users[0]; const user = this.firebaseUserToAuthUser(userData); return { valid: true, payload: userData, user, tokenType: "jwt", }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : String(error), }; } } /** * Convert JWT payload to AuthUser */ payloadToUser(payload) { // Extract custom claims const customClaims = payload; return { id: payload.sub, email: payload.email, name: payload.name, picture: payload.picture, emailVerified: payload.email_verified, roles: customClaims.roles || [], permissions: customClaims.permissions || [], metadata: { firebase: { sign_in_provider: payload.firebase?.sign_in_provider || "unknown", identities: payload.firebase?.identities, }, }, }; } /** * Convert Firebase user data to AuthUser */ firebaseUserToAuthUser(userData) { let customAttributes = {}; if (userData.customAttributes) { try { customAttributes = JSON.parse(userData.customAttributes); } catch { logger.warn("Failed to parse Firebase customAttributes, treating as empty"); } } return { id: userData.localId, email: userData.email, name: userData.displayName, picture: userData.photoUrl, emailVerified: userData.emailVerified, roles: customAttributes.roles || [], permissions: customAttributes.permissions || [], createdAt: userData.createdAt ? new Date(parseInt(userData.createdAt)) : undefined, lastLoginAt: userData.lastLoginAt ? new Date(parseInt(userData.lastLoginAt)) : undefined, metadata: { providerUserInfo: userData.providerUserInfo, }, }; } /** * Get user by ID via Firebase REST API * Requires API key */ async getUser(_userId) { if (!this.apiKey) { logger.warn("Firebase API key required for user lookup"); return null; } // Firebase REST API doesn't support direct user lookup by UID // This would require Admin SDK or custom backend logger.warn("Direct user lookup by ID requires Firebase Admin SDK which is not supported in browser/edge environments"); return null; } /** * Health check */ async healthCheck() { try { // Check if we can fetch the JWKS const proxyFetch = createProxyFetch(); const response = await proxyFetch("https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com", { signal: AbortSignal.timeout(5000) }); return { healthy: response.ok, providerConnected: response.ok, sessionStorageHealthy: true, }; } catch (error) { return { healthy: false, providerConnected: false, sessionStorageHealthy: true, error: error instanceof Error ? error.message : String(error), }; } } } //# sourceMappingURL=firebase.js.map