Skip to content

MCP requirement - Audience Mapper and Checker referring RFC 8707 resource parameter#45739

Closed
tnorimat wants to merge 1 commit intokeycloak:mainfrom
Hitachi:ISSUE-41527
Closed

MCP requirement - Audience Mapper and Checker referring RFC 8707 resource parameter#45739
tnorimat wants to merge 1 commit intokeycloak:mainfrom
Hitachi:ISSUE-41527

Conversation

@tnorimat
Copy link
Copy Markdown
Contributor

@tnorimat tnorimat commented Jan 25, 2026

closes #41527

The PR's code size is large, but half of them are for testing.

Class structure

  • ResourceIndicatorMapper : adds the value of resource parameter in an authorization request to an access token's "aud" claim.
  • SecureResourceIndicatorExecutor / SecureResourceIndicatorExecutorFactory: checks if resource parameter value in an authorization request and token request are valid. If not, returns an error.

The way of adding resource parameter value to an access token's aud claim

  1. AuthorizationEndpoint.updateAuthenticationSession stores the resource parameter value in an authorization request to an AuthenticationSession.
  2. TokenManager.attachAuthenticationSession copies the resource parameter value of the AuthenticationSession to AuthenticatedClientSession.
  3. ResourceIndicatorMapper and SecureResourceIndicatorExecutor can see it from AuthenticatedClientSession.getNote.

@thomasdarimont
Copy link
Copy Markdown
Contributor

thomasdarimont commented Jan 28, 2026

Thanks for picking this up again.

I see that you are using a protocol mapper to restrict the allowed resource indicator uris.

I used the same approach in my initial PR, but this was declined back then as restricting values for a protocol flow via mapper was uncommon back then. That's why I went with the more sophisticated approach that introduced a SPI to restrict allowed resource indicators backed by authorization services to allow users to define resources.

Perhaps opinions changed in-between :)

I think using a protocol mapper to declare a static list of allowed resource indicator uris per client has its limits but might still be expressive enough to solve common scenarios.

@tnorimat
Copy link
Copy Markdown
Contributor Author

@thomasdarimont Hello, thank you for the comments. I missed the point.
Regarding restricting the allowed resources, I will try to client policies while Introducing a new SPI is better option.

@tnorimat tnorimat force-pushed the ISSUE-41527 branch 2 times, most recently from 28ef4e1 to 0d7456b Compare January 30, 2026 01:31
@tnorimat
Copy link
Copy Markdown
Contributor Author

@stianst Hello, the feature is also important the same as CIMD to comply with MCP spec even if there is the workaround using client scope. Coud anyone in your team check the PR?

Copy link
Copy Markdown
Contributor

@cgeorgilakis cgeorgilakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work for a needed feature.
See my comments for proposed changes.


public static final String PROVIDER_ID = "oidc-resource-indicator-mapper";

private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens :

aud REQUIRED - as defined in Section 4.1.3 of [RFC7519]. See
Section 3 for indications on how an authorization server should
determine the value of "aud" depending on the request.
If the request does not include a "resource" parameter, the
authorization server MUST use a default resource indicator in the
"aud" claim. If a "scope" parameter is present in the request, the
authorization server SHOULD use it to infer the value of the default
resource indicator to be used in the "aud" claim.

I propose to add an optional default value for aud claim (defaultAud) . Default value will be null.
If resource is empty and defaultAud is not empty, mapper will add this default value to aud claim.
In this way Keycloak will respect the JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens for aud claim.


String acr;

String resource;

This comment was marked as resolved.

