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