Skip to content

Conversation

@thomasdarimont
Copy link
Contributor

@thomasdarimont thomasdarimont commented Dec 7, 2024

Add support for RFC 8707 OAuth2 Resource Indicators (#14355)

Discussion: #35743

  • Adds ResourceIndicatorMapper OIDC protocol mapper to manage the aud claim based on the resource parameter
  • Make AuthorizationEndpoint and TokenEndpoint aware of resource parameter.
  • Enhanced Authz request parsing to support repeated parameter (resource)
  • Added OAuth2ResourceIndicatorResolver SPI to allow filtering of resource indicators and lookup associated clients for resource indicators

If the authorization request contains one or more resource parameters, those resource values are first checked against the list of allowed resource indicators for the given client. Allowed resource indicators are determined
via the OAuth2ResourceIndicatorResolver SPI.
If the resource indicators are legit for the client, the initially requested resource identifier set is stored in the client session.
Token Requests / Token Refresh Requests can later request access tokens minted to support resource indicators in the aud claim based on the resource indicators that were initially requested.

Note that this allows users to dynamically extend or narrow down the aud claim based on the given resource identifiers in token / token refresh requests.

Fixes #14355

Signed-off-by: Thomas Darimont [email protected]

@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch 2 times, most recently from d2ff0f1 to 7958620 Compare December 7, 2024 12:37
@thomasdarimont
Copy link
Contributor Author

thomasdarimont commented Dec 7, 2024

Simple usage example

EDIT:
Note, this PR now longer supports to configure the allowed resource indicators via the ResourceIndicatorMapper.
This is just here to document the idea.
This PR now uses a new ResourceIndicatorResolver SPI to resolve the allowed resource indicators.
There is currently no configuration / UI support to define allowed resource indicators.

Client config for myclient

ResourceIndicatorMapper for myclient contains the allow resource identifier patterns:

image

Initial Auth Request:

$ISSUER/protocol/openid-connect/auth
?client_id=myclient
&redirect_uri=...
&state=...
&response_mode=fragment
&response_type=code
&scope=openid
&nonce=...
&code_challenge=...
&code_challenge_method=S256
&resource=foo
&resource=bar

-> This restricts the current auth session to use only foo and bar as resource indicators, even if the client myclient would also allow baz.

Token Request

POST $ISSUER/protocol/openid-connect/token
grant_type=authorization_code
&client_id=myclient
&code=...
&resource=foo
...

Access Token response

Decoded Access Token with audience:

{
    ...
    aud: [foo]
}

Token Refresh Request

POST $ISSUER/protocol/openid-connect/token
Authorization: ...
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=...
&resource=bar
...

Access Token response

Decoded Access Token with audience:

{
    ...
    aud: [bar]
}

Token Refresh request with invalid resource

POST $ISSUER/protocol/openid-connect/token
Authorization: ...
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=...
&resource=gugu
...

Access Token response

Returns error

{"error":"invalid_grant","error_description":"Invalid resource"}

@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from 7958620 to 84af69f Compare December 7, 2024 18:54
@stianst
Copy link
Contributor

stianst commented Dec 9, 2024

Haven't had a chance to look at this properly or think too much about it; but feels a bit like shoehorning/abusing protocol mappers, and not a natural way to manage resources for a client.

I think how we want to support resource indicators in Keycloak is better done first as a design/discussion rather than a PR.

@stianst
Copy link
Contributor

stianst commented Dec 9, 2024

Few random thoughts around this:

  • We'll probably eventually need some way of querying clients given a resource indicator
  • Resource indicators according to the spec must be a URI
  • RFC8693 has audience and resource has optional parameters; that means it's perfectly valid to only specify the resource parts, which again means there needs to be a efficient way to lookup the client for a given resource
  • Clients can have a base-url, kinda makes sense to allow configuring resources relative to that, rather than having to specify the full URI, at least in most common cases
  • Not sure how this all fits together with authz services, but there's definitively some overlap between resources defined for authz and resource indicators

@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from 84af69f to f150b10 Compare December 9, 2024 17:42
@thomasdarimont
Copy link
Contributor Author

Hi @stianst, thanks for your helpful comments! I created the following discussion to revise the design of the resource identifiers support: #35743

In this discussion I address the concerns you mentioned.

@thomasdarimont
Copy link
Contributor Author

thomasdarimont commented Dec 9, 2024

Yes, I agree this is somewhat abusing the protocol mappers mechanism beyond token generation since it also alters the Authorization Server behaviour. However, I felt quite pragmatic to do so. However, I agree that this protocol mapper usage might be uncommon - but I have to start somewhere :O)

