admin 管理员组

文章数量: 1086019

I have a public client app from which I obtained the access token of a user via standard flow. I now want to obtain a token for the confidential client bridge of the same user (sub = same user ID).

I tried following but it's sadly very ambiguously worded in some places.

My current request always gives me Invalid token.

The request:

curl --location '' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode 'subject_token=<app token>' \
--data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:access_token' \
--data-urlencode 'audience=bridge' \
--data-urlencode 'client_id=app'

Since it's a public client, there is no client secret. The manual only says that public clients are not allowed to perform token exchanges using tokens from other clients but the token is from the client app itself or is there a comma missing and it means a public client cannot exchange for other clients using a token in general?

My goal is just to trade down to a token with less permissions. But if the public client can only trade for other tokens of itself, how do I strip permissions from the resulting token?

Edit: I also added the bridge client to the audience field of the app user token, but sill no luck.

I have a public client app from which I obtained the access token of a user via standard flow. I now want to obtain a token for the confidential client bridge of the same user (sub = same user ID).

I tried following https://www.keycloak./securing-apps/token-exchange#_internal_internal_making_request but it's sadly very ambiguously worded in some places.

My current request always gives me Invalid token.

The request:

curl --location 'https://example/keycloak/realms/test/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode 'subject_token=<app token>' \
--data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:access_token' \
--data-urlencode 'audience=bridge' \
--data-urlencode 'client_id=app'

Since it's a public client, there is no client secret. The manual only says that public clients are not allowed to perform token exchanges using tokens from other clients but the token is from the client app itself or is there a comma missing and it means a public client cannot exchange for other clients using a token in general?

My goal is just to trade down to a token with less permissions. But if the public client can only trade for other tokens of itself, how do I strip permissions from the resulting token?

Edit: I also added the bridge client to the audience field of the app user token, but sill no luck.

Share Improve this question edited Mar 28 at 9:44 Splitframe asked Mar 27 at 13:22 SplitframeSplitframe 3344 silver badges18 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 0 +100

Usually in token exchange the main goal is one of the following and the token exchange request should include a scope parameter that represents something like a business area.

  • Downscoping - to reduce token scopes so that the target API does not receive the client's full access token. This is the most common case.

  • Upscoping - to call upstream APIs with scopes that you don't want the original client to have a relationship with. This should not be used if it operates outside user consent.

The client must be authorized to:

  • Use token exchange

  • Use any scopes requested, including for upscoping

  • The authorization server may deny certain operations, like upscoping, for public clients

Most commonly, token exchange is done by backend conponents, like an API gateway or an API. Before calling the upstream they replace the original token in the HTTP authorization header with the exchanged token. Therefore, this type of backend component must be registered as an OAuth client with the token exchange capability and scopes.

In your case I think an approach of the following kind, where you rule out causes one by one will help you understand Keycloak specific logic:

  • First find the error cause in Keycloak logs - if required, change the log level eg to DEBUG.

  • Change client permissions according to Keycloak docs, to grant token exchange permissions.

  • Temporarily configure a client secret to see if that makes a difference. If so you may need to redesign deployments, eg to make an API gateway or entry point API do the token exchange.

I finally got it working. For people that experience the same issues here are my detailed settings and requests.:

Public Client: app
Confidential Client: bridge

Grant permissions according to this manual: https://www.keycloak./securing-apps/token-exchange#_internal-token-to-internal-token-exchange

For public to public exchange:

curl -X POST \
--location 'https://mykeycloakurl/realms/myrealm/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=app' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:refresh_token' \
--data-urlencode 'scope=<scope to be included>' \
--data-urlencode 'subject_token=<user app token>'

For public to confidential (should be handled by a backend system to protect the client secret):

curl -X POST \ 
--location 'https://mykeycloakurl/realms/myrealm/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=bridge' \
--data-urlencode 'client_secret=ytN5ZmXorCo3772yXAVXNIbualdYtvtm' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:refresh_token' \
--data-urlencode 'subject_token=<user app token>'

Important the requests are POST.
And if you want to downgrade the scopes like @Gary Archer said you also need to manage the Scope policies accordingly. You can also specify a scope when exchanging public to confidential.

Want to clear what public client mean in token exchnage

token-exchnage allowed

app (public) --> app(public)

app (public) ---> bridge(confedential)

app (confedential) ---> bridge(confedential)

token-exhange not allowed

app(public) --> bridge(public)

app(confedential) -->bridge(public)

I belive you have configuredtoken-exchange scope permission for the client set to the audience parameter.

You need to control what scope you need if you want lesser permissions in exchanged token

本文标签: oauth 20Internal to Internal Token exchange with public clientStack Overflow