package bouncer import "net/http" // Guard resolves the caller's Principal for r, or an error describing why not. type Guard interface { Authenticate(r *http.Request) (*Principal, error) } // CredentialGuard resolves the Principal AND its underlying credential (e.g. // *models.ApiToken) in one pass -- a DB-backed guard must never verify twice // per request (RESEARCH.md Pitfall 5: last_used_at must stamp once). type CredentialGuard interface { AuthenticateCredential(r *http.Request) (*Principal, any, error) } // UnauthorizedWriter lets a guard write its own failure response. jwtGuard // implements this (reusing write401's {"error":true,"message":...} shape). // TokenGuard does NOT implement it: PHP's TokenScope, not ApiTokenGuard, owns // the {"error":"Invalid token"} 401 body (D-08) -- Registry.Middleware must // pass an unauthenticated request through untouched when a guard has no // UnauthorizedWriter, leaving denial to downstream middleware. type UnauthorizedWriter interface { WriteUnauthorized(w http.ResponseWriter, err error) }