Also the implementation can easily changed to load the resource identifiers list from somewhere else :)

We'll probably eventually need some way of querying clients given a resource indicator

I understand the use-case. I added the point to the discussion.

Resource indicators according to the spec must be a URI

Yes, that's written in the spec and the resource examples I gave were just that, examples. Quite often Keycloak users also model resource servers / APIs as clients without flows in Keycloak to configure client specific roles etc, that can be used by other clients. In my mind resource identifiers == client_id for resource server clients, my be this is a bit off, but since client_ids are usually not URIs (at least it's not enforced) I used non-URI values as resources in the examples.
I'm fine with using URIs though which would probably support your requirement for performing lookups for clients by resource identifier.

RFC8693 has audience and resource has optional parameters; that means it's perfectly valid to only specify the resource parts, which again means there needs to be a efficient way to lookup the client for a given resource

Noted, and added to the discussion.

Clients can have a base-url, kinda makes sense to allow configuring resources relative to that, rather than having to specify the full URI, at least in most common cases

Sometimes the configured base URL might not necessarily match the resource identifiers. I'm thinking of deployments of a resource server in different environments (dev, test, qa, prod), where the base URL is different but the logical resource URIs might be the same for all environments.

Not sure how this all fits together with authz services, but there's definitively some overlap between resources defined for authz and resource indicators

I think the resources in authz services could indeed be used for this, but this would lead to a stronger coupling of the authorization services with the oauth2 functionality which might cause other problems.
Thinking about this a bit, we could use authorization resources to configure the allowed resource identifiers for a client.
This can already be implemented quite easily in this PR: I just have to adjust the new org.keycloak.protocol.oauth2.ResourceIndicators#getSupportedResourceIndicators(..) method to fetch the resource URIs from authorization services instead of the protocol mapper configuration.

We then would also need a way to let clients "opt-in" to the resource identifier support somehow, perhaps with an "advanced setting" in the client configuration: Resource Identifier Support Enabled: on/off default off.

@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from f150b10 to bbe2913 Compare December 19, 2024 23:23
@thomasdarimont
Copy link
Contributor Author

thomasdarimont commented Dec 19, 2024

I refactored this the resource indicator support a bit:

  • Introduced OAuth2ResourceIndicatorResolver SPI to allow users to specify their own ways for validating resource identifiers (see O4) Add new dedicated SPI to validate allowed resource indicators R1 in Support for RFC 8707 OAuth2 Resource Indicators #35743)
  • Removed the ability to configure supported resource indicators from the Resource Indicator Mapper

We could then add a default implementation that checks given resource identifiers based on something like a component config (config per client) etc.

@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from bbe2913 to f21d068 Compare December 19, 2024 23:43
@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch 5 times, most recently from 9753f35 to 53facf8 Compare January 8, 2025 13:22
@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from 53facf8 to 464c2cf Compare January 27, 2025 16:56
@thomasdarimont
Copy link
Contributor Author

thomasdarimont commented Jan 27, 2025

I updated the PR with the a way to configure client specific resource indicators with the help of the authorization services:

image
User can now specify custom resources with the special type URI: urn:keycloak:oauth2:resource-indicator to denote a resource as resource indicator.

To apply the resource indicator semantics to the aud claim of a token, users need to configure a dedicated "resource-indicator-mapper",
image

@cgeorgilakis
Copy link
Contributor

Nice work 👏.
What happen during token exchange OIDC to OIDC client in aud claim if mapper exists and resource parameter exists as request parameter?

@thomasdarimont
Copy link
Contributor Author

thomasdarimont commented Jan 28, 2025

Thanks @cgeorgilakis for your feedback. The handling of the resource parameter is out of scope for this PR, as the resource handling for token exchange is covered by the Token Exchange rfc8693 Section 2.1 and should be covered by separate PR, e.g. #31553 or #36848.

@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch 2 times, most recently from 9eea73b to c9eb21c Compare March 5, 2025 08:51
@mposolda mposolda self-assigned this Mar 19, 2025
@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from c9eb21c to fd19563 Compare March 19, 2025 12:59
@thomasdarimont
Copy link
Contributor Author

thomasdarimont commented Mar 19, 2025

The latest version of the PR uses authorization-services resources to describe the resource indicators that are available to a client.

Client photoz client
image

... with authorization enabled
image

Resources can be defined via Authorization / Resources in the client:
image

The custom type: urn:keycloak:oauth2:resource-indicator declares a resource indicator, the URI specifies the actual URI: https://api.acme.com/photos

Available resource indicators for a client can be seenin the resources tab:
image

A new Resource Indicators protocol mapper exposes the configured resources in the aud claim in addition to the existing linked clientIds.
image

