The team exposes Grafana in three "temporary" clicks for an audit. No native SSO, default admin password changed "later". Two weeks later port 443 still faces the Internet with homemade basic auth easy to brute-force.
An OAuth proxy puts authentication where legacy apps cannot — in front, not inside.
Typical architecture
Client → TLS terminator → oauth2-proxy → upstream app. Flow: IdP redirect → callback → encrypted cookie → app access.
App stays internal HTTP; only edge is exposed.
Secure configuration
Cookie: Secure, HttpOnly, SameSite=Lax minimum. Rotatable --cookie-secret. Restrict --email-domain or OIDC groups.
Never expose upstream directly without parallel auth — instant bypass.
Headers and trust
Pass X-Forwarded-User/Email/Groups. App must not trust these from the world; firewall: proxy only.
For app RBAC, map OIDC claims → headers or separate proxy paths.
Special cases
Inbound webhooks: auth bypass route with separate HMAC secret.
Machine API: not oauth2-proxy — mTLS or dedicated API token.
Health checks: /health without auth for LB, no sensitive data.
Operations
Centralize auth failure logs. Session expiry matches company policy. Document offboarding: revoke IdP sessions + rotate cookie secret if sensitive departure.
Deployment checklist
- Upstream app not directly reachable from Internet
- Cookie Secure HttpOnly SameSite configured
- Restrictive
--email-domainor OIDC groups - Listed bypass routes (health, webhook HMAC)
- X-Forwarded-* trusted app-side from proxy only
- WebSocket tested end-to-end
- Session expiry aligned with HR policy
- Centralized auth failure logs
- Documented offboarding procedure
- Cert renewal tested without mass logout
Validate with internal "bypass proxy" pentest — if success, fix network before announcing SSO.
Team integration and lifecycle
Onboarding: new engineer gets IdP access + proxy path doc — not direct upstream URL. Offboarding: revoke IdP and invalidate session cookies (rotate cookie-secret if sensitive departure).
Multi-environment: staging oauth2-proxy to staging IdP app registration — do not share prod/staging client_id.
Observability: oauth-proxy 401/403 vs upstream 502 metrics — distinguish auth fail vs app down.
Limits: oauth2-proxy does not replace fine authorization (app RBAC) — it authenticates, app still authorizes.
Multiple internal tools (Grafana + Prometheus + Kibana): one oauth2-proxy per upstream or central Traefik forward auth — both work, do not mix cookie secrets.
Session length vs idle timeout: align with data sensitivity (prod logs vs HR wiki).
Break-glass local account disabled by default, enabled via break-glass procedure only — no forgotten backdoor.
High availability
Two oauth2-proxy instances behind load balancer with shared cookie secret — session sticky or encrypted cookie works stateless.
IdP outage impact : document fallback read-only mode or maintenance page — SSO dependency explicit.
Health check oauth2-proxy /ping excluding upstream — LB removes bad proxy nodes only.
Operational summary
OAuth proxy adds SSO in front of legacy apps — secure cookies, no direct upstream access, bypass routes documented. WebSocket and cert renewal tested. IdP outage runbook explicit.
Production readiness
Load test login flow peak Monday morning. Verify cookie expiry matches HR policy. Pen test bypass attempt documented closed. Backup auth break-glass tested quarterly.
SSO rollout narrative
Rolling out oauth2-proxy across three internal tools took one sprint when the network path was already correct — and three when upstream apps remained reachable by IP. The difference was not OAuth complexity but firewall rules and team habit. Treat SSO as a network project with an OAuth front, not purely an identity integration.
Document the happy path login in under thirty seconds for a new engineer. If ProxyJump plus oauth2-proxy plus MFA exceeds two minutes, adoption leaks around the control via shared credentials — worse than before.
Review cookie settings after every security audit. SameSite changes in browsers have broken cross-subdomain setups that worked for years.
Network SSO
Upstream reachable by IP = SSO bypass. Document login <30s.
Decide and move forward without blind spots
- Block direct Internet access to upstream app — only the proxy should be publicly reachable.
- Configure Secure HttpOnly SameSite cookies — list bypass routes (health, HMAC-signed webhooks).
- Restrict OIDC domains or groups — session expiry aligned with HR policy, centralized auth logs.
- Test WebSocket end-to-end — trust X-Forwarded-* from proxy only.
- Pentest proxy bypass — documented offboarding, cert renewal without mass logout.
Compare hosting suited to SSO stacks via our directory and guides.
Frequently asked questions
oauth2-proxy vs classic reverse proxy?
oauth2-proxy handles OAuth/OIDC flow and sets session cookie; plain nginx does not validate IdP identity without extra modules.
Does the app see the real user?
Via X-Forwarded-User/Email/Groups if configured — app must trust them only from the proxy (network ACL).
WebSockets?
Enable upstream WebSocket on nginx/traefik; oauth2-proxy must allow upgrade routes with same session cookie.
Misconfiguration risk?
Bypass via wide IP whitelist, cookie without Secure/HttpOnly, or app accepting spoofed headers from Internet.
Before announcing "SSO enabled", try reaching upstream without the proxy — if it works, you are not done.
