Summary
The forward-auth endpoint /oauth2/token/verify (used by nginx auth_request, Traefik forwardAuth, Caddy forward_auth) currently validates the presented token as a Bearer token only — signature + real-time revocation check — and returns identity claims as response headers. It does not validate a DPoP proof. As a result, DPoP-bound (sender-constrained) tokens terminated at a reverse proxy are not actually proof-of-possession-checked at the edge.
Current behavior
internal/handler/auth_verify.go — reads the Authorization: Bearer header, introspects (signature + revocation), returns 200 + identity headers or 401. No DPoP header handling, no cnf.jkt binding check.
Proposed change
Extend /oauth2/token/verify to optionally accept and validate a DPoP proof (RFC 9449):
- Read the
DPoP request header alongside Authorization.
- When the access token carries a
cnf.jkt confirmation claim (i.e. it's DPoP-bound), require a valid DPoP proof and verify the proof's public key thumbprint matches cnf.jkt.
- Validate the proof
htm/htu against the forwarded method/URI (the proxy must pass the original method + URL; document the required forwarded headers per proxy).
- Reuse the existing
pkg/dpop primitives.
- On failure, return 401 with an appropriate
WWW-Authenticate: DPoP challenge.
This lets reverse proxies enforce sender-constrained tokens without each upstream re-implementing DPoP.
References
- RFC 9449 — OAuth 2.0 Demonstrating Proof of Possession (DPoP)
- Existing primitives:
pkg/dpop
Reported by Andrii Deinega via WIMSE WG review of ZeroID.
Summary
The forward-auth endpoint
/oauth2/token/verify(used by nginxauth_request, TraefikforwardAuth, Caddyforward_auth) currently validates the presented token as a Bearer token only — signature + real-time revocation check — and returns identity claims as response headers. It does not validate a DPoP proof. As a result, DPoP-bound (sender-constrained) tokens terminated at a reverse proxy are not actually proof-of-possession-checked at the edge.Current behavior
internal/handler/auth_verify.go— reads theAuthorization: Bearerheader, introspects (signature + revocation), returns 200 + identity headers or 401. NoDPoPheader handling, nocnf.jktbinding check.Proposed change
Extend
/oauth2/token/verifyto optionally accept and validate a DPoP proof (RFC 9449):DPoPrequest header alongsideAuthorization.cnf.jktconfirmation claim (i.e. it's DPoP-bound), require a valid DPoP proof and verify the proof's public key thumbprint matchescnf.jkt.htm/htuagainst the forwarded method/URI (the proxy must pass the original method + URL; document the required forwarded headers per proxy).pkg/dpopprimitives.WWW-Authenticate: DPoPchallenge.This lets reverse proxies enforce sender-constrained tokens without each upstream re-implementing DPoP.
References
pkg/dpopReported by Andrii Deinega via WIMSE WG review of ZeroID.