Dedicated Protocol Mapper:
image

One can find an example realm and some http request examples with curl here: https://gist.github.com/thomasdarimont/0ccab1238d3824a84a37a16a086cf890

@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from fd19563 to 2366d80 Compare June 27, 2025 20:34
@keycloak-github-bot
Copy link

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.cluster.UserFederationInvalidationClusterTest#crudWithFailover

Keycloak CI - Clustering IT

java.lang.RuntimeException: java.lang.IllegalStateException: Keycloak unexpectedly died :(
	at org.keycloak.testsuite.arquillian.containers.KeycloakQuarkusServerDeployableContainer.start(KeycloakQuarkusServerDeployableContainer.java:71)
	at org.jboss.arquillian.container.impl.ContainerImpl.start(ContainerImpl.java:185)
	at org.jboss.arquillian.container.impl.client.container.ContainerLifecycleController$8.perform(ContainerLifecycleController.java:137)
	at org.jboss.arquillian.container.impl.client.container.ContainerLifecycleController$8.perform(ContainerLifecycleController.java:133)
...

Report flaky test

Copy link

@keycloak-github-bot keycloak-github-bot bot left a comment

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@thomasdarimont thomasdarimont marked this pull request as ready for review July 14, 2025 14:28
@thomasdarimont thomasdarimont requested a review from a team as a code owner July 14, 2025 14:28
@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from 2366d80 to 08a3f08 Compare July 14, 2025 14:31
- Adds ResourceIndicatorMapper OIDC protocol mapper
- Make AuthorizationEndpoint and Token Endpoint aware of resource parameter.
- Enhanced Authz request parsing to support repeated parameter (resource)

If the authorization request contains one or more resource parameters, those resource values are first checked against
the list of allowed resource indicator patterns for the given client.
If the resource indicators are legit for the client, the initially requested resource identifier set is stored in the client session.
Token Requests / Token Refresh Requests can later request access tokens minted to support resource indicators in the aud claim based on the resource indicators that were initially requested.

Note that this allows users to dynamically extend or narrow down
the audience based on the given resource identifiers in token / token refresh requests.

The allowed resource identifiers for a client are managed via the ResourceIndicatorMapper.
ResourceIndicatorMapper manages aud claim via resource parameters. Users can configure the list of allowed resource indicator patterns in the ResourceIndicatorMapper configuration for a client. Note that only resource indicators allowed by the mapper configuration can be requested for a client.

Fixes keycloak#14355

Signed-off-by: Thomas Darimont <[email protected]>

Refactor ResourceIndicator handling

- Introduced OAuth2ResourceIndicatorsProvider SPI to abstract how valid Resource Indicators for clients are determined
- Revised the resource parameter checking
- Removed configuration of resource indicators from ResourceIndicatorMapper

Signed-off-by: Thomas Darimont <[email protected]>

Add an example default implementation for OAuth2ResourceIndicatorsProvider

Signed-off-by: Thomas Darimont <[email protected]>

Revise code in flattenDecodedFormParametersToParamsMap

Signed-off-by: Thomas Darimont <[email protected]>

Revise OAuth2ResourceIndicatorsProvider

- Rename SPI to resource-indicator-resolver
- Rename to OAuth2ResourceIndicatorsProvider
- Add ability to lookup clients by resource indicators

Signed-off-by: Thomas Darimont <[email protected]>

Next iteration for RFC 8707 OAuth2 Resource Indicators support

- We now allow specifying supported resource indicators via the authorizations tab in the client configuration.
- Users can declare new resources with the special type URI: `urn:keycloak:resource-identifier` to denote an resource identifier.
- According to the RFC 8707, resource indicators must be URIs, but we also support Keycloak clientIds as allowed resource indicators to support existing Keycloak realm configurations.

Signed-off-by: Thomas Darimont <[email protected]>

Change KEYCLOAK_RESOURCE_INDICATOR_TYPE to urn:keycloak:oauth2:resource-identifier

Signed-off-by: Thomas Darimont <[email protected]>

Revise DefaultOAuth2ResourceIndicatorResolver

Signed-off-by: Thomas Darimont <[email protected]>

Validate resource parameter during token exchange.

Signed-off-by: Thomas Darimont <[email protected]>

Change KEYCLOAK_RESOURCE_INDICATOR_TYPE to urn:keycloak:oauth2:resource-indicator

Signed-off-by: Thomas Darimont <[email protected]>

# Conflicts:
#	services/src/main/java/org/keycloak/protocol/oidc/grants/OAuth2GrantTypeBase.java
#	services/src/main/java/org/keycloak/protocol/oidc/tokenexchange/AbstractTokenExchangeProvider.java
@thomasdarimont thomasdarimont force-pushed the poc/oauth2-resource-indicators branch from 08a3f08 to 87884d8 Compare August 7, 2025 12:15
@stianst stianst marked this pull request as draft October 3, 2025 07:21
Copy link
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.

We should limit the initial support of resource indicators to a client having a single resource uri.

That brings the question of what resource can a client requests; for that we would need a way to link the client to the audience clients. Which is something we are aiming to deliver in #43179

We could leverage client roles though for this purpose perhaps? We could use something like the following:

  1. Add an optional Resource URI attribute to clients
  2. Get a list of all clients the client has a client role scope on
  3. For each resource parameter check it matches the Resource URI of one of the clients from 2

I changed this to a draft since there are no tests at all; I'd suggest we break this up into multiple stages starting with only supporting resource parameter in authorization request and token requests grant types authorization_code and refresh_token.

@stianst
Copy link
Contributor

stianst commented Oct 3, 2025

@thomasdarimont do you want to proceed with this PR or should I or someone else take over?

@thomasdarimont
Copy link
Contributor Author

Thanks @stianst for picking this up again. I don't have time to work on this today / next few days. So if you want to work on this now feel free to adopt it.

Moving forward, instead of continuing this PR, we should close it and create a new one.
I think we can salvage some of the ideas from this PR and add this feature in a stepwise manner.

Some questions/thoughts regarding your proposal:

We could use something like the following:

  1. Add an optional Resource URI attribute to clients
  2. Get a list of all clients the client has a client role scope on
  3. For each resource parameter check it matches the Resource URI of one of the clients from 2

Q1) How to configure the (primary) resource indicator for a client?
For 1. Would you like to use a new client attribute or a dedicated property for this?
I initially thought of a client attribute, but the (primary) resource indicator must be unique among all clients in a realm. I'm not sure if we already do this somewhere, but we may need to add a new field to the client JPA entity to store the resource indicator URI, making it easier to ensure uniqueness at the database level.
We can then expose this as a dedicated client property (more work, which might require admin API consumer updates) or pass it through the attributes (a hacky solution, but it would not break admin API consumers).

