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

183 lines 6.32 kB
// src/lib/auth/providers/betterAuth.ts import { createProxyFetch } from "../../proxy/proxyFetch.js"; import { AuthError } from "../errors.js"; import * as jose from "jose"; import { BaseAuthProvider } from "./BaseAuthProvider.js"; /** * Better Auth Provider * * Supports Better Auth, a self-hosted open-source authentication solution. * Validates session tokens and JWTs issued by Better Auth server. * * Features: * - JWT validation using HMAC secret * - Session validation via Better Auth API * - Social provider support (GitHub, Google, Discord) * - Session management (inherited from BaseAuthProvider) * * @example * ```typescript * const betterAuth = new BetterAuthProvider({ * type: "better-auth", * secret: "your-better-auth-secret", * baseUrl: "https://your-app.com" * }); * * const result = await betterAuth.authenticateToken(sessionToken); * if (result.valid) { * console.log("Authenticated user:", result.user); * } * ``` */ export class BetterAuthProvider extends BaseAuthProvider { type = "better-auth"; secret; baseUrl; secretKey; constructor(config) { super(config); if (!config.secret) { throw AuthError.create("CONFIGURATION_ERROR", "Better Auth secret is required", { details: { provider: "better-auth", missingFields: ["secret"] } }); } if (!config.baseUrl) { throw AuthError.create("CONFIGURATION_ERROR", "Better Auth baseUrl is required", { details: { provider: "better-auth", missingFields: ["baseUrl"] } }); } this.secret = config.secret; this.baseUrl = config.baseUrl.replace(/\/$/, ""); // Remove trailing slash this.secretKey = new TextEncoder().encode(this.secret); } /** * Validate Better Auth token (session or JWT) */ async authenticateToken(token, _context) { // Try JWT validation first (if it looks like a JWT) if (token.includes(".") && token.split(".").length === 3) { const jwtResult = await this.validateJWT(token); if (jwtResult.valid) { return jwtResult; } } // Fall back to session validation via API return this.validateSessionViaAPI(token); } /** * Validate JWT using the secret */ async validateJWT(token) { try { const { payload } = await jose.jwtVerify(token, this.secretKey); if (!payload.sub) { return { valid: false, error: "Token missing required 'sub' claim", }; } const user = { id: payload.sub, email: payload.email, name: payload.name, picture: payload.picture, emailVerified: payload.email_verified, roles: payload.roles || [], permissions: payload.permissions || [], metadata: payload.metadata, }; return { valid: true, payload: payload, user, expiresAt: payload.exp ? new Date(payload.exp * 1000) : undefined, tokenType: "jwt", }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : String(error), }; } } /** * Validate session via Better Auth API */ async validateSessionViaAPI(sessionToken) { try { const proxyFetch = createProxyFetch(); const response = await proxyFetch(`${this.baseUrl}/api/auth/session`, { headers: { Cookie: `better-auth.session_token=${sessionToken}`, }, signal: AbortSignal.timeout(5000), }); if (!response.ok) { return { valid: false, error: `Session validation failed: HTTP ${response.status}`, }; } const session = (await response.json()); if (!session.user) { return { valid: false, error: "Invalid session", }; } const user = { id: session.user.id, email: session.user.email, name: session.user.name, picture: session.user.image, emailVerified: session.user.emailVerified, roles: session.user.roles || [], permissions: session.user.permissions || [], createdAt: session.user.createdAt ? new Date(session.user.createdAt) : undefined, metadata: session.user, }; return { valid: true, payload: session, user, expiresAt: session.session?.expiresAt ? new Date(session.session.expiresAt) : undefined, tokenType: "session", }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : String(error), }; } } /** * Health check */ async healthCheck() { try { const proxyFetch = createProxyFetch(); // Better Auth typically exposes session endpoint that we can check const response = await proxyFetch(`${this.baseUrl}/api/auth/session`, { signal: AbortSignal.timeout(5000), }); // Even a 401 means the endpoint is working const isReachable = response.status < 500; return { healthy: isReachable, providerConnected: isReachable, sessionStorageHealthy: true, }; } catch (error) { return { healthy: false, providerConnected: false, sessionStorageHealthy: true, error: error instanceof Error ? error.message : String(error), }; } } } //# sourceMappingURL=betterAuth.js.map