60 lines
8.8 KiB
Markdown
60 lines
8.8 KiB
Markdown
# 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**.
|