087fbdd176
Budget.Core: entities, DTOs, enums, FrequencyCalculator (no EF/ASP.NET deps) Budget.Infrastructure: AppDbContext, migrations, BudgetAuthorizationService Budget.Api: controllers, middleware, Program.cs — references both projects EF and Npgsql packages moved to Infrastructure; Api retains only JwtBearer, HealthChecks, and EF.Design (needed for dotnet ef CLI). Dockerfile updated to copy all three project directories before publishing. Migration namespaces updated from Budget.Api.Data.* to Budget.Infrastructure.Data.* and model type strings updated to Budget.Core.Models.* in the snapshot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
4.0 KiB
Markdown
87 lines
4.0 KiB
Markdown
# Plan: Replace Custom AuthContext with react-oidc-context
|
|
|
|
## Goal
|
|
|
|
Replace the hand-rolled `AuthContext.tsx` / `UserManager` setup with `react-oidc-context`,
|
|
which wraps `oidc-client-ts` and handles token expiry events, silent renew errors, and
|
|
session monitoring out of the box.
|
|
|
|
## Current state
|
|
|
|
- `src/Budget.Client/src/auth/authConfig.ts` — `UserManagerSettings` object
|
|
- `src/Budget.Client/src/auth/AuthContext.tsx` — custom Provider + `useAuth()` hook, module-level `UserManager`
|
|
- `src/Budget.Client/src/auth/AuthGuard.tsx` — reads from `useAuth()`
|
|
- `src/Budget.Client/src/pages/CallbackPage.tsx` — calls `userManager.signinRedirectCallback()`
|
|
- `src/Budget.Client/src/api/client.ts` — `setTokenProvider(fn)` wired in `main.tsx`
|
|
- `src/Budget.Client/src/main.tsx` — wraps app in `<AuthProvider>`
|
|
|
|
## Target state
|
|
|
|
- Remove `AuthContext.tsx`.
|
|
- `main.tsx` wraps the app in `<AuthProvider>` from `react-oidc-context` using the existing `authConfig`.
|
|
- `AuthGuard.tsx` uses `useAuth()` from `react-oidc-context`.
|
|
- `CallbackPage.tsx` becomes a thin component — `react-oidc-context` handles the callback automatically when `onSigninCallback` is provided to `AuthProvider`.
|
|
- A `TokenSync` component reads the token from `react-oidc-context` and pushes it into `api/client.ts` via `setTokenProvider`.
|
|
- `onSigninCallback` navigates to `/` on success but skips navigation when the URL contains `error`.
|
|
- `CallbackPage` reads `error` / `error_description` from URL params and renders a user-friendly error message.
|
|
|
|
## Steps
|
|
|
|
### Phase 1 — Install
|
|
|
|
1. `npm install react-oidc-context` in `src/Budget.Client/`.
|
|
|
|
### Phase 2 — Replace AuthProvider
|
|
|
|
2. Delete `src/Budget.Client/src/auth/AuthContext.tsx`.
|
|
3. Update `src/Budget.Client/src/main.tsx`:
|
|
- Import `AuthProvider` from `react-oidc-context`.
|
|
- Add `onSigninCallback` that calls `window.history.replaceState({}, '', '/')` unless the URL has `?error=`.
|
|
- Wrap the app: `<AuthProvider {...authConfig} onSigninCallback={...}>`.
|
|
- Remove the old `<AuthProvider>` import and `setTokenProvider` wiring.
|
|
4. Create `src/Budget.Client/src/auth/TokenSync.tsx`:
|
|
```tsx
|
|
import { useAuth } from 'react-oidc-context';
|
|
import { useEffect } from 'react';
|
|
import { setTokenProvider } from '../api/client';
|
|
|
|
export function TokenSync() {
|
|
const auth = useAuth();
|
|
useEffect(() => {
|
|
setTokenProvider(() => auth.user?.access_token ?? null);
|
|
}, [auth.user]);
|
|
return null;
|
|
}
|
|
```
|
|
5. Render `<TokenSync />` inside `<AuthProvider>` in `main.tsx`.
|
|
|
|
### Phase 3 — Update AuthGuard and CallbackPage
|
|
|
|
6. Update `AuthGuard.tsx` to use `useAuth` from `react-oidc-context`. The shape is:
|
|
- `auth.isLoading` — waiting for user to load
|
|
- `auth.isAuthenticated` — user is signed in
|
|
- `auth.signinRedirect()` — trigger login
|
|
7. Update `CallbackPage.tsx`:
|
|
- `react-oidc-context` processes the callback automatically; the page just needs to read `?error` / `?error_description` from `useSearchParams()` and render a friendly message if present.
|
|
- On success the `onSigninCallback` in `main.tsx` navigates away, so this component effectively only shows on error.
|
|
|
|
### Phase 4 — Clean up
|
|
|
|
8. Remove `export { userManager }` from the old AuthContext (now deleted) and fix any imports that referenced it (only `CallbackPage` should have used it).
|
|
9. Run `npm run build` — confirm zero TypeScript errors.
|
|
|
|
## Key decisions
|
|
|
|
- `authConfig.ts` stays as-is; `AuthProvider` accepts `UserManagerSettings` spread directly.
|
|
- `setTokenProvider` stays in `api/client.ts` — `TokenSync` bridges between `react-oidc-context` and the fetch client without making the client depend on React.
|
|
- Silent renew errors are handled automatically by `react-oidc-context`'s built-in event listeners — no custom wiring needed.
|
|
|
|
## Files affected
|
|
|
|
- `package.json` (new dep: `react-oidc-context`)
|
|
- `src/Budget.Client/src/main.tsx`
|
|
- `src/Budget.Client/src/auth/AuthContext.tsx` (deleted)
|
|
- `src/Budget.Client/src/auth/AuthGuard.tsx`
|
|
- New: `src/Budget.Client/src/auth/TokenSync.tsx`
|
|
- `src/Budget.Client/src/pages/CallbackPage.tsx`
|