ruby-sdk/lib/mcp/client/oauth
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Koichi ITO f51ccfbe66 Let providers without a CIMD URL reader refresh their tokens
## Motivation and Context

`Flow#refresh!` reads `client_id_metadata_document_url` from the provider unconditionally, once to decide whether
a provider without stored `client_information` may still refresh and once to build the CIMD `client_id`.
Only `Provider` defines that reader: `CrossAppAccessProvider` and `ClientCredentialsProvider`
raise `NoMethodError` once a stored `refresh_token` is found. `MCP::Client::HTTP` calls `refresh!` on any `401` while
a `refresh_token` is stored, so a `jwt-bearer` client whose authorization server issued one fails with
`NoMethodError` on the first expired token instead of refreshing. The error is not an `AuthorizationError`,
so the transport does not fall back to the grant either. RFC 7521 Section 4.1 makes a refresh token unusual
for an assertion grant, not forbidden, and one in storage must not crash the client.

The flow now reads the URL through a duck-typed helper, like `authorization_flow` and `client_assertion`,
so a provider without the reader has no CIMD URL and refreshes with its stored `client_information`:
the `refresh_token` grant is sent with the stored client authentication, and no new ID-JAG assertion is obtained.
The authorization-code registration path reads the URL through the same helper.
The docs now say that a refresh token issued anyway is used on the next `401`, and that the assertion provider
is not called again on refresh.
A refresh token stored without the `issuer` the SDK records is refused for these two providers before any request:
unlike `Provider`, they have no tokens that predate the record, and a refresh asks no validator, so such a token
would be presented to whatever authorization server discovery names. The refusal is an `AuthorizationError`,
which sends the transport back through the grant, and the grant asks.

## How Has This Been Tested?

New tests in `test/mcp/client/oauth/flow_test.rb` refresh through a `CrossAppAccessProvider` and
a `ClientCredentialsProvider` holding a `refresh_token`, asserting the `refresh_token` grant, the Basic credentials,
and that the assertion provider is not called. A transport test in `test/mcp/client/oauth/http_oauth_test.rb` sends
a `401` to a `CrossAppAccessProvider` with a stored `refresh_token` and expects the refreshed bearer token on the retry.
All three raise `NoMethodError` against the previous library.
Further tests pin the issuer stamp on the refreshed tokens, the refusal of an authorization server that did not issue them
for a `CrossAppAccessProvider`, a clean `AuthorizationError` for a provider with neither the reader
nor stored `client_information`, and registration through DCR for a provider without the reader.
A refresh token without a recorded issuer is refused for both providers before any request is sent.

## Breaking Changes

None.
2026-09-18 11:17:21 +09:00
..
authorization_request.rb Let the embedding application refuse an authorization request 2026-08-31 02:33:10 +09:00
bounded_body.rb Bound OAuth response bodies in the client 2026-08-16 03:29:15 +09:00
client_credentials_provider.rb Let a provider add parameters to the token requests it makes 2026-09-17 13:26:52 +09:00
cross_app_access_provider.rb Let a provider add parameters to the token requests it makes 2026-09-17 13:26:52 +09:00
discovery.rb Restrict OAuth Discovery URL Destinations 2026-08-13 14:54:27 +09:00
flow.rb Let providers without a CIMD URL reader refresh their tokens 2026-09-18 11:17:21 +09:00
id_jag_token_exchange.rb Bound OAuth response bodies in the client 2026-08-16 03:29:15 +09:00
in_memory_storage.rb Bind Stored Client Credentials to the Authorization Server Issuer per SEP-2352 2026-07-05 01:12:08 +09:00
jwt_client_assertion.rb Support private_key_jwt Client Authentication for the client_credentials Grant 2026-06-28 20:59:37 +09:00
pkce.rb Add OAuth 2.1 client support for MCP authorization flow 2026-05-17 00:48:41 +09:00
provider.rb Let a provider add parameters to the token requests it makes 2026-09-17 13:26:52 +09:00
storage_backed_provider.rb Let a provider add parameters to the token requests it makes 2026-09-17 13:26:52 +09:00