Files
hope-events/frontend/src/lib/auth.ts
T
joshuaandClaude Opus 5.5 545f3d913e Add shared API types and type the smaller frontend pages
Expand src/types with User, Event, EventOption, Registration, Payment,
Ticket, paginated results and dashboard stats, modelled on the Prisma
schema. apiFetch now defaults to unknown instead of any. Dashboard
loaders are wrapped in useCallback so their effects can list them as
dependencies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:10:01 +02:00

60 lines
2.0 KiB
TypeScript

export type AuthUser = {
id: string;
name: string;
email: string;
role?: "admin" | "supervisor" | "staff" | "user";
phoneNumber?: string | null;
notificationPreference?: "email" | "whatsapp" | "both";
twoFactorEnabled?: boolean;
twoFactorMethod?: TwoFactorMethod | null;
// True for roles that must use 2FA (admin, supervisor) — it can't be turned off.
twoFactorRequired?: boolean;
};
export type AuthResponse = AuthUser & {
token: string;
// Only present when the user ticked "Trust this device" on the 2FA step.
deviceToken?: string;
};
export type TwoFactorMethod = "email" | "whatsapp";
// What the login endpoint returns instead of a token when the account has 2FA on:
// a code was sent to `destination` (masked) and must be exchanged for the token.
export type TwoFactorChallenge = {
twoFactorRequired?: true;
challengeId: string;
method: TwoFactorMethod;
destination: string;
// The role requires 2FA and it wasn't on yet: verifying this code turns it on.
enrolling?: boolean;
};
export function isTwoFactorChallenge(res: unknown): res is TwoFactorChallenge {
return !!res && typeof res === "object" && (res as TwoFactorChallenge).twoFactorRequired === true;
}
const TOKEN_KEY = "hope_events_token";
// Remembers this browser after a 2FA code so the next login can skip it. Kept on
// logout on purpose — it identifies the device, not the session.
const DEVICE_KEY = "hope_events_device";
export function saveDeviceToken(token: string) {
if (typeof window !== "undefined") localStorage.setItem(DEVICE_KEY, token);
}
export function getDeviceToken(): string | null {
if (typeof window === "undefined") return null;
return localStorage.getItem(DEVICE_KEY);
}
export function saveToken(token: string) {
if (typeof window !== "undefined") localStorage.setItem(TOKEN_KEY, token);
}
export function getToken(): string | null {
if (typeof window === "undefined") return null;
return localStorage.getItem(TOKEN_KEY);
}
export function clearToken() {
if (typeof window !== "undefined") localStorage.removeItem(TOKEN_KEY);
}