import { createAuthClient } from 'better-auth/client' import { apiUrl } from './env' // Better Auth browser client. It targets the worker's catch-all `/api/auth/**` // routes (generated from src/auth.ts in the functions package). Pass the FULL // auth base (`/auth`): better-auth's withPath only appends its default // `/api/auth` when baseURL has no path, and apiUrl() already carries `/api`, so // a bare apiUrl() would leave the client calling `/api/get-session` (404) and // the app would loop back to /login. Cookies ride every request so the session // round-trips across origins. export const authClient = createAuthClient({ baseURL: `${apiUrl()}/auth` }) export type AuthSession = { user?: { email?: string | null name?: string | null image?: string | null } | null expires?: string } // Thrown by signInWithPassword when the email/password pair is wrong. export const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS' // Thrown by registerWithPassword when the email is already taken. export const EMAIL_IN_USE = 'EMAIL_IN_USE' export async function getAuthSession(): Promise { const { data } = await authClient.getSession() if (!data?.user) { return {} } return { user: { email: data.user.email, name: data.user.name ?? null, image: data.user.image ?? null, }, expires: data.session?.expiresAt ? new Date(data.session.expiresAt).toISOString() : undefined, } } // Sign in with an email and password. On success Better Auth sets the session // cookie; on bad credentials this throws INVALID_CREDENTIALS. export async function signInWithPassword( email: string, password: string, _redirectPath = '/app', ) { const { error } = await authClient.signIn.email({ email, password }) if (error) { throw new Error(INVALID_CREDENTIALS) } } // Create a new account. Better Auth signs the user in on success (sets the // session cookie), so they land logged in. export async function registerWithPassword( email: string, password: string, options: { name?: string; redirectPath?: string } = {}, ) { const { error } = await authClient.signUp.email({ email, password, // Better Auth requires a name; fall back to the local-part of the email. name: options.name?.trim() || email.split('@')[0], }) if (error) { // Better Auth returns 422 (UNPROCESSABLE_ENTITY) when the email is taken. if (error.status === 422 || /exist|taken/i.test(error.message ?? '')) { throw new Error(EMAIL_IN_USE) } throw new Error('Unable to create account') } } export async function signOut() { const { error } = await authClient.signOut() if (error) { throw new Error('Unable to sign out') } }