Skip to content

feat(auth): add periodic upstream OIDC revalidation with refresh-token support#164

Open
paulojmdias wants to merge 3 commits into
sigbit:mainfrom
paulojmdias:feat/auth_periodic_oidc_revalidation
Open

feat(auth): add periodic upstream OIDC revalidation with refresh-token support#164
paulojmdias wants to merge 3 commits into
sigbit:mainfrom
paulojmdias:feat/auth_periodic_oidc_revalidation

Conversation

@paulojmdias

@paulojmdias paulojmdias commented May 13, 2026

Copy link
Copy Markdown

Summary

Adds periodic upstream IdP re-validation so the proxy honors IdP-side deprovisioning (suspension, group removal, password reset) without waiting for the downstream JWT to expire.

When enabled, every authenticated request whose cached upstream session is older than --auth-revalidate-interval triggers a call to the OIDC provider's /userinfo endpoint (with transparent refresh-token rotation when offline_access is granted). On a fatal IdP response, every downstream access token issued for the subject is revoked, and the user is forced to re-authenticate.

Type of Change

  • feat: A new feature

Behavior

Mirrors oauth2-proxy's fatal/non-fatal model by default:

  • Fatal (invalid_grant, invalid_client, HTTP 401/403 from /userinfo) → revoke all downstream tokens for the subject, return 401.
  • Non-fatal (network failure, 5xx, decode errors, OAuth2 invalid_request, timeouts) → allow the request through, retry on the next interval. Configurable via --auth-revalidate-on-failure=deny to revoke instead — useful when the upstream IdP returns non-fatal-coded errors for revoked sessions (e.g. dex emits invalid_request for revoked refresh tokens) or when operators prefer security over availability during IdP outages.
  • Cache hit (within interval) → skipped, no IdP traffic.
  • Bounded timeout — each upstream call is capped by --auth-revalidate-timeout (default 10s) so an unresponsive IdP cannot hang proxy request latency. Exceeding it is treated as a non-fatal failure.
  • No upstream session on file (e.g. token issued before this feature shipped) → reject with 401 to force re-login.
    Concurrent requests for the same subject are deduplicated via singleflight so a single IdP round-trip serves all in-flight requests. The revalidation context is decoupled from any single caller via context.WithoutCancel so a client disconnect does not cancel the in-flight call shared by other waiters.

Configuration

New flags (and matching env vars):

Flag Env Default Description
--auth-revalidate-interval AUTH_REVALIDATE_INTERVAL 60s Re-validation interval. Set to 0 to disable.
--auth-revalidate-timeout AUTH_REVALIDATE_TIMEOUT 10s Max time to wait for a single upstream re-validation call (token refresh + /userinfo). Bounds the impact of an unresponsive IdP on request latency.
--auth-revalidate-on-failure AUTH_REVALIDATE_ON_FAILURE allow Behavior on non-fatal revalidation errors. allow (default, oauth2-proxy parity) logs a warning and proceeds; favors availability during IdP outages. deny revokes the subject's tokens and rejects the request; favors security and is recommended when the IdP returns non-fatal-coded errors for revoked sessions.

For graceful refresh-token rotation, request the offline_access scope:

--oidc-scopes "openid,profile,email,offline_access"

Without offline_access, most IdPs (including Okta) will not issue a refresh token, and the user will be forced to re-authenticate when the upstream access token expires.

Assisted-By: Claude Opus 4.7

…n support

Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
…on mode

Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
@codecov

codecov Bot commented May 27, 2026

Copy link
Copy Markdown

@hrntknr

hrntknr commented May 27, 2026

Copy link
Copy Markdown
Member

doc-check has already been removed, so the failure can be ignored.

@hrntknr hrntknr self-requested a review May 27, 2026 13:08
@paulojmdias

Copy link
Copy Markdown
Author

Thanks @hrntknr! Please let me know if I should increase the code coverage 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants