perf(auth): cache identity provider discovery #23

Merged
ryan.mcgee merged 1 commit from perf/oidc-discovery-cache into main 2026-10-15 19:26:06 +00:00
Owner

Summary

  • Starting an OIDC sign-in made two requests to the provider (discovery document and keys) on every anonymous visit, so anyone could make this server hammer a provider, and a slow provider slowed the login page for everyone.

Changes

  • TraefikControl.Identity.Discovery: per-issuer cache (ETS reads, one coordinating process).
    • Kept for as long as the provider says, clamped to 1 minute to 1 hour.
    • One fetch per issuer at a time; callers arriving meanwhile wait for it. Fetches run in their own processes, so a slow provider does not hold up another.
    • A failed fetch is not retried for 30 seconds. A previous good copy is served through the outage and refreshed after the wait.
    • A token signed with a key the cache lacks (key rotation) fetches the keys again, at most once a minute per issuer (refresh_jwks callback).
    • Editing or removing a provider clears its entry.
  • Identity uses it for authorization URLs and code exchange. README updated.
  • FakeIdp (test support) counts requests, can go down, be slow, and rotate keys.

Verification

  • mix format, compile --warnings-as-errors, credo --strict, dialyzer: clean. mix test: 1506 passed on SQLite and PostgreSQL; the OIDC test files were run five times in a row on SQLite, all passing (some tests are timing based).
  • 50 simultaneous sign-in starts against a provider with 300 ms latency: all succeeded in 700 ms with one discovery and one keys request; the next start took under 1 ms.

Risks

  • A provider that rotates keys is picked up on the first token with an unknown key, at most once a minute; within that minute a second unknown key fails sign-in once.
  • The cache is per node of the control plane (not shared across several instances).
## Summary - Starting an OIDC sign-in made two requests to the provider (discovery document and keys) on every anonymous visit, so anyone could make this server hammer a provider, and a slow provider slowed the login page for everyone. ## Changes - `TraefikControl.Identity.Discovery`: per-issuer cache (ETS reads, one coordinating process). - Kept for as long as the provider says, clamped to 1 minute to 1 hour. - One fetch per issuer at a time; callers arriving meanwhile wait for it. Fetches run in their own processes, so a slow provider does not hold up another. - A failed fetch is not retried for 30 seconds. A previous good copy is served through the outage and refreshed after the wait. - A token signed with a key the cache lacks (key rotation) fetches the keys again, at most once a minute per issuer (`refresh_jwks` callback). - Editing or removing a provider clears its entry. - `Identity` uses it for authorization URLs and code exchange. README updated. - `FakeIdp` (test support) counts requests, can go down, be slow, and rotate keys. ## Verification - `mix format`, `compile --warnings-as-errors`, `credo --strict`, `dialyzer`: clean. `mix test`: 1506 passed on SQLite and PostgreSQL; the OIDC test files were run five times in a row on SQLite, all passing (some tests are timing based). - 50 simultaneous sign-in starts against a provider with 300 ms latency: all succeeded in 700 ms with one discovery and one keys request; the next start took under 1 ms. ## Risks - A provider that rotates keys is picked up on the first token with an unknown key, at most once a minute; within that minute a second unknown key fails sign-in once. - The cache is per node of the control plane (not shared across several instances).
perf(auth): cache identity provider discovery
All checks were successful
CI / format, compile, credo, dialyzer (pull_request) Successful in 1m32s
CI / tests (SQLite) (pull_request) Successful in 2m15s
CI / tests (PostgreSQL) (pull_request) Successful in 2m10s
05dcab1ec9
- cache each provider's discovery document and keys per issuer,
  for the time the provider allows (1 minute to 1 hour)
- one fetch per issuer at a time; a failed fetch is not retried for
  30 seconds and a previous good copy keeps sign-in working
- a token signed with an unknown key refetches the keys, at most
  once a minute per issuer
- editing or removing a provider clears its cache
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
White_Ghost_Cyber_Systems/traefik-control!23
No description provided.