diff --git a/README.md b/README.md index d88f4b3..ab78fdb 100644 --- a/README.md +++ b/README.md @@ -136,14 +136,15 @@ The template includes a complete auth setup designed to work with the companion - **AuthProvider** (`src/lib/auth.tsx`) — React context tracking `isAuthenticated`, `isLoading`, `email`, and `userId` - **Automatic token refresh** (`src/api/api.ts`) — 401 responses trigger a refresh attempt; concurrent requests are coalesced into a single refresh call -- **Session check on load** — `GET /auth/me` validates the session on mount +- **Session check on load** — `GET /v1/auth/me` validates the session on mount - **Auth in router context** — `auth` is available in route `beforeLoad` for route guards ### How it works -1. Backend sets httpOnly cookies (`app_access` + `app_refresh`) on login +1. Backend sets httpOnly cookies (`app_access` + `app_refresh`) on login; the + refresh cookie is path-scoped to `/v1/auth/refresh` 2. All API requests include cookies via `credentials: "include"` -3. On 401, the client POSTs to `/auth/refresh` to rotate tokens +3. On 401, the client POSTs to `/v1/auth/refresh` to rotate tokens 4. If refresh succeeds, the original request is retried transparently 5. If refresh fails, `onUnauthorized` fires and auth state is cleared @@ -254,7 +255,7 @@ await renderWithFileRoutes(
, { }) ``` -MSW handlers are configured in `src/api/handlers.ts`. A default handler for `/auth/me` (returns 401) is included to suppress warnings during tests. +MSW handlers are configured in `src/api/handlers.ts`. A default handler for `/v1/auth/me` (returns 401) is included to suppress warnings during tests. ## Project Structure @@ -316,7 +317,7 @@ Set `VITE_LOG_LEVEL` to control the minimum level (default: `debug` in dev, `war ### Feature Flags -`FeatureFlagProvider` fetches flags from the API's `GET /flags` endpoint (cached via TanStack Query, refetches on window focus). If the API call fails, it falls back to `VITE_FEATURE_*` env vars. +`FeatureFlagProvider` fetches flags from the API's `GET /v1/flags` endpoint (cached via TanStack Query, refetches on window focus). If the API call fails, it falls back to `VITE_FEATURE_*` env vars. Use the `useFeatureFlag("flag_name")` hook or the `` component for conditional rendering. diff --git a/src/api/api.ts b/src/api/api.ts index 6266029..23ad210 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -14,7 +14,7 @@ let refreshPromise: Promise | null = null async function attemptRefresh(): Promise { try { - const res = await fetch(`${baseUrl}/auth/refresh`, { + const res = await fetch(`${baseUrl}/v1/auth/refresh`, { method: "POST", credentials: "include", }) @@ -37,7 +37,10 @@ export const api = ky.create({ // Don't try to refresh if this IS the refresh request (prevent loop) const url = request.url - if (url.includes("/auth/refresh") || url.includes("/auth/jwt/logout")) { + if ( + url.includes("/v1/auth/refresh") || + url.includes("/v1/auth/jwt/logout") + ) { if (onUnauthorized) onUnauthorized() return } diff --git a/src/api/handlers.ts b/src/api/handlers.ts index cad6578..34ddf28 100644 --- a/src/api/handlers.ts +++ b/src/api/handlers.ts @@ -9,6 +9,6 @@ import { http, HttpResponse, type HttpHandler } from "msw" */ export const handlers: HttpHandler[] = [ - // Return 401 for /auth/me by default (unauthenticated) - http.get("*/auth/me", () => HttpResponse.json(null, { status: 401 })), + // Return 401 for /v1/auth/me by default (unauthenticated) + http.get("*/v1/auth/me", () => HttpResponse.json(null, { status: 401 })), ] diff --git a/src/lib/auth.tsx b/src/lib/auth.tsx index 073100c..75b6141 100644 --- a/src/lib/auth.tsx +++ b/src/lib/auth.tsx @@ -45,7 +45,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const logout = useCallback(async () => { try { - await api.post("auth/jwt/logout") + await api.post("v1/auth/jwt/logout") } catch { // Clear state regardless of fetch success } @@ -61,7 +61,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const checkAuth = useCallback(async () => { try { const user = await api - .get("auth/me") + .get("v1/auth/me") .json<{ id: string; email: string; name: string | null }>() setIsAuthenticated(true) setEmail(user.email) diff --git a/src/lib/feature-flags.tsx b/src/lib/feature-flags.tsx index e50cc80..3b47282 100644 --- a/src/lib/feature-flags.tsx +++ b/src/lib/feature-flags.tsx @@ -39,7 +39,7 @@ export function FeatureFlagProvider({ queryKey: ["feature-flags"], queryFn: async () => { try { - return await api.get("flags").json() + return await api.get("v1/flags").json() } catch { return getEnvFlags() }