Files
jobtrackingapp/docs/redesign/audit/API_AND_IDENTITY.md
2026-09-09 19:29:39 +02:00

60 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API integration and identity boundaries
## Transport
[api.ts](../../../job-tracker-ui/src/api.ts) is the shared Axios transport. Production uses `/api`; local development defaults to port 5202 unless `NEXT_PUBLIC_API_BASE_URL` overrides it. The value is embedded in the static build. All discovered direct frontend `api`/`axios` calls are in [API_CALL_SITES](API_CALL_SITES.md); UI wrappers such as `cvBuilder`, `aiWorkspace` and `applicationWorkspace` are included. SDK OAuth/navigation and server-to-server provider calls are separate from that census.
Credentialed requests use the HttpOnly auth cookie and XSRF handling. B02 adds immutable session provenance, cancellation and late-response rejection. Downloads use authorized blob requests where required; B05 CSV bytes must not be decoded/re-encoded through `Blob.text()`. Error handling uses shared extraction plus screen-specific recovery. A new fetch/cache/form abstraction must preserve all of these behaviors.
## Domain contract map
Paths are relative to `/api`. See the linked census for each method, interpolated ID and exact source line. DTOs are in [frontend types](../../../job-tracker-ui/src/types.ts), domain wrappers, and [API controllers](../../../JobTrackerApi/Controllers).
| Frontend owner | API groups | Identity / mutation contract |
|---|---|---|
| Login/account/security cards | `/auth/config`, `/auth/me`, login/register/reset/verification, `/auth/profile`, avatar, email-change, 2FA, sessions, language preferences | Public challenge/setup entry versus authenticated self-account actions; client never chooses authoritative owner |
| Table/Kanban/edit/add | `/jobapplications`, `/board`, choices, duplicate-check, status, restore; `/companies` and relationship | Owner-scoped job/company lookup and associations; status changes and soft deletion distinct from creation intent |
| Capture/discovery | `/jobimport/preview`, `/job-discovery/search`, saved-searches/results, Gmail suggested jobs | Review/preview and create separate; B01 connection enforcement; saved searches belong to account |
| Personal analytics/reminders | Dashboard/statistics call sites, reminders/rules, notification-settings | Current-account reporting and dates; global/admin reporting separate |
| Full/overlay job workspace | `/jobapplications/:id/workspace`, timeline, analysis, match, CV, tailoring, cover-letter, drafts, submitted-packages, checklist, interview-prep, follow-up | Job owner boundary plus nested artifact IDs; workspace aggregate reads do not authorize child writes |
| Profile/evidence | `/career/profile`, versions/restore, `/career/evidence`, `/profile-cv/*` | Principal-owned singleton; extraction proposal review; history/projection distinct from CV overrides |
| CV library/editor | `/cv/*` through `cvBuilderApi`, themes, outline, variant save/copy/history/publish/preview/export | Private owner + resource ID; job association validates same owner; B03 admission shared with other PDF callers |
| Public CV | Public-CV slug endpoints | Anonymous access only through explicitly published variant; not arbitrary private ID download |
| Contextual AI | `/ai/usage`, `/jobapplications/:id/ai/modules`, generate/history; profile and CV AI endpoints | Server entitlement, context ownership/consent and quotas; UI accept step does not replace backend authorization |
| Documents | `/attachments/*`, submitted-package attachments, PDF export | Authorized upload/download/delete and scoped references; preserve content disposition and blob handling |
| Correspondence/mail | `/correspondence/*`, `/gmail/*`, `/microsoft-graph/*`, `/imap/*`, `/email/providers`, message, drafts, send | OAuth mailbox distinct from login; owner/job association, draft revision, send attempt/delivery state |
| Calendar | `/calendar/status`, `/calendar/jobs/:id/events`, `events.ics` | Owned job dates + provider permission; date-only and UTC timestamp semantics remain distinct |
| Operations/notifications | `/operations`, cancel/retry, `/notifications`, read/dismiss | Owner-scoped asynchronous work; rerender does not mean enqueue again |
| Export/lifecycle/billing | `/export/jobs`, `/export/account`, `/backup/encrypted`, `/account-lifecycle/*`, `/billing/status`, checkout/portal | Owner data; recent-auth/destructive/provider constraints; B05 at CSV boundary |
| Admin | `/users`, `/admin/audit`, `/admin/system` and maintenance/email/AI subroutes | Server Admin role; sensitive system views kept separate from personal workspace |
No backend contract changes are proposed for Phase 1. API endpoint names and old route query values are compatibility surface even if new screen labels differ.
## Authentication and authorization
[Program.cs](../../../JobTrackerApi/Program.cs#L299) configures the smart authentication selector and local/provider validation. Local bearer or cookie JWT validation includes issuer/audience/signature/lifetime and a live local-session validation step. Production account-aware policies resolve authenticated identity before limiting. Relevant middleware order is forwarded headers/security/routing/CORS and CSRF handling, then [authentication → authorization → rate limiter → B02 SessionContextMiddleware → controllers](../../../JobTrackerApi/Program.cs#L654). Do not move owner limiting ahead of authentication during shell/API cleanup.
[CurrentUserService](../../../JobTrackerApi/Services/CurrentUserService.cs) derives local owner identity from the principal. Its background-owner scope cannot replace an HTTP request identity. [JobTrackerContext](../../../JobTrackerApi/Data/JobTrackerContext.cs#L132) uses live owner evaluation and fail-closed query filters. These are additional guards; not a license to remove explicit ownership checks from services or to introduce unscoped `IgnoreQueryFilters()` calls.
The [CV service](../../../JobTrackerApi/Services/CvVariantService.cs#L74) looks up `id + owner` and validates job associations with `jobId + owner`. Its explicit public lookup bypasses ordinary filters only for the published-slug path. Career Profile PUT derives the owner from current authentication and updates that singleton, explaining why B02 session provenance is separate from ID-based authorization. The old retained A form replay under B was not simply an IDOR. B02 now rejects retained/missing/forged session provenance server-side; the client also unmounts/aborts private state.
Admin controllers enforce Admin roles; Pro operations have server entitlement checks. The permissive default in `AccountPlanContext` is for isolated previews, while routed App supplies server-backed values. It is not proof that an ordinary user can obtain Pro server access. The frontend's auth/config and role-based navigation are presentation logic; a stale or crafted frontend must remain constrained by the API.
This is an integration map with targeted service verification, not a fresh claim that every authorization path in the application has been penetration-tested. Keep the permanent B01–B06 tests and original independent audit evidence.
## State and storage map
| State | Current scope | Preservation condition |
|---|---|---|
| Auth credential | HttpOnly cookie; CSRF companion cookie | Do not move tokens into browser storage or let client headers select owner |
| `authUserKey`, auth persistence preferences | Browser identity marker/preferences | Not an authoritative server identity; B02 handles account-generation change |
| `authSessionContext` storage signal | Cross-tab change notification | Immutable per page context; stale work aborts/reauthenticates, it does not adopt B's identity |
| Form/editor state and workspace tab cache | React/component memory | Dispose on B02 invalidation; preserve unsaved-navigation guard within one identity |
| Dashboard visibility | `dashboardPrefs:<owner>` localStorage | Owner-scoped, five booleans; not cross-device persistence |
| Saved job views, headline fallback, onboarding dismissal | Owner-keyed localStorage | Never rehydrate A's private view/draft data into B's page |
| Theme/language/sidebar collapse/table page size and columns | Device/browser preferences (language also server-hydrated) | These are presentation preferences, not private document caches |
| Pro notice dismissal | Feature-keyed sessionStorage | Presentation-only; no CV/account content, entitlement still server-derived |
| Mail drafts / application drafts / CV history | Server persistence with domain-specific APIs | Preserve each existing contract; do not replace with a generic browser draft mechanism |
Sources: [auth.ts](../../../job-tracker-ui/src/auth.ts), [sessionBoundary.ts](../../../job-tracker-ui/src/sessionBoundary.ts), [App.tsx](../../../job-tracker-ui/src/App.tsx), `SavedViewsMenu`, `DashboardView`, `ProfilePage`, `ProFeatureNotice`. Any future cache must be account-scoped and must not replay mutations after the page's identity generation changes. Record revision conflicts, identity isolation and duplicate-creation idempotency remain **three distinct contracts**.