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.
87 lines
4.0 KiB
Markdown
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 |