Let the client_credentials and jwt-bearer grants run without Protected Resource Metadata
## Motivation and Context
When a server publishes no Protected Resource Metadata, discovery takes the legacy 2025-03-26 path and `prm` is `nil`.
The authorization-code branch of `run!` allowed for that by passing `prm || {}` to `resolve_scope`,
but `run_client_credentials!` and `run_jwt_bearer!` passed `prm` as is, and `resolve_scope` then
called `prm["scopes_supported"]` on `nil`. Without a non-empty challenge scope, a `ClientCredentialsProvider` or
`CrossAppAccessProvider` pointed at such a server therefore failed with `NoMethodError` after discovery and before
the token request or the ID-JAG callback, instead of running its grant against the legacy authorization base
the way `Provider` does.
`resolve_scope` now treats a missing PRM as advertising no scopes, so the challenge scope or the provider's own scope applies,
and the authorization-code branch passes `prm` unchanged like the other two.
## How Has This Been Tested?
New tests in `test/mcp/client/oauth/flow_test.rb` run the `client_credentials` grant against a PRM-less server with metadata
at the origin, checking the provider's scope, the `resource`, and the recorded issuer, and run the `jwt-bearer` grant against
a server without any metadata, checking the default token endpoint, the assertion audience, the `resource`, and the recorded
issuer. Both raise `NoMethodError` against the previous library.
## Breaking Changes
None.
This commit is contained in:
parent
41fbf05d26
commit
29e8d3f2b9
1 changed files with 72 additions and 2 deletions
|
|
@ -87,7 +87,7 @@ module MCP
|
|||
|
||||
ensure_pkce_supported!(as_metadata)
|
||||
|
||||
effective_scope = resolve_scope(scope: scope, prm: prm || {})
|
||||
effective_scope = resolve_scope(scope: scope, prm: prm)
|
||||
effective_scope = normalize_offline_access_scope(effective_scope, as_metadata: as_metadata)
|
||||
|
||||
# Asked before registering, not after: a refusal must not leave this client registered at an authorization server
|
||||
|
|
@ -898,7 +898,8 @@ module MCP
|
|||
def resolve_scope(scope:, prm:)
|
||||
return scope if scope && !scope.empty?
|
||||
|
||||
supported = prm["scopes_supported"]
|
||||
# `prm` is nil on the legacy path, where nothing advertises scopes.
|
||||
supported = prm && prm["scopes_supported"]
|
||||
return supported.join(" ") if supported.is_a?(Array) && !supported.empty?
|
||||
|
||||
return @provider.scope if @provider.scope && !@provider.scope.empty?
|
||||
|
|
|
|||
Loading…
Reference in a new issue