Security
- Verify caller-supplied ID tokens against Google's published signing keys before trusting them. An ID token sent alongside a direct
access_tokencallback was previously decoded without checking its signature, soextra.id_infoandextra.id_tokencould be populated from a forged token.uidandinfowere never affected, as they come from the userinfo endpoint. A caller-supplied ID token genuinely issued by Google for the same user and a trusted client continues to be accepted after verification. - Require a caller-supplied ID token to describe the same user as the access token it was sent with, by comparing the token's
subagainst the userinfo subject. A verified signature only proves Google issued the token, not that it belongs to the person the access token identifies, so without this a genuine ID token for one user could be paired with an access token for another and leaveuidandextra.id_infodescribing different people. Theat_hashclaim is checked first as a fast path, and this subject check settles the casesat_hashcannot: tokens that omit the claim, and tokens whoseat_hashis stale because the client refreshed its access token after sign-in. This check runs even whenskip_infois set, as that option trims the auth hash rather than waiving verification. - The bundled example app no longer disables TLS certificate verification. Anyone who copied that line into an application should remove it: it turns off certificate checking for every Faraday-based request in the process, not just the ones this gem makes.
Added
reset_jwks_cache!for clearing the cached Google signing keys between tests.cached_jwks, the class-level fetch-and-cache primitive behind it, which takes the fetch itself as a block.JWKS_URL,JWKS_CACHE_TTL, andJWKS_RETRY_INTERVALconstants, and aJwksUnavailableerror.- An upper bound of
< 4on thejwtdependency. This is precautionary rather than a response to a released version: it keeps a future major release from being picked up before it has been verified against this strategy.
Deprecated
- Nothing.
Removed
- The fallback that placed the opaque access token in
extra.id_tokenwhen no ID token was present.extra.id_tokenis now absent in that case rather than holding a value that was never an ID token. This was only reachable withskip_jwtset; without it the fallback raised instead.
Fixed
- Ignore every credential field a caller supplies in a direct access-token callback apart from the access token itself and a verified ID token.
refresh_tokenand token expiry in particular cannot be verified, so they are no longer carried through. - Avoid decoding opaque access tokens as JWTs when no ID token is available.
- Fail with a normal authentication failure when a callback carries no usable credential, such as an ID token with no access token, a JSON body that is not an object, or an unparseable body. Previously these raised a
NoMethodErrororTypeErrorthat OmniAuth turned into a failure whose message was the raw Ruby error, so applications received an unstablemessageparameter such asundefined method 'expired?'instead ofinvalid_credentials. - Serve the cached signing keys when Google's key endpoint is briefly unreachable, and back off before refetching, rather than retrying on every request, including when nothing is cached yet.
- Reject a signing key response that is not an object with a
keysarray, rather than passing it on to be interpreted as some other kind of key. - Cap how often an ID token naming an unrecognised key can force a key refresh, so it cannot be used to drive unbounded outbound requests while holding the shared cache lock. Key rotation still resolves within
JWKS_RETRY_INTERVAL. jwt_leewaynow also applies when verifying caller-supplied ID tokens, matching how it already behaved for theextrablock.- Support non-rewindable JSON request bodies under Rack 3.
- Accept ID tokens issued to any configured
authorized_client_ids, matching the audiences already accepted for access tokens. - Reuse successfully verified ID token claims when building
extra, so each ID token is decoded and validated only once per request.