mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-17 04:37:48 +00:00
docs(oidcgate): expand user guide and cross-link
- Add HAProxy and Envoy ext_authz_http wiring snippets. - Add full OIDCGATE_* env-var inventory (26 fields). - Add Security Posture section (X-Forwarded-Uri sanitisation, excludedURLs guardrail, callbackURL/logoutURL validation). - Add Bearer-token (M2M) auth composition section with link to BEARER_AUTH.md. - Add Operational Guidance section (healthz/readyz ACL, Redis for multi-replica, no built-in metrics, graceful shutdown deadline). - Add Debugging section (sentinel path, silent open-redirect rejections, /readyz warm-up). - Cross-link from docs/CONFIGURATION.md.
This commit is contained in:
@@ -14,6 +14,7 @@ Complete reference for all Traefik OIDC middleware configuration options.
|
|||||||
- [Security Headers](#security-headers)
|
- [Security Headers](#security-headers)
|
||||||
- [Scope Configuration](#scope-configuration)
|
- [Scope Configuration](#scope-configuration)
|
||||||
- [Advanced Options](#advanced-options)
|
- [Advanced Options](#advanced-options)
|
||||||
|
- [Standalone binary (oidcgate)](#standalone-binary-oidcgate)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -664,3 +665,19 @@ sessionEncryptionKey: ${OIDC_SECRET_API}
|
|||||||
# Good
|
# Good
|
||||||
sessionEncryptionKey: ${OIDC_SECRET_SVC}
|
sessionEncryptionKey: ${OIDC_SECRET_SVC}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Standalone binary (oidcgate)
|
||||||
|
|
||||||
|
If you don't run Traefik, the same configuration shape documented above
|
||||||
|
works for the [`oidcgate`](OIDCGATE.md) standalone forward-auth daemon
|
||||||
|
under `cmd/oidcgate`. Three extra top-level keys (`listen`, `authPath`,
|
||||||
|
`startPath`) configure the daemon itself; everything else maps 1:1 onto
|
||||||
|
the `traefikoidc.Config` fields documented in this reference.
|
||||||
|
|
||||||
|
See [`docs/OIDCGATE.md`](OIDCGATE.md) for the full daemon guide including
|
||||||
|
nginx, Caddy, Traefik ForwardAuth, HAProxy and Envoy wiring snippets,
|
||||||
|
the `OIDCGATE_*` environment-variable inventory, the security posture
|
||||||
|
(X-Forwarded-Uri sanitisation, excludedURLs guardrail), and how to layer
|
||||||
|
M2M [bearer-token auth](BEARER_AUTH.md) on the same daemon.
|
||||||
|
|||||||
+275
-21
@@ -2,7 +2,26 @@
|
|||||||
|
|
||||||
`oidcgate` is a single binary that exposes the same OIDC middleware that
|
`oidcgate` is a single binary that exposes the same OIDC middleware that
|
||||||
powers the Traefik plugin as a forward-auth daemon for nginx, Caddy,
|
powers the Traefik plugin as a forward-auth daemon for nginx, Caddy,
|
||||||
Traefik ForwardAuth, HAProxy, and Envoy ext_authz_http.
|
Traefik ForwardAuth, HAProxy, and Envoy `ext_authz_http`.
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
|
||||||
|
- [Build](#build)
|
||||||
|
- [Run](#run)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [YAML file](#yaml-file)
|
||||||
|
- [Environment-variable overrides](#environment-variable-overrides)
|
||||||
|
- [Endpoints](#endpoints)
|
||||||
|
- [Reverse-proxy snippets](#reverse-proxy-snippets)
|
||||||
|
- [nginx (`auth_request`)](#nginx-auth_request)
|
||||||
|
- [Caddy (`forward_auth`)](#caddy-forward_auth)
|
||||||
|
- [Traefik (`ForwardAuth`)](#traefik-forwardauth)
|
||||||
|
- [HAProxy](#haproxy)
|
||||||
|
- [Envoy (`ext_authz_http`)](#envoy-ext_authz_http)
|
||||||
|
- [Security posture](#security-posture)
|
||||||
|
- [Bearer-token (M2M) auth on the same daemon](#bearer-token-m2m-auth-on-the-same-daemon)
|
||||||
|
- [Operational guidance](#operational-guidance)
|
||||||
|
- [Debugging](#debugging)
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
@@ -16,39 +35,92 @@ go build -o oidcgate ./cmd/oidcgate
|
|||||||
./oidcgate --config /etc/oidcgate/config.yaml
|
./oidcgate --config /etc/oidcgate/config.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Config
|
The daemon parses `--config`, loads YAML, applies any `OIDCGATE_*` env-var
|
||||||
|
overrides, validates the result, and binds to `listen`. On SIGINT/SIGTERM it
|
||||||
|
calls `http.Server.Shutdown` with a 15s deadline, draining in-flight requests.
|
||||||
|
|
||||||
YAML, mirroring the existing Traefik plugin schema with three extra keys:
|
## Configuration
|
||||||
|
|
||||||
|
### YAML file
|
||||||
|
|
||||||
|
The OIDC subtree of the config maps 1:1 onto the [`traefikoidc.Config`](CONFIGURATION.md)
|
||||||
|
struct — every field documented under "Configuration Reference" works here
|
||||||
|
verbatim. Three extra top-level keys configure the daemon itself:
|
||||||
|
|
||||||
|
| Key | Default | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `listen` | _required_ | TCP address (e.g. `:8080`, `127.0.0.1:8080`). |
|
||||||
|
| `authPath` | `/oauth2/auth` | Silent-probe endpoint (used by nginx `auth_request`). |
|
||||||
|
| `startPath` | `/oauth2/start` | Visible sign-in endpoint. |
|
||||||
|
|
||||||
|
Minimal example (see [`examples/oidcgate.yaml`](../examples/oidcgate.yaml)):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
listen: ":8080"
|
listen: ":8080"
|
||||||
authPath: "/oauth2/auth" # default; nginx auth_request subrequest target
|
|
||||||
startPath: "/oauth2/start" # default; visible sign-in endpoint
|
|
||||||
providerURL: "https://accounts.google.com"
|
providerURL: "https://accounts.google.com"
|
||||||
clientID: "your-client-id"
|
clientID: "your-client-id"
|
||||||
clientSecret: "your-client-secret"
|
clientSecret: "your-client-secret"
|
||||||
sessionEncryptionKey: "64-hex-bytes"
|
sessionEncryptionKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||||
callbackURL: "/oauth2/callback"
|
callbackURL: "/oauth2/callback"
|
||||||
logoutURL: "/oauth2/logout"
|
logoutURL: "/oauth2/logout"
|
||||||
# ... any other traefikoidc Config field works here verbatim
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Secrets can be overridden via environment variables:
|
Nested structs (`redis:`, `securityHeaders:`, `dynamicClientRegistration:`)
|
||||||
`OIDCGATE_CLIENT_SECRET`, `OIDCGATE_SESSION_ENCRYPTION_KEY`,
|
round-trip cleanly through YAML — same shape as in `.traefik.yml`.
|
||||||
`OIDCGATE_CLIENT_ID`, etc.
|
|
||||||
|
### Environment-variable overrides
|
||||||
|
|
||||||
|
Any of the following scalar fields can be overridden at runtime by an
|
||||||
|
`OIDCGATE_<UPPER_SNAKE_CASE>` environment variable. The env var wins over
|
||||||
|
the YAML value when set and non-empty. Intended for secret injection
|
||||||
|
(K8s `valueFrom.secretKeyRef`, systemd `EnvironmentFile=`, etc.).
|
||||||
|
|
||||||
|
| YAML key | Env var |
|
||||||
|
|---|---|
|
||||||
|
| `listen` | `OIDCGATE_LISTEN` |
|
||||||
|
| `authPath` | `OIDCGATE_AUTH_PATH` |
|
||||||
|
| `startPath` | `OIDCGATE_START_PATH` |
|
||||||
|
| `providerURL` | `OIDCGATE_PROVIDER_URL` |
|
||||||
|
| `clientID` | `OIDCGATE_CLIENT_ID` |
|
||||||
|
| `clientSecret` | `OIDCGATE_CLIENT_SECRET` |
|
||||||
|
| `audience` | `OIDCGATE_AUDIENCE` |
|
||||||
|
| `callbackURL` | `OIDCGATE_CALLBACK_URL` |
|
||||||
|
| `logoutURL` | `OIDCGATE_LOGOUT_URL` |
|
||||||
|
| `postLogoutRedirectURI` | `OIDCGATE_POST_LOGOUT_REDIRECT_URI` |
|
||||||
|
| `sessionEncryptionKey` | `OIDCGATE_SESSION_ENCRYPTION_KEY` |
|
||||||
|
| `cookiePrefix` | `OIDCGATE_COOKIE_PREFIX` |
|
||||||
|
| `cookieDomain` | `OIDCGATE_COOKIE_DOMAIN` |
|
||||||
|
| `logLevel` | `OIDCGATE_LOG_LEVEL` |
|
||||||
|
| `revocationURL` | `OIDCGATE_REVOCATION_URL` |
|
||||||
|
| `oidcEndSessionURL` | `OIDCGATE_OIDC_END_SESSION_URL` |
|
||||||
|
| `userIdentifierClaim` | `OIDCGATE_USER_IDENTIFIER_CLAIM` |
|
||||||
|
| `groupClaimName` | `OIDCGATE_GROUP_CLAIM_NAME` |
|
||||||
|
| `roleClaimName` | `OIDCGATE_ROLE_CLAIM_NAME` |
|
||||||
|
| `clientAuthMethod` | `OIDCGATE_CLIENT_AUTH_METHOD` |
|
||||||
|
| `clientAssertionPrivateKey` | `OIDCGATE_CLIENT_ASSERTION_PRIVATE_KEY` |
|
||||||
|
| `clientAssertionKeyPath` | `OIDCGATE_CLIENT_ASSERTION_KEY_PATH` |
|
||||||
|
| `clientAssertionKeyID` | `OIDCGATE_CLIENT_ASSERTION_KEY_ID` |
|
||||||
|
| `clientAssertionAlg` | `OIDCGATE_CLIENT_ASSERTION_ALG` |
|
||||||
|
| `caCertPath` | `OIDCGATE_CA_CERT_PATH` |
|
||||||
|
| `caCertPEM` | `OIDCGATE_CA_CERT_PEM` |
|
||||||
|
|
||||||
|
Nested-struct fields (Redis, security headers, DCR) are YAML-only — set
|
||||||
|
them in the config file, not via env.
|
||||||
|
|
||||||
## Endpoints
|
## Endpoints
|
||||||
|
|
||||||
| Path | Method | Purpose |
|
| Path | Method | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `/oauth2/auth` | GET | Silent probe — `200` if authenticated, `401` if not |
|
| `/oauth2/auth` | GET | Silent probe — `200` if authenticated, `401` if not. Never returns `302`; the middleware's redirect-to-IdP is rewritten in-flight to `401` with the original `Location` carried as `X-Auth-Redirect`. |
|
||||||
| `/oauth2/start` | GET | Visible sign-in — `302` to IdP, accepts `?rd=` for return target |
|
| `/oauth2/start` | GET | Visible sign-in — `302` to the IdP authorize URL. Accepts `?rd=<safe-path>` (or honours `X-Forwarded-Uri`) for the post-login redirect target. |
|
||||||
| `/oauth2/callback` | GET | IdP callback |
|
| `/oauth2/callback` | GET | IdP `code`+`state` exchange. Path is configurable via `callbackURL`. |
|
||||||
| `/oauth2/logout` | GET/POST | Session terminate |
|
| `/oauth2/logout` | GET/POST | Terminates the session. Path is configurable via `logoutURL`. Honours `oidcEndSessionURL` for RP-initiated logout. |
|
||||||
| `/healthz` | GET | Liveness — `200` while process is alive |
|
| `/healthz` | GET | Liveness — `200` while the process is alive. |
|
||||||
| `/readyz` | GET | Readiness — `200` after first metadata discovery, else `503` |
|
| `/readyz` | GET | Readiness — `200` once the OIDC discovery document has been fetched, otherwise `503`. |
|
||||||
|
|
||||||
## nginx
|
## Reverse-proxy snippets
|
||||||
|
|
||||||
|
### nginx (`auth_request`)
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
location = /oauth2/auth {
|
location = /oauth2/auth {
|
||||||
@@ -65,7 +137,7 @@ location @oidc_signin {
|
|||||||
}
|
}
|
||||||
location /oauth2/ {
|
location /oauth2/ {
|
||||||
proxy_pass http://oidcgate:8080;
|
proxy_pass http://oidcgate:8080;
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
}
|
}
|
||||||
location / {
|
location / {
|
||||||
@@ -79,13 +151,17 @@ location / {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Caddy
|
### Caddy (`forward_auth`)
|
||||||
|
|
||||||
```
|
```caddyfile
|
||||||
example.com {
|
example.com {
|
||||||
forward_auth oidcgate:8080 {
|
forward_auth oidcgate:8080 {
|
||||||
uri /oauth2/auth
|
uri /oauth2/auth
|
||||||
copy_headers X-Forwarded-User X-Forwarded-Email
|
copy_headers X-Forwarded-User X-Forwarded-Email
|
||||||
|
@denied status 401
|
||||||
|
handle_response @denied {
|
||||||
|
redir /oauth2/start?rd={http.request.uri} 302
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handle /oauth2/* {
|
handle /oauth2/* {
|
||||||
reverse_proxy oidcgate:8080
|
reverse_proxy oidcgate:8080
|
||||||
@@ -94,7 +170,7 @@ example.com {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Traefik ForwardAuth
|
### Traefik (`ForwardAuth`)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
http:
|
http:
|
||||||
@@ -106,3 +182,181 @@ http:
|
|||||||
- X-Forwarded-User
|
- X-Forwarded-User
|
||||||
- X-Forwarded-Email
|
- X-Forwarded-Email
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Traefik can follow the `X-Auth-Redirect` value via a chained `redirectScheme`
|
||||||
|
middleware, or you can configure the upstream router to redirect `401` →
|
||||||
|
`/oauth2/start` directly.
|
||||||
|
|
||||||
|
### HAProxy
|
||||||
|
|
||||||
|
```haproxy
|
||||||
|
frontend fe_https
|
||||||
|
bind *:443 ssl crt /etc/haproxy/certs/site.pem
|
||||||
|
http-request set-var(req.orig_uri) path
|
||||||
|
http-request send-spoe-group oidc auth-check # or use lua/SPOE; simplest is the lua snippet below
|
||||||
|
|
||||||
|
# The simpler pattern: dispatch /oauth2/* to oidcgate, everything else
|
||||||
|
# goes through a Lua filter that issues a sub-request to /oauth2/auth.
|
||||||
|
acl is_oidc_endpoint path_beg /oauth2/
|
||||||
|
use_backend be_oidcgate if is_oidc_endpoint
|
||||||
|
default_backend be_app
|
||||||
|
|
||||||
|
backend be_oidcgate
|
||||||
|
server oidcgate1 oidcgate:8080
|
||||||
|
|
||||||
|
backend be_app
|
||||||
|
server app1 backend:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
HAProxy does not have a first-class `auth_request` equivalent in pure
|
||||||
|
config — the canonical patterns are SPOE (Stream Processing Offload Engine),
|
||||||
|
a Lua filter that issues `/oauth2/auth` and reads the response, or a
|
||||||
|
sidecar that does the dance. Reach for SPOE for high-throughput
|
||||||
|
production; Lua is simpler for low-volume.
|
||||||
|
|
||||||
|
### Envoy (`ext_authz_http`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
http_filters:
|
||||||
|
- name: envoy.filters.http.ext_authz
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
|
||||||
|
transport_api_version: V3
|
||||||
|
http_service:
|
||||||
|
server_uri:
|
||||||
|
uri: http://oidcgate:8080
|
||||||
|
cluster: oidcgate
|
||||||
|
timeout: 2s
|
||||||
|
path_prefix: /oauth2/auth
|
||||||
|
authorization_request:
|
||||||
|
allowed_headers:
|
||||||
|
patterns:
|
||||||
|
- exact: cookie
|
||||||
|
- exact: authorization
|
||||||
|
- prefix: x-forwarded-
|
||||||
|
authorization_response:
|
||||||
|
allowed_upstream_headers:
|
||||||
|
patterns:
|
||||||
|
- exact: x-forwarded-user
|
||||||
|
- exact: x-forwarded-email
|
||||||
|
allowed_client_headers:
|
||||||
|
patterns:
|
||||||
|
- exact: x-auth-redirect
|
||||||
|
- exact: set-cookie
|
||||||
|
```
|
||||||
|
|
||||||
|
On `401`, the `X-Auth-Redirect` header is surfaced to the downstream client
|
||||||
|
via `allowed_client_headers`. A small Envoy `router` filter or
|
||||||
|
`local_reply_config` rule can convert that into a browser-facing `302`
|
||||||
|
redirect to `/oauth2/start`.
|
||||||
|
|
||||||
|
## Security posture
|
||||||
|
|
||||||
|
- **`X-Forwarded-Uri` is sanitised.** The daemon forces
|
||||||
|
`TrustForwardedURI=true` so the middleware honours `X-Forwarded-Uri` for
|
||||||
|
the post-login redirect target. To prevent open redirects (CWE-601),
|
||||||
|
the value is rejected unless it is a safe same-origin path: must start
|
||||||
|
with `/`, must NOT start with `//` (protocol-relative), and must have
|
||||||
|
no scheme or host after parsing. Absolute URLs or anything that could
|
||||||
|
redirect off-origin falls through to `req.URL.RequestURI()`.
|
||||||
|
|
||||||
|
- **`excludedURLs` cannot bypass the daemon's own paths.** At config
|
||||||
|
load, the loader rejects any `excludedURLs` entry that is a prefix of
|
||||||
|
`authPath`, `startPath`, `callbackURL`, `logoutURL`, or the internal
|
||||||
|
sentinel path. A misconfiguration like `excludedURLs: ["/"]` (common
|
||||||
|
"allow all then add auth selectively" mistake) is rejected at startup
|
||||||
|
with a descriptive error.
|
||||||
|
|
||||||
|
- **`callbackURL` and `logoutURL` must be paths.** Absolute URLs are
|
||||||
|
rejected at config load — both because `http.ServeMux.Handle` panics
|
||||||
|
on non-`/` patterns and because the middleware's path-match would
|
||||||
|
silently fail.
|
||||||
|
|
||||||
|
- **`listen` is required.** Empty or missing `listen` is rejected at
|
||||||
|
startup rather than failing later at `net.Listen`.
|
||||||
|
|
||||||
|
- **Secrets via env vars.** `clientSecret` and `sessionEncryptionKey`
|
||||||
|
can be supplied via env vars instead of YAML so they don't end up on
|
||||||
|
disk if you use a secret manager.
|
||||||
|
|
||||||
|
## Bearer-token (M2M) auth on the same daemon
|
||||||
|
|
||||||
|
oidcgate uses the full `traefikoidc.Config` shape, so the bearer-token
|
||||||
|
M2M auth path documented in [`BEARER_AUTH.md`](BEARER_AUTH.md) works
|
||||||
|
out of the box. Add to your YAML:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
enableBearerAuth: true
|
||||||
|
audience: "https://api.example.com"
|
||||||
|
bearerIdentifierClaim: "sub"
|
||||||
|
# stripAuthorizationHeader: true # default
|
||||||
|
# bearerOverridesCookie: false # default — cookie wins on collision
|
||||||
|
```
|
||||||
|
|
||||||
|
With this set, the daemon accepts both:
|
||||||
|
- Browser users hitting `/oauth2/auth` → cookie session flow.
|
||||||
|
- API clients calling the protected backend with `Authorization: Bearer <jwt>`
|
||||||
|
→ bearer validation, principal headers, no session.
|
||||||
|
|
||||||
|
The bearer path doesn't go through `/oauth2/auth` separately — it's
|
||||||
|
applied by the middleware on every request the daemon sees, before the
|
||||||
|
cookie session check. See [BEARER_AUTH.md](BEARER_AUTH.md) for the full
|
||||||
|
threat model, identifier sanitisation rules, and failure-response
|
||||||
|
matrix.
|
||||||
|
|
||||||
|
## Operational guidance
|
||||||
|
|
||||||
|
- **Run behind a fronting proxy on a private network.** The daemon does
|
||||||
|
not terminate TLS. Put it on a localhost socket or a private subnet
|
||||||
|
reachable only from your nginx/Caddy/Traefik/HAProxy/Envoy.
|
||||||
|
- **`/healthz` and `/readyz` are unauthenticated** — correct for
|
||||||
|
Kubernetes liveness/readiness probes, but **do not expose them past a
|
||||||
|
load balancer**. Restrict via an ACL: nginx `allow 10.0.0.0/8; deny
|
||||||
|
all;`, Caddy `@health remote_ip 10.0.0.0/8`, k8s NetworkPolicy, or
|
||||||
|
your CNI of choice.
|
||||||
|
- **Multi-replica deployments** need a shared session store. Enable the
|
||||||
|
`redis:` block in the config (see [`docs/REDIS.md`](REDIS.md)) so
|
||||||
|
sessions survive a hop between replicas.
|
||||||
|
- **No built-in Prometheus metrics yet.** If you need request-level
|
||||||
|
visibility, take it from your fronting proxy's access logs — both
|
||||||
|
nginx and Envoy can tag `auth_request` / `ext_authz` outcomes.
|
||||||
|
- **Logs are minimal by default.** Set `logLevel: debug` while
|
||||||
|
bringing up a new deployment; raise to `info` (default) or higher
|
||||||
|
once stable. Debug logs include path-match decisions and metadata
|
||||||
|
refresh outcomes.
|
||||||
|
- **Graceful shutdown is 15s.** SIGINT or SIGTERM triggers
|
||||||
|
`http.Server.Shutdown(ctx)` with a 15-second deadline; in-flight
|
||||||
|
requests are allowed to complete. If your orchestrator's grace
|
||||||
|
period is shorter, requests can be cut mid-flight.
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
- **Requests appear as `/__oidcgate_protected__` in middleware debug
|
||||||
|
logs.** This is the internal sentinel path used when `/oauth2/auth`
|
||||||
|
and `/oauth2/start` delegate into the traefikoidc middleware. The
|
||||||
|
upstream client never sees it; it only shows up in the middleware's
|
||||||
|
own `Debugf` output when `logLevel: debug` is set.
|
||||||
|
|
||||||
|
- **`/oauth2/auth` returns `401` with `X-Auth-Redirect` header on
|
||||||
|
unauthenticated requests.** This is the deliberate translation of the
|
||||||
|
middleware's `302` to make nginx `auth_request` work. The browser is
|
||||||
|
redirected via the fronting proxy's `error_page 401 = @oidc_signin;`
|
||||||
|
pattern, not by following the daemon's response directly.
|
||||||
|
|
||||||
|
- **`/readyz` stays `503` after startup.** The middleware fetches the
|
||||||
|
OIDC discovery document lazily on first request, so `/readyz` returns
|
||||||
|
`503` until at least one request has triggered metadata discovery.
|
||||||
|
Hit `/oauth2/auth` once after startup to warm it up — many K8s
|
||||||
|
setups achieve the same effect because the liveness probe already
|
||||||
|
goes through the proxy chain.
|
||||||
|
|
||||||
|
- **Cookie/session diagnostics.** With `logLevel: debug` the middleware
|
||||||
|
logs which session manager was selected (in-memory vs Redis), whether
|
||||||
|
cookies decrypted successfully, and the JWT validation outcome.
|
||||||
|
|
||||||
|
- **Open-redirect rejections are silent.** When the daemon ignores an
|
||||||
|
unsafe `X-Forwarded-Uri` value, it falls back to `req.URL.RequestURI()`
|
||||||
|
without logging. This is intentional (no recon signal) — if a user
|
||||||
|
reports "I keep landing on the wrong page after login", inspect
|
||||||
|
whether the upstream proxy is forwarding a non-canonical
|
||||||
|
`X-Forwarded-Uri` value.
|
||||||
|
|||||||
Reference in New Issue
Block a user