With this, we would only allow one (primary?) resource indicator per client for now, which can be used as an alias to target this specific client/resource server via the resource parameter.
However, later we may need to support additional (sub-resource) indicators per resource server (e.g., if those clients represent an API gateway or another collection of resource servers). Those additional resource indicators might come from another source, but we can add support for that later. In my PR, I introduced an OAuth2ResourceIndicatorResolver SPI for this.

Q2) Mechanism to select the eligible resource indicators for a given client
Do you mean to use the client role scope for selecting other related clients and derive the resource indicator from that?
Doesn't this require clients to specify at least one client role for selection?

To verify this:
Imagine we have a photos-app OIDC client that can obtain tokens and two APIs that the photos-app should be able to access: photos-api, accounts-api.
We create photos-api and accounts-api clients as a resource server (all flows disabled). For both, we specify resource indicators, such as https://accounts.acme.test and https://photos.acme.test.
We now want to express that a photos-app token should be able to access photoz-api and accounts-api via the resource parameter in the authorization / token request. With your method, I need to define at least one role in the photoz-api and accounts-api clients to be able to add them to the client role scope selection of the photoz-app. So I create a "dummy" role like access for photos-api, accounts-api, and add them to the client role scope of the photos-app.

Is this what you had in mind?

Q3) How to match a client via resource indicator to the requested resource parameter?

Using the client role scope to select clients and derive the eligible resource indicators is a simple solution. Still, it requires additional configuration (role definitions) and is also limited to just one indicator (which is fine for now). However, I wonder if we should make this pluggable from the start and use a new SPI to derive eligible resource indicators for a client with it (a default implementation could use the client role scope for this purpose).
I believe the clients selected need to have a resource indicator URI configured to be considered for matching the resource parameter. We could filter out clients without a resource indicator.

Q4) How do we specify whether a client supports the resource parameter or not?
Is this enabled for all clients automatically? In my PR, I added a dedicated ResourceIndicatorMapper OIDC protocol mapper that admins could add to their clients to explicitly enable the handling of resource parameters, which added eligible resource indicator values to the aud claim after some filtering.

Q5) When and where do we determine the eligible resource indicators?
In my PR, the set of eligible resource indicators was determined during authorization request handling and subsequently filtered based on the actual token request, allowing users to request a subset of the initially requested resources.

@stianst
Copy link
Contributor

stianst commented Oct 3, 2025

Thanks @stianst for picking this up again. I don't have time to work on this today / next few days. So if you want to work on this now feel free to adopt it.

I don't either right now, but want to make sure we get this sorted and included in 26.5. We can work together on this in some form :)

Moving forward, instead of continuing this PR, we should close it and create a new one. I think we can salvage some of the ideas from this PR and add this feature in a stepwise manner.

Some questions/thoughts regarding your proposal:

We could use something like the following:

  1. Add an optional Resource URI attribute to clients
  2. Get a list of all clients the client has a client role scope on
  3. For each resource parameter check it matches the Resource URI of one of the clients from 2

Q1) How to configure the (primary) resource indicator for a client? For 1. Would you like to use a new client attribute or a dedicated property for this? I initially thought of a client attribute, but the (primary) resource indicator must be unique among all clients in a realm. I'm not sure if we already do this somewhere, but we may need to add a new field to the client JPA entity to store the resource indicator URI, making it easier to ensure uniqueness at the database level. We can then expose this as a dedicated client property (more work, which might require admin API consumer updates) or pass it through the attributes (a hacky solution, but it would not break admin API consumers).

With this, we would only allow one (primary?) resource indicator per client for now, which can be used as an alias to target this specific client/resource server via the resource parameter. However, later we may need to support additional (sub-resource) indicators per resource server (e.g., if those clients represent an API gateway or another collection of resource servers). Those additional resource indicators might come from another source, but we can add support for that later. In my PR, I introduced an OAuth2ResourceIndicatorResolver SPI for this.

I'm not actually convinced it has to be unique, or at least not enforced at the DB level. Only thing that would happen if two services are registered with the same resource URI is that a client would pick one; or if it only has a relationship with one then it will ignore the other.

If there is a duplicated; we don't know if it's the first or the second that is using the wrong resource URI; or maybe actually in fact there is two clients for a given service, and it uses them for different purposes.

With what I'm proposing below when clients and services are linked this also becomes less of an issue.

Q2) Mechanism to select the eligible resource indicators for a given client Do you mean to use the client role scope for selecting other related clients and derive the resource indicator from that? Doesn't this require clients to specify at least one client role for selection?

To verify this: Imagine we have a photos-app OIDC client that can obtain tokens and two APIs that the photos-app should be able to access: photos-api, accounts-api. We create photos-api and accounts-api clients as a resource server (all flows disabled). For both, we specify resource indicators, such as https://accounts.acme.test and https://photos.acme.test. We now want to express that a photos-app token should be able to access photoz-api and accounts-api via the resource parameter in the authorization / token request. With your method, I need to define at least one role in the photoz-api and accounts-api clients to be able to add them to the client role scope selection of the photoz-app. So I create a "dummy" role like access for photos-api, accounts-api, and add them to the client role scope of the photos-app.

Is this what you had in mind?

Something along those lines; let's continue the discussion around that in #43179

For resource indicators let's assume there is a way to list all clients a client can connect to, so that we have a limited set of clients to look for matching resource indicators.

Q3) How to match a client via resource indicator to the requested resource parameter?

Using the client role scope to select clients and derive the eligible resource indicators is a simple solution. Still, it requires additional configuration (role definitions) and is also limited to just one indicator (which is fine for now). However, I wonder if we should make this pluggable from the start and use a new SPI to derive eligible resource indicators for a client with it (a default implementation could use the client role scope for this purpose). I believe the clients selected need to have a resource indicator URI configured to be considered for matching the resource parameter. We could filter out clients without a resource indicator.

I'd start without it being pluggable - it's just less code and things to test; however, don't have any issues with a quick follow-up that makes it pluggable.

Q4) How do we specify whether a client supports the resource parameter or not? Is this enabled for all clients automatically? In my PR, I added a dedicated ResourceIndicatorMapper OIDC protocol mapper that admins could add to their clients to explicitly enable the handling of resource parameters, which added eligible resource indicator values to the aud claim after some filtering.

Don't think we need that if we have what I talk about above; it's basically supported as long as a client is connected to a different client that has a resource-uri configured

Q5) When and where do we determine the eligible resource indicators? In my PR, the set of eligible resource indicators was determined during authorization request handling and subsequently filtered based on the actual token request, allowing users to request a subset of the initially requested resources.

Not really following what you are saying here. What is an eligible resource indicator? How would a user request a subset? Are you talking about optional consents or something?

@thomasdarimont
Copy link
Contributor Author

Yes let's do this together :) I can send a new PR with just the minimal bits to support a new "Root Resource URI" client attribute for clients. I guess this setting should be placed in the "Advanced Settings" somehow and be empty by default.

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.

Resource Indicators for OAuth 2.0 (RFC 8707)

4 participants