Split into Budget.Core / Budget.Infrastructure / Budget.Api projects

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>
This commit is contained in:
Spencer Twaddle
2026-05-02 16:30:31 -05:00
parent c3d1420c4c
commit 087fbdd176
37 changed files with 826 additions and 78 deletions
+86
View File
@@ -0,0 +1,86 @@
# 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`