diff --git a/documentation/AE__Permissions_and_Security.md b/documentation/AE__Permissions_and_Security.md index 3e206bad..2a955ea0 100644 --- a/documentation/AE__Permissions_and_Security.md +++ b/documentation/AE__Permissions_and_Security.md @@ -86,6 +86,18 @@ site_access_code_kv: { } ``` +### `x-no-account-id` — Narrow Transport Exception + +`x-no-account-id` is a transport-level escape hatch that strips account context before the request leaves the frontend. It is not a permission grant and it is not a replacement for JWT or `x-account-id`. + +Use it only when the request truly cannot be made account-scoped. Current legitimate cases should stay narrow: + +1. Bootstrap / site-domain discovery before the account is known. +2. Explicit public or guest endpoints that do not have an account context. +3. Helper paths that intentionally need a global-default fallback. + +If a request already has a valid account context, prefer `x-account-id` and let the JWT carry session identity. Treat any new `x-no-account-id` use as temporary until it is reviewed and either replaced or justified. + --- ## Utility Functions @@ -154,6 +166,11 @@ Returns `1` if `level_a` is higher, `-1` if lower, `0` if equal. Useful for thre - Security model: API key is one layer; JWT + `x-account-id` scoping provides the primary auth. - Do not introduce new usages. Prefer `PUBLIC_AE_BOOTSTRAP_KEY` for unauthenticated lookups. +### JWT usage guidance +- JWTs are the preferred proof of an established session. Keep them attached to authenticated flows instead of leaning on transport-level bypasses. +- If a route or helper can work with a JWT and an account ID, it should not need `x-no-account-id`. +- If a helper still needs the bypass today, document the reason and add a removal target. + ### Email Display Non-trusted users must never see a full email address. Obscure using: ```typescript diff --git a/documentation/BOOTSTRAP__AI_Agent_Quickstart.md b/documentation/BOOTSTRAP__AI_Agent_Quickstart.md index 3fcce07e..61a2a21b 100644 --- a/documentation/BOOTSTRAP__AI_Agent_Quickstart.md +++ b/documentation/BOOTSTRAP__AI_Agent_Quickstart.md @@ -29,7 +29,7 @@ running in Docker. The frontend talks to it exclusively via the V3 REST API. | Editors | CodeMirror 6 (primary), Edra/TipTap (secondary) | | Native | Electron app for onsite launcher (`src/lib/electron/electron_relay.ts`) | | Backend | FastAPI + MariaDB, V3 API (`/v3/crud/`, `/v3/lookup/`) | -| Auth | Custom headers: `x-aether-api-key` + `x-account-id` (no Bearer tokens) | +| Auth | Custom headers: `x-aether-api-key` + `x-account-id`; JWT Bearer is auto-injected when a session exists | --- @@ -294,7 +294,9 @@ These are real incidents — know them before you start. 10. **Using query `key` as a proxy for bypass stripped `x-account-id`** — this caused valid account-scoped requests to lose account context and 403. `key` can be a valid - endpoint/business param, but it is not equivalent to `x-no-account-id: bypass`. + endpoint/business param, but it is not equivalent to `x-no-account-id: bypass`. Keep + `x-no-account-id` usage narrow and temporary; do not expand it without a documented + allowlist case. 11. **Pre-stringifying `*_json` fields before passing to API wrappers** — the API wrappers (`api_post__crud_obj.ts` for V3, `api.ts` for legacy CRUD) automatically serialize any diff --git a/documentation/GUIDE__AE_API_V3_for_Frontend.md b/documentation/GUIDE__AE_API_V3_for_Frontend.md index 773dcae7..b1d157d7 100644 --- a/documentation/GUIDE__AE_API_V3_for_Frontend.md +++ b/documentation/GUIDE__AE_API_V3_for_Frontend.md @@ -19,12 +19,16 @@ Required for any non-public data (Journals, Badges, Users, etc.). * **Header:** `x-account-id: ` 2. **Administrative Bypass**: For authorized scripts needing global access. * **Header:** `x-no-account-id: bypass` + * **Scope:** Narrow escape hatch only. Keep it limited to allowlisted bootstrap/public/global-default paths and prefer `x-account-id` or JWT-backed requests everywhere else. 3. **Token Access**: Provide a **JWT** in the query string. * **Query Param:** `?jwt=` 4. **Important Distinction:** A query parameter named `key` is **not** an account-context bypass signal. * `key` may be used by specific endpoints/business logic, but it must **not** cause the frontend to remove `x-account-id`. * Only explicit `x-no-account-id: bypass` should strip account context. +> [!NOTE] +> The `x-no-account-id` path should continue to shrink over time. If you need a new use, document why `x-account-id` or JWT cannot cover it and mark the use as temporary unless it is a hard bootstrap/global-default requirement. + > [!CAUTION] > **UNSUPPORTED HEADERS:** The header `x-aether-api-token` is **NOT recognized** by the V3 API. If you send it, the backend will treat you as a guest and block access to private data. diff --git a/documentation/PROJECT__AE_Site_Passcode_Security.md b/documentation/PROJECT__AE_Site_Passcode_Security.md index 4f4bae7d..3fda5e51 100644 --- a/documentation/PROJECT__AE_Site_Passcode_Security.md +++ b/documentation/PROJECT__AE_Site_Passcode_Security.md @@ -83,6 +83,15 @@ This gives session expiry without a network call on every page load. **Note:** The backend fixes described below have been implemented and tested in the `aether_api_fastapi` repository (the `/authenticate_passcode` endpoint now uses explicit role priority, returns a full passcode JWT with `auth_type: 'passcode'`, applies per-role TTLs, and validates passcode length). Frontend changes can proceed once the backend deployment with these fixes is available. +### Backend Agent Follow-Up + +If the backend team revisits this area, keep the next round focused on narrowing escape hatches rather than adding new ones: + +1. Audit every `x-no-account-id` use and decide whether it is still required for bootstrap, public delivery, or a global-default fallback. +2. Prefer JWT-backed auth once a session exists; do not add new transport-level bypass paths for authenticated UI flows. +3. Mark any remaining bypass-only helper as temporary and add a removal target. +4. Plan the eventual removal of `access_code_kv_json` from public bootstrap payloads once passcode auth is fully deployed. + **Phase 2 status:** Not started — removing `access_code_kv_json` from the public site model remains pending. **File:** `aether_api_fastapi/app/routers/api.py`