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

4.0 KiB

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 §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 — AH-1 evidence and disposition space
  • ADR-004 — the shared-core resolution backend this rides
  • ADR-046 — 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