feat: Socialite OIDC JWKS refresh#388
Merged
Merged
Conversation
Adds a refresh flag to OpenIdProvider::getOpenIdConfig() and getJwks() so the discovery doc and key set can be re-fetched when verification fails for a key reason. getUserByOIDCToken() retries decode once after a forced refresh on UnexpectedValueException matching the "kid" invalid path, and on SignatureInvalidException for the rare case where a provider replaces key material under an existing kid. A 10-second forced-refresh cooldown bounds the refresh rate so a flood of tokens with unknown kids cannot turn the verification path into a JWKS endpoint hammer. The timestamp is set before the HTTP call, so a failed fetch also counts toward cooldown.
Concrete OpenIdProvider used by the JWKS rotation tests. Exposes the real JWT verification path via verifyToken() and allows tests to override jwksRefreshCooldownSeconds and the Guzzle client directly, avoiding the per-request httpClient context plumbing for the JWKS rotation cases under test.
Five tests covering the refresh-on-miss + cooldown behavior using real RSA keypairs and end-to-end Firebase JWT::encode / JWT::decode rather than mock-shaped fakes: - unknown new kid refreshes JWKS and succeeds - cached valid kid does not refetch - token without kid fails without triggering refresh - cooldown bounds repeated unknown-kid refreshes - same kid with stale key material refreshes once and succeeds
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR makes Socialite's OpenID Connect token verification resilient to provider JWKS rotation in long-running workers.
Previously,
OpenIdProviderfetched the provider discovery document and JWKS once, cached the parsed keys on the provider instance, and reused them for the worker lifetime. That is fast, but under Swoole it means a provider key rotation can leave a worker validating ID tokens against stale keys until restart.The provider now refreshes discovery/JWKS and retries verification when Firebase JWT reports a stale-key-shaped failure:
kidthat is not in the cached JWKSkidbut the cached key material no longer verifies the signatureMalformed tokens, missing
kids, invalid claims, nonce failures, issuer failures, and audience failures still fail normally without forcing JWKS refresh.Details
OpenIdProvider::getOpenIdConfig()andgetJwks().JWT::decode()once after a forced JWKS refresh for unknownkidand stale key material cases.kidtokens cannot turn verification into unlimited JWKS HTTP requests.This intentionally avoids adding a PSR-6 cache adapter or switching to Firebase's
CachedKeySet. The issue is narrow, key rotation is rare, and a local retry/cooldown keeps the fix simple without adding framework-wide cache compatibility surface.Tests
Added real Firebase JWT/OpenSSL coverage for:
kidis missing from cached keyskidis already presentkidkidfailureskid