String resourceParam = authzRequestContext.getAuthorizationEndpointRequest().getResource();
logger.debugv(" on authz request: resourceParam = {0}", resourceParam);
List<String> allowResourceList = convertContentFilledList(configuration.getAllowPermittedResources());
if (allowResourceList != null && !allowResourceList.isEmpty()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combine two if in one if for better performance and clearance.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified the codes as follows:

                if (allowResourceList != null && !allowResourceList.isEmpty() && !allowResourceList.contains(resourceParam)) {
                        logger.warnv("not allowed resource parameter value: resource = {0}", resourceParam);
                        throw new ClientPolicyException(OAuthErrorException.INVALID_REQUEST, ERR_NOT_PERMITTED_RESOURCE);
                }

@tnorimat
Copy link
Copy Markdown
Contributor Author

tnorimat commented Feb 4, 2026

@cgeorgilakis Hello, thank you for the comment. I will check them.

@tnorimat tnorimat force-pushed the ISSUE-41527 branch 3 times, most recently from ece170c to 8652ad0 Compare February 7, 2026 12:01
Copy link
Copy Markdown
Contributor

@stianst stianst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you provide an overview on how this should be used? Is it basically just mapping a known resource into the token? Not sure if that makes all that much sense to me.

I haven't had time to look at this in more detail sorry, but hope to take a look next week.

@stianst stianst requested review from mposolda and rmartinc February 10, 2026 12:57
@tnorimat
Copy link
Copy Markdown
Contributor Author

tnorimat commented Feb 13, 2026

@stianst Hello,

The PR's resource parameter support should be used as follows:
An access token's audience is a single resource on a resource server or resource server itself and its ID is identified in URI format like "iss" claim for an authorization server. One of the actual examples is MCP server.

The same thing can be done by client scope + audience mapper, but it does not work if several resource servers support the same name of scope.

It consists of two parts: mapper and executor.

The mapper has a single role: putting resource parameter value of an authorization request to "aud" claim of an access token.

The executor has two roles:

  1. [Audience Binding] checking if resource parameter value of an authorization request is one of pre-registered resource values. If not, retuning an error.
  2. [Consistency] checking if resource parameter value of an token request in authorization code grant is equal to resource parameter value of an authorization request If not, retuning an error.

From security perspective, both should be applied. If not, any resource parameter value is can be set to "aud" claim of the access token.

Comparison of RFC 8707 specification and the spec of the PR is as follows:

  • Target types of request:
    • RFC 8707:
      • Authorization Request
      • Token Request with all types of grants
    • The PR:
      • Authorization Request
      • Token Request with authorization code grant
  • The number of resource parameter:
    • RFC 8707:
      • Allowing multiple resource parameter
    • The PR:
      • Only single resource parameter
  • Downscoping
    • RFC 8707:
      • Possible
    • The PR:
      • Impossible

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements support for RFC 8707 Resource Indicators to meet MCP (Model Context Protocol) specification requirements. The implementation adds the ability to include a resource parameter in OAuth2 authorization and token requests, which gets mapped to the aud (audience) claim in access tokens.

Changes:

  • Introduces a new experimental feature flag RESOURCE_INDICATOR for RFC 8707 support
  • Adds ResourceIndicatorMapper protocol mapper to bind resource parameter values to access token audience claims
  • Adds SecureResourceIndicatorExecutor client policy executor to validate resource parameter values across authorization and token requests

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
common/src/main/java/org/keycloak/common/Profile.java Adds RESOURCE_INDICATOR experimental feature flag
services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java Defines RESOURCE_PARAM constant for RFC 8707
services/src/main/java/org/keycloak/protocol/oidc/mappers/ResourceIndicatorMapper.java New protocol mapper that adds resource parameter to access token audience
services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureResourceIndicatorExecutor.java New executor that validates resource parameters in authorization and token requests
services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureResourceIndicatorExecutorFactory.java Factory for SecureResourceIndicatorExecutor
services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java Adds resource field to authorization request model
services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java Parses resource parameter from authorization requests
services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java Propagates resource parameter through authorization flow
services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java Manages resource parameter in client session notes
services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper Registers ResourceIndicatorMapper
services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory Registers SecureResourceIndicatorExecutorFactory
tests/base/src/test/java/org/keycloak/tests/oauth/ResourceIndicatorMapperTest.java Comprehensive test coverage for resource indicator functionality
tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/LoginUrlBuilder.java Adds resource parameter support to login URL builder
tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/AccessTokenRequest.java Adds resource parameter support to access token requests
tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/RefreshRequest.java Adds resource parameter support to refresh token requests
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientPoliciesUtil.java Adds utility method for creating resource executor config
tests/utils-shared/src/main/java/org/keycloak/testsuite/util/ClientPoliciesUtil.java New utilities file with complete client policy helper methods

@stianst
Copy link
Copy Markdown
Contributor

stianst commented Feb 18, 2026

@stianst Hello,

The PR's resource parameter support should be used as follows: An access token's audience is a single resource on a resource server or resource server itself and its ID is identified in URI format like "iss" claim for an authorization server. One of the actual examples is MCP server.

I'm not really following TBH; so to use this I have to:

  1. Create a client scope and add the ResourceIndicatorMapper that will map any resource parameter as a aud claim
  2. Create a client policy most likely per-client to set what clients can use what resource parameters?

Also, if I don't do 2, then a client can get any resource into the aud claim of the token?

@stianst stianst self-assigned this Feb 18, 2026
@tnorimat
Copy link
Copy Markdown
Contributor Author

@stianst Yes, so make it secure, applying the executor is needed.
To avoid this misconfiguration, I can put two roles onto one component: mapper or executor.
Which client applies the mapper is determined by scope, while the executor is determined by conditions/policy. So I think the latter is flexible. What do you think about that?

@stianst
Copy link
Copy Markdown
Contributor

stianst commented Mar 18, 2026

Closing this since we now have experimental support for resource indicators through audience filtering: #46763

@stianst stianst closed this Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP requirement - Audience Mapper and Checker referring RFC 8707 resource parameter

5 participants