feat(auth): AuthProvider context + useAuth hook
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import type { User } from "@/types";
|
||||
|
||||
/**
|
||||
* Tri-state lifecycle for the auth surface.
|
||||
*
|
||||
* - "loading" → we haven't asked /api/auth/me yet, or the result
|
||||
* is still in flight. Route guards render a
|
||||
* placeholder so we don't bounce the user to
|
||||
* /login before the cookie has had a chance to
|
||||
* prove itself.
|
||||
* - "authenticated" → /me returned a real User. The rest of the app
|
||||
* can safely render protected surfaces.
|
||||
* - "unauthenticated"→ /me failed (no cookie, expired cookie, server
|
||||
* down). Route guards redirect to /login.
|
||||
*/
|
||||
export type AuthStatus = "loading" | "authenticated" | "unauthenticated";
|
||||
|
||||
export interface AuthContextValue {
|
||||
status: AuthStatus;
|
||||
user: User | null;
|
||||
/**
|
||||
* Authenticate against /api/auth/login. Throws `ApiError` on failure
|
||||
* (with `code` = "invalid_credentials" | "account_disabled" |
|
||||
* "rate_limited" | "error"). On success, status flips to
|
||||
* "authenticated" and `user` is populated.
|
||||
*/
|
||||
login: (username: string, password: string) => Promise<void>;
|
||||
/**
|
||||
* Hit /api/auth/logout and clear local state. Always succeeds
|
||||
* locally — even if the server is unreachable, the operator is
|
||||
* effectively signed out from the SPA's perspective.
|
||||
*/
|
||||
logout: () => Promise<void>;
|
||||
/**
|
||||
* Re-run the /api/auth/me probe. Useful after a profile edit on the
|
||||
* admin pages so the sidebar reflects the new role without a
|
||||
* full reload.
|
||||
*/
|
||||
refresh: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const AuthContext = createContext<AuthContextValue | null>(null);
|
||||
|
||||
/**
|
||||
* Read the current auth context. Throws when used outside of an
|
||||
* `<AuthProvider>` so consumers fail loudly during development instead
|
||||
* of silently rendering the unauthenticated branch.
|
||||
*/
|
||||
export function useAuth(): AuthContextValue {
|
||||
const ctx = useContext(AuthContext);
|
||||
if (!ctx) {
|
||||
throw new Error("useAuth must be used inside <AuthProvider>");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user