/** * BetterAuth client for frontend authentication. * * This client handles: * - Sign in/out operations * - Session management * - Automatic token refresh */ import { createAuthClient } from "better-auth/react"; // Note: Credentials plugin import removed - better-auth has built-in credentials support /** * Auth client instance configured for Jarvis. */ export const authClient = createAuthClient({ // Base URL for auth API baseURL: typeof window !== "undefined" ? window.location.origin : (process.env.BETTER_AUTH_URL ?? "http://localhost:3042"), // Plugins can be added here when needed plugins: [], }); /** * Export commonly used auth functions. */ export const { signIn, signOut, useSession, getSession } = authClient; /** * Sign in with username and password. * Returns the session on success, throws on failure. * * Uses direct fetch since our server accepts username (not email) * and the default BetterAuth client expects email. */ export async function signInWithCredentials(username: string, password: string): Promise { const baseURL = typeof window !== "undefined" ? window.location.origin : (process.env.BETTER_AUTH_URL ?? "http://localhost:3042"); const response = await fetch(`${baseURL}/api/auth/sign-in/credentials`, { method: "POST", headers: { "Content-Type": "application/json", }, credentials: "include", // Include cookies body: JSON.stringify({ username, password }), }); if (!response.ok) { const error = (await response.json().catch(() => ({}))) as { message?: string }; throw new Error(error.message ?? "Authentication failed"); } const data = (await response.json()) as unknown; return data; } /** * Get the current access token for API calls. * Returns null if not authenticated. */ export async function getAccessToken(): Promise { const session = await getSession(); if (!session.data?.user) { return null; } // Type assertion for custom user fields const user = session.data.user as { accessToken?: string; tokenExpiresAt?: number; }; // Check if token is expired (with 1 minute buffer) if (user.tokenExpiresAt && user.tokenExpiresAt - Date.now() < 60000) { // Token is expired or about to expire // The session will be refreshed automatically by BetterAuth // but we should return null to trigger a re-auth if needed return null; } return user.accessToken ?? null; } /** * Check if the current user is an admin. */ export async function isAdmin(): Promise { const session = await getSession(); if (!session.data?.user) { return false; } const user = session.data.user as { isAdmin?: boolean }; return user.isAdmin === true; }