feat(auth): forward-auth verification endpoint#34
Conversation
Serve forward_auth.path (default /auth/verify) so a fronting reverse proxy (nginx auth_request, Traefik forwardAuth) can delegate auth to the proxy. The endpoint validates the request's Bearer token and evaluates the configured route policies against the original request, taken from the fronting proxy's X-Forwarded-Method / X-Forwarded-Uri (falling back to X-Original-Method / X-Original-URI, then the verify request's own): - 200 echoing the configured claim headers (verified identity) for the fronting proxy to copy upstream; - 401 when unauthenticated (Location: login_url when configured); - 403 when authenticated but missing a required role. The token/policy/claims decision is factored into a single Auth::decide shared by the JWT middleware and this endpoint, so both stay in lockstep. The endpoint is mounted after the auth layer, so it is not itself gated. Closes #33
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThe PR refactors the JWT middleware's inline authorization logic into a shared ChangesForward-auth verification endpoint
Sequence Diagram(s)sequenceDiagram
participant FrontingProxy
participant ForwardAuth
participant Auth
participant RoutePolicy
rect rgba(70, 130, 180, 0.5)
Note over FrontingProxy,ForwardAuth: Public /auth/verify — not gated by JWT middleware
FrontingProxy->>ForwardAuth: ANY /auth/verify<br/>(X-Forwarded-Method, X-Forwarded-Uri, Authorization)
ForwardAuth->>ForwardAuth: extract original method + path from forwarding headers
end
rect rgba(100, 160, 100, 0.5)
Note over ForwardAuth,RoutePolicy: Shared Auth::decide path (also used by middleware)
ForwardAuth->>Auth: decide(headers, original_path, original_method)
Auth->>Auth: parse + verify bearer token
Auth->>RoutePolicy: match route, check require_auth / required_roles
Auth-->>ForwardAuth: AuthDecision
end
alt Allow
ForwardAuth-->>FrontingProxy: 200 OK + claim headers (e.g. x-forwarded-user)
else Unauthenticated
ForwardAuth-->>FrontingProxy: 401 Unauthorized (+ Location: login_url if set)
else Forbidden
ForwardAuth-->>FrontingProxy: 403 Forbidden
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
| Filename | Overview |
|---|---|
| src/auth/forward.rs | New file implementing the ForwardAuth verification endpoint with correct token validation, forwarded-header parsing, 200/401/403 response semantics, and five well-targeted tests covering valid, invalid, no-token, wrong-role, and unprotected-path cases |
| src/auth/mod.rs | Refactors JWT middleware to delegate to a new shared Auth::decide() method; logic is preserved exactly and the new AuthDecision enum cleanly expresses all three outcomes (Allow, Unauthenticated, Forbidden) |
| src/lib.rs | Adds ForwardAuth route mounting; the endpoint is correctly merged after the JWT middleware layer (so it is not gated by it) and before Shield/Maintenance layers (so rate limiting and maintenance mode still apply) |
| README.md | Moves forward-auth from the roadmap to the implemented features list and narrows the roadmap to External AuthZ / BFF sessions only |
Reviews (2): Last reviewed commit: "refactor(auth): simplify forward-auth qu..." | Re-trigger Greptile
…oken
- Use split_once('?') for the original-path query strip; the previous
split().next().unwrap_or(..) had a dead fallback branch (split always
yields at least one element).
- Add a verify-endpoint test that a token signed by the wrong key is
rejected with 401, guarding the verification path against regressions
(e.g. a dropped algorithm-confusion check).
Part of #33
Summary
Implements the forward-auth verification endpoint so the proxy can act as an external auth gate for a fronting reverse proxy (nginx
auth_request, TraefikforwardAuth).forward_authconfig was parsed (itspoliciesalready drive the JWT route rules) but the endpoint it advertises wasn't served.What's added (when
auth.mode == jwtandforward_auth.enabled)forward_auth.path(default/auth/verify). It validates the request'sBearertoken and evaluates the routepoliciesagainst the original request, read from the fronting proxy'sX-Forwarded-Method/X-Forwarded-Uri(falling back toX-Original-Method/X-Original-URI, then the verify request's own method/path).x-forwarded-user) so the fronting proxy copies the verified identity upstream.Location: forward_auth.login_urlwhen configured); 403 when authenticated but missing a required role.Design
The token → policy → claims decision is factored into a single
Auth::decideshared by the JWT middleware and the verify endpoint, so they can never drift. The route reuses the already-builtAuth(same keys + policies). Original method/path parsing strips any query string.Testing
4 endpoint tests (200 + echoed claim header, 401 + login Location, 403 missing role, unprotected original path) plus the existing 16 auth tests unchanged after the refactor.
cargo nextest run --features redis: 109 passed. clippy (all-features) + fmt clean.Out of scope (separate roadmap items)
External gRPC AuthZ (
authz), BFF sessions (bff), and multi-application configs (applications_path).Docs
README: forward-auth endpoint moved to Features; the roadmap line now tracks only External AuthZ / BFF.
Closes #33