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

275 lines 10.3 kB
// src/lib/auth/providers/auth0.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"; /** * Auth0 Authentication Provider * * Supports JWT validation with JWKS for Auth0-issued tokens. * Uses jose library for JWT verification against Auth0's JWKS endpoint. * * Features: * - JWT validation with JWKS * - User profile fetching (requires Management API token) * - Role and permission extraction from token claims * * @example * ```typescript * const auth0 = new Auth0Provider({ * type: "auth0", * domain: "your-tenant.auth0.com", * clientId: "your-client-id", * audience: "https://your-api.example.com" * }); * * const result = await auth0.authenticateToken(bearerToken); * if (result.valid) { * console.log("Authenticated user:", result.user); * } * ``` */ export class Auth0Provider extends BaseAuthProvider { type = "auth0"; domain; clientId; audience; rolesNamespace; permissionsNamespace; jwks = null; constructor(config) { super(config); if (!config.domain) { throw AuthError.create("CONFIGURATION_ERROR", "Auth0 domain is required", { details: { missingFields: ["domain"] } }); } if (!config.clientId) { throw AuthError.create("CONFIGURATION_ERROR", "Auth0 clientId is required", { details: { missingFields: ["clientId"] } }); } this.domain = config.domain; this.clientId = config.clientId; this.audience = config.audience; // Allow custom namespaces for roles/permissions claims. // If no namespace is configured, fall back to standard "roles" / "permissions" claims. this.rolesNamespace = config.options?.rolesNamespace; this.permissionsNamespace = config.options?.permissionsNamespace; } /** * Initialize JWKS for JWT verification */ async initialize() { try { const jwksUrl = new URL(`https://${this.domain}/.well-known/jwks.json`); this.jwks = jose.createRemoteJWKSet(jwksUrl); logger.debug(`Auth0 provider initialized for domain: ${this.domain}`); } catch (error) { throw AuthError.create("PROVIDER_INIT_FAILED", "Failed to initialize Auth0 JWKS", { cause: error instanceof Error ? error : new Error(String(error)) }); } } /** * Validate Auth0 JWT token */ async authenticateToken(token, _context) { if (!this.jwks) { await this.initialize(); } try { if (!this.jwks) { return { valid: false, error: "Auth0 JWKS not initialized", }; } const { payload } = await jose.jwtVerify(token, this.jwks, { issuer: `https://${this.domain}/`, audience: this.audience, }); const auth0Payload = payload; // Validate audience / authorized party against clientId if (this.audience) { const aud = auth0Payload.aud; const audArray = Array.isArray(aud) ? aud : [aud]; if (!audArray.includes(this.audience)) { return { valid: false, error: `Token audience does not match expected audience: ${this.audience}`, }; } } if (this.clientId) { const aud = auth0Payload.aud; const azp = payload.azp; const audArray = Array.isArray(aud) ? aud : [aud]; if (azp) { if (azp !== this.clientId) { return { valid: false, error: `Token azp claim "${azp}" does not match clientId "${this.clientId}"`, }; } } else if (!audArray.includes(this.clientId)) { return { valid: false, error: `Token audience does not include clientId "${this.clientId}"`, }; } } // Extract roles: use custom namespace if configured, otherwise standard "roles" claim const rolesKey = this.rolesNamespace ?? "roles"; const permissionsKey = this.permissionsNamespace ?? "permissions"; const roles = payload[rolesKey] || auth0Payload.roles || []; const permissions = payload[permissionsKey] || auth0Payload.permissions || []; // Extract user information from token const user = { id: auth0Payload.sub, email: auth0Payload.email, name: auth0Payload.name, picture: auth0Payload.picture, emailVerified: auth0Payload.email_verified, roles, permissions, metadata: { iss: auth0Payload.iss, aud: auth0Payload.aud, }, }; return { valid: true, payload: payload, user, expiresAt: new Date(auth0Payload.exp * 1000), tokenType: "jwt", }; } catch (error) { const message = error instanceof Error ? error.message : String(error); logger.warn("Auth0 token validation failed:", message); return { valid: false, error: message, }; } } /** * Fetch user profile from Auth0 Management API * Note: Requires AUTH0_MANAGEMENT_TOKEN environment variable */ async getUser(userId) { const managementToken = process.env.AUTH0_MANAGEMENT_TOKEN; if (!managementToken) { logger.warn("AUTH0_MANAGEMENT_TOKEN not set, cannot fetch user profile"); return null; } try { const proxyFetch = createProxyFetch(); const response = await proxyFetch(`https://${this.domain}/api/v2/users/${encodeURIComponent(userId)}`, { headers: { Authorization: `Bearer ${managementToken}`, }, }); if (!response.ok) { if (response.status === 404) { return null; } throw AuthError.create("PROVIDER_ERROR", `Auth0 API returned ${response.status}`, { details: { statusCode: response.status } }); } const data = (await response.json()); return { id: data.user_id, email: data.email, name: data.name, picture: data.picture, emailVerified: data.email_verified, roles: data.app_metadata?.roles || [], permissions: data.app_metadata ?.permissions || [], createdAt: data.created_at ? new Date(data.created_at) : undefined, lastLoginAt: data.last_login ? new Date(data.last_login) : undefined, metadata: data.user_metadata, }; } catch (error) { logger.error("Failed to fetch Auth0 user:", error); throw error; } } /** * Get user by email from Auth0 Management API */ async getUserByEmail(email) { const managementToken = process.env.AUTH0_MANAGEMENT_TOKEN; if (!managementToken) { logger.warn("AUTH0_MANAGEMENT_TOKEN not set, cannot fetch user by email"); return null; } try { const proxyFetch = createProxyFetch(); const response = await proxyFetch(`https://${this.domain}/api/v2/users-by-email?email=${encodeURIComponent(email)}`, { headers: { Authorization: `Bearer ${managementToken}`, }, }); if (!response.ok) { throw AuthError.create("PROVIDER_ERROR", `Auth0 API returned ${response.status}`, { details: { statusCode: response.status } }); } const users = (await response.json()); if (users.length === 0) { return null; } const data = users[0]; return { id: data.user_id, email: data.email, name: data.name, picture: data.picture, emailVerified: data.email_verified, roles: data.app_metadata?.roles || [], permissions: data.app_metadata ?.permissions || [], createdAt: data.created_at ? new Date(data.created_at) : undefined, lastLoginAt: data.last_login ? new Date(data.last_login) : undefined, metadata: data.user_metadata, }; } catch (error) { logger.error("Failed to fetch Auth0 user by email:", error); throw error; } } /** * Health check */ async healthCheck() { try { const proxyFetch = createProxyFetch(); const response = await proxyFetch(`https://${this.domain}/.well-known/openid-configuration`); return { healthy: response.ok, providerConnected: response.ok, sessionStorageHealthy: true, error: response.ok ? undefined : `HTTP ${response.status}`, }; } catch (error) { return { healthy: false, providerConnected: false, sessionStorageHealthy: true, error: error instanceof Error ? error.message : String(error), }; } } } //# sourceMappingURL=auth0.js.map