docs(security): narrow x-no-account-id guidance and JWT notes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,12 +19,16 @@ Required for any non-public data (Journals, Badges, Users, etc.).
|
||||
* **Header:** `x-account-id: <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=<token>`
|
||||
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.
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user