@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
JavaScript
// 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