Files
alkhttp/docs/architecture/decisions/073-door-surface-basic-auth-tolerance.md
glm-5.3-flash 0766f4bebe feat(server): door-surface Basic auth tolerance (review 008)
review 008 resolution:

- AH-1: door_auth_middleware on extras doors resolves
  Authorization: Basic base64(user:pass) — password field as the
  token (GitHub-PAT shape, git-lfs / git smart-HTTP clients) — through
  the same IdentityProvider::resolve_from_token backend as Bearer; no
  new trait method, resolve-fails -> None fallback preserved. Bearer
  surfaces stay Bearer-only: the gateway endpoints, /openapi.json,
  /healthz, the MCP nest, and the WS upgrade path never resolve Basic.
  The 'Bearer-only is the auth mechanism' doc line flips (ADR-073).
- AH-2 ride-along: extras door route tables are static — the merged
  router owns the decoy fallback, a second fallback panics at
  construction; documented on with_extra_routes and ADR-046 §1.

docs: ADR-073, ADR-046 §1/§4 amendment, ADR-004 presentation table,
review 008 marked resolved; new server::* re-exports; dep:base64
behind the server feature.

Verification: cargo test (449 pass, incl. door Basic resolve-through
and gateway-stays-Bearer-only pins), test --all-features (528),
clippy --all-targets -D warnings, fmt --check, fuzz corpus replay
(21/21), cargo doc.
2026-10-17 16:09:36 +00:00

87 lines
4.0 KiB
Markdown

# ADR-073: Door-Surface Basic Auth Tolerance (git / git-lfs Clients)
## Status
Accepted
## Context
Review 008 AH-1, from alkgit's LFS POC (POC-L): a real `git-lfs`
3.7.0 client authenticates the batch endpoint with **Basic**
credentials (URL userinfo — the spec-shape behavior of the official
client; config `http://user:pass@host/repo`). The shared identity
middleware was Bearer-only — the module doc said so explicitly
("Bearer-only is the auth mechanism") — so a door mounted under the
shared auth layer received the batch request with the middleware
already having folded in `None`, and the door had to re-parse the
`Authorization` header itself (Basic *or* Bearer) to get any identity.
The middleware is shared by the gateway endpoints and `to_mcp`. Every
HTTP-door surface whose real client authenticates with Basic (LFS
today; git's smart-HTTP transport is tomorrow's `git` door) would
silently re-implement the fallback locally — the quiet-bypass shape
alkcall review 012 records for the ownership seam, one layer up in the
same stack.
## Decision
**Split the auth surface into token surfaces and the door surface,
and teach the door surface Basic — through the same resolution
backend, with no new `IdentityProvider` method.**
- `bearer_auth_middleware` stays Bearer-only and continues to cover
the token-auth surfaces: the gateway endpoints, `/openapi.json`,
`/healthz`, and the `to_mcp` nest. A Basic header contributes no
identity there. `/call`, `/batch`, `/subscribe`, `/publish`, `/mcp`,
and the WS upgrade path are token surfaces — this is the "no blanket
method" constraint: a Basic credential never gains identity anywhere
the deployment has not explicitly set up for it.
- `door_auth_middleware` (new) is applied by `build_router` to the
extras-door router (`HttpAdapter::with_extra_routes`) — the surface
legacy clients authenticate on.
- Both middlewares resolve through `IdentityProvider::resolve_from_token()`.
Basic parsing: `Basic base64(user:pass)` — the password field (the
GitHub-PAT shape; the user segment is ignored) is decoded and fed as
the `AuthToken` raw bytes. A colon-less value is treated as a bare
password. No new trait method: the provider's grant table governs a
Basic credential exactly as it governs the same credential presented
as Bearer — and the same backend mechanism that grants privileges to
tokens grants them to these credentials (SOCKS5
userinfo-style integrations can follow the same pattern later).
- Fallback semantics unchanged: no header → `None`; unparseable or
unresolvable credential → `None` (unauthenticated, not an error).
A parse-time failure never becomes a 4xx of its own.
- Doors may opt out with their own middleware stack as before
([ADR-046](046-assembly-layer-custom-http-routes.md) §4).
## Consequences
**Positive:**
- `git-lfs` / `git` doors work against a deployment whose provider
maps password-tokens through `resolve_from_token`, with zero
door-side header re-parsing.
- Scoping is structural (per-middleware), not
per-path-inspection: Basic tolerance exists only where the
assembly layer mounted the door surface.
**Negative:**
- A door surface deployment that does *not* want its door credentials
resolvable as Basic must now ensure the provider does not resolve
those values as tokens — or opt the door out of the shared layer.
- Scheme prefixes remain case-sensitive ("Bearer "/"Basic "), matching
the pre-existing Bearer parsing; strictly RFC-noncompliant
lowercase-scheme clients parse to `None`.
## References
- [Review 008](../../reviews/008-http-door-consumer-review.md) — AH-1
evidence and disposition space
- [ADR-004](004-auth-as-shared-core.md) — the shared-core resolution
backend this rides
- [ADR-046](046-assembly-layer-custom-http-routes.md) — the extras-door
surface the door middleware covers (§4 amended; AH-2 static-route
constraint recorded there)
- alkcall review 012 — the same-round quiet-bypass analysis this
avoids repeating at the door layer