2017-09-30 01:45:07 +00:00
2019-01-21 17:01:40 +00:00
[[_token-exchange]]
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
== Using token exchange
2017-09-30 01:45:07 +00:00
2017-12-21 10:36:08 +00:00
:tech_feature_name: Token Exchange
2018-12-04 18:07:52 +00:00
:tech_feature_setting: -Dkeycloak.profile.feature.token_exchange=enabled
2017-12-21 10:36:08 +00:00
include::../templates/techpreview.adoc[]
2019-12-19 22:17:26 +00:00
[NOTE]
====
2021-12-22 10:28:04 +00:00
In order to use token exchange you should also enable the `token_exchange` feature. Please, take a look at link:{installguide_profile_link}[{installguide_profile_name}].
2019-12-19 22:17:26 +00:00
====
2021-12-22 10:28:04 +00:00
=== How token exchange works
2017-09-30 01:45:07 +00:00
In {project_name}, token exchange is the process of using a set of credentials or token to obtain an entirely different token.
A client may want to invoke on a less trusted application so it may want to downgrade the current token it has.
2018-05-29 08:02:18 +00:00
A client may want to exchange a {project_name} token for a token stored for a linked social provider account.
2017-09-30 01:45:07 +00:00
You may want to trust external tokens minted by other {project_name} realms or foreign IDPs. A client may have a need
to impersonate a user. Here's a short summary of the current capabilities of {project_name} around token exchange.
* A client can exchange an existing {project_name} token created for a specific client for a new token targeted to a different client
* A client can exchange an existing {project_name} token for an external token, i.e. a linked Facebook account
* A client can exchange an external token for a {project_name} token.
* A client can impersonate a user
2021-05-27 12:29:27 +00:00
Token exchange in {project_name} is a very loose implementation of the link:https://datatracker.ietf.org/doc/html/rfc8693[OAuth Token Exchange] specification at the IETF.
2017-09-30 01:45:07 +00:00
We have extended it a little, ignored some of it, and loosely interpreted other parts of the specification. It is
a simple grant type invocation on a realm's OpenID Connect token endpoint.
2022-02-08 13:07:16 +00:00
[source,subs="attributes+"]
2017-09-30 01:45:07 +00:00
----
2022-02-08 13:07:16 +00:00
{kc_realms_path}/{realm}/protocol/openid-connect/token
2017-09-30 01:45:07 +00:00
----
It accepts form parameters (`application/x-www-form-urlencoded`) as input and the output depends on the type of token you requested an exchange for.
Token exchange is a client endpoint so requests must provide authentication information for the calling client.
Public clients specify their client identifier as a form parameter. Confidential clients can also use form parameters
to pass their client id and secret, Basic Auth, or however your admin has configured the client authentication flow in your
2021-12-22 10:28:04 +00:00
realm.
==== Form parameters
2017-09-30 01:45:07 +00:00
client_id::
_REQUIRED MAYBE._ This parameter is required for clients using form parameters for authentication. If you are using
Basic Auth, a client JWT token, or client cert authentication, then do not specify this parameter.
client_secret::
_REQUIRED MAYBE_. This parameter is required for clients using form parameters for authentication and using a client secret as a credential.
Do not specify this parameter if client invocations in your realm are authenticated by a different means.
grant_type::
_REQUIRED._ The value of the parameter must be `urn:ietf:params:oauth:grant-type:token-exchange`.
subject_token::
_OPTIONAL._ A security token that represents the identity of the party on behalf of whom the request is being made. It is required if you are exchanging an existing token for a new one.
subject_issuer::
_OPTIONAL._ Identifies the issuer of the `subject_token`. It can be left blank if the token comes from the current realm or if the issuer
can be determined from the `subject_token_type`. Otherwise it is required to be specified. Valid values are the alias of an `Identity Provider` configured for your realm. Or an issuer claim identifier
configured by a specific `Identity Provider`.
subject_token_type::
_OPTIONAL._ This parameter is the type of the token passed with the `subject_token` parameter. This defaults
to `urn:ietf:params:oauth:token-type:access_token` if the `subject_token` comes from the realm and is an access token.
If it is an external token, this parameter may or may not have to be specified depending on the requirements of the
`subject_issuer`.
requested_token_type::
_OPTIONAL._ This parameter represents the type of token the client wants to exchange for. Currently only oauth
2020-07-22 13:20:07 +00:00
and OpenID Connect token types are supported. The default value for this depends on whether it
2017-09-30 01:45:07 +00:00
is `urn:ietf:params:oauth:token-type:refresh_token` in which case you will be returned both an access token and refresh
token within the response. Other appropriate values are `urn:ietf:params:oauth:token-type:access_token` and `urn:ietf:params:oauth:token-type:id_token`
audience::
_OPTIONAL._ This parameter specifies the target client you want the new token minted for.
requested_issuer::
_OPTIONAL._ This parameter specifies that the client wants a token minted by an external provider. It must
be the alias of an `Identity Provider` configured within the realm.
requested_subject::
_OPTIONAL._ This specifies a username or user id if your client wants to impersonate a different user.
scope::
_NOT IMPLEMENTED._ This parameter represents the target set of OAuth and OpenID Connect scopes the client
is requesting. It is not implemented at this time but will be once {project_name} has better support for
scopes in general.
2017-10-02 22:35:43 +00:00
NOTE: We currently only support OpenID Connect and OAuth exchanges. Support for SAML based clients and identity providers may be added in the future depending on user demand.
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
==== Responses from a token exchange request
2017-09-30 01:45:07 +00:00
A successful response from an exchange invocation will return the HTTP 200 response code with a content type that
depends on the `requested-token-type` and `requested_issuer` the client asks for. OAuth requested token types will return
2021-05-27 12:29:27 +00:00
a JSON document as described in the link:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-token-exchange-16[OAuth Token Exchange] specification.
2017-09-30 01:45:07 +00:00
2018-02-08 21:09:26 +00:00
[source,json]
2017-09-30 01:45:07 +00:00
----
{
"access_token" : ".....",
"refresh_token" : ".....",
"expires_in" : "...."
}
----
Clients requesting a refresh token will get back both an access and refresh token in the response. Clients requesting only
access token type will only get an access token in the response. Expiration information may or may not be included for
2018-05-29 08:02:18 +00:00
clients requesting an external issuer through the `requested_issuer` paramater.
2017-09-30 01:45:07 +00:00
Error responses generally fall under the 400 HTTP response code category, but other error status codes may be returned
depending on the severity of the error. Error responses may include content depending on the `requested_issuer`.
OAuth based exchanges may return a JSON document as follows:
2018-02-08 21:09:26 +00:00
[source,json]
2017-09-30 01:45:07 +00:00
----
{
"error" : "...."
"error_description" : "...."
}
----
Additional error claims may be returned depending on the exchange type. For example, OAuth Identity Providers may include
an additional `account-link-url` claim if the user does not have a link to an identity provider. This link can be used
for a client initiated link request.
NOTE: Token exchange setup requires knowledge of fine grain admin permissions (See the link:{adminguide_link}[{adminguide_name}] for more information). You will need to grant clients
2017-11-16 03:51:42 +00:00
permission to exchange. This is discussed more later in this chapter.
2017-09-30 01:45:07 +00:00
The rest of this chapter discusses the setup requirements and provides examples for different exchange scenarios.
For simplicity's sake, let's call a token minted by the current realm as an _internal_ token and a token minted by
an external realm or identity provider as an _external_ token.
2021-12-22 10:28:04 +00:00
=== Internal token to internal token exchange
2017-09-30 01:45:07 +00:00
With an internal token to token exchange you have an existing token minted to a specific client and you want to exchange
2021-12-22 10:28:04 +00:00
this token for a new one minted for a different target client. Why would you want to do this? This generally happens+
2017-09-30 01:45:07 +00:00
when a client has a token minted for itself, and needs to make additional requests to other applications that require different
claims and permissions within the access token. Other reasons this type of exchange might be required is if you
need to perform a "permission downgrade" where your app needs to invoke on a less trusted app and you don't want
to propagate your current access token.
2019-01-21 17:01:40 +00:00
[[_client_to_client_permission]]
2021-12-22 10:28:04 +00:00
==== Granting permission for the exchange
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
Clients that want to exchange tokens for a different client need to be authorized in the Admin Console.
You need to define a `token-exchange` fine grain permission in the target client you want permission to exchange to.
2017-09-30 01:45:07 +00:00
.Target Client Permission
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-target-client-permission-unset.png[Target Client Permission]
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
.Procedure
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
. Toggle *Permissions Enabled* to *ON*.
+
2017-09-30 01:45:07 +00:00
.Target Client Permission
image:{project_images}/exchange-target-client-permission-set.png[]
2021-12-22 10:28:04 +00:00
+
That page displays a *token-exchange* link.
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
. Click that link to start defining the permission.
+
This setup page displays.
+
2017-09-30 01:45:07 +00:00
.Target Client Exchange Permission Setup
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-target-client-permission-setup.png[Target Client Exchange Permission Setup]
. Define a policy for this permission by clicking the *Authorization* link
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
. Click the *Policies* tab.
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
. Create a *Client* Policy.
+
2017-09-30 01:45:07 +00:00
.Client Policy Creation
image:{project_images}/exchange-target-client-policy.png[]
2021-12-22 10:28:04 +00:00
. Enter in the starting client that is the authenticated client that is requesting a token exchange.
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
. After you create this policy, go back to the target client's *token-exchange* permission and add the client policy you just defined.
+
2017-09-30 01:45:07 +00:00
.Apply Client Policy
image:{project_images}/exchange-target-client-exchange-apply-policy.png[]
2017-10-02 22:35:43 +00:00
Your client now has permission to invoke. If you do not do this correctly, you will get a 403 Forbidden response if you
2017-09-30 01:45:07 +00:00
try to make an exchange.
2021-12-22 10:28:04 +00:00
==== Making the request
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
When your client is exchanging an existing token for a token targeting another client, you use the `audience` parameter.
This parameter must be the client identifier for the target client that you configured in the Admin Console.
2017-09-30 01:45:07 +00:00
2022-02-08 13:07:16 +00:00
[source,bash,subs="attributes+"]
2017-09-30 01:45:07 +00:00
----
curl -X POST \
-d "client_id=starting-client" \
2021-02-03 12:54:30 +00:00
-d "client_secret=the client secret" \
2017-09-30 01:45:07 +00:00
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=...." \
2018-04-06 05:26:20 +00:00
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token" \
2017-09-30 01:45:07 +00:00
-d "audience=target-client" \
2022-02-08 13:07:16 +00:00
http://localhost:8080{kc_realms_path}/myrealm/protocol/openid-connect/token
2017-09-30 01:45:07 +00:00
----
2017-10-02 22:35:43 +00:00
2017-09-30 01:45:07 +00:00
The `subject_token` parameter must be an access token for the target realm. If your `requested_token_type` parameter
is a refresh token type, then the response will contain both an access token, refresh token, and expiration. Here's
2017-10-02 22:35:43 +00:00
an example JSON response you get back from this call.
2017-09-30 01:45:07 +00:00
2017-10-02 22:35:43 +00:00
[source,json]
2017-09-30 01:45:07 +00:00
----
{
"access_token" : "....",
"refresh_token" : "....",
"expires_in" : 3600
}
----
2021-12-22 10:28:04 +00:00
=== Internal token to external token exchange
2017-09-30 01:45:07 +00:00
2017-10-02 22:35:43 +00:00
You can exchange a realm token for an externl token minted by an external identity provider. This external identity provider
2021-12-22 10:28:04 +00:00
must be configured within the `Identity Provider` section of the Admin Console. Currently only OAuth/OpenID Connect based external
2017-10-02 22:35:43 +00:00
identity providers are supported, this includes all social providers. {project_name} does not perform a backchannel exchange to the external provider. So if the account
is not linked, you will not be able to get the external token. To be able to obtain an external token one of
these conditions must be met:
* The user must have logged in with the external identity provider at least once
* The user must have linked with the external identity provider through the User Account Service
* The user account was linked through the external identity provider using link:{developerguide_link}[Client Initiated Account Linking] API.
Finally, the external identity provider must have been configured to store tokens, or, one of the above actions must
have been performed with the same user session as the internal token you are exchanging.
If the account is not linked, the exchange response will contain a link you can use to establish it. This is
discussed more in the <<_internal_external_making_request, Making the Request>> section.
2019-01-21 17:01:40 +00:00
[[_grant_permission_external_exchange]]
2021-12-22 10:28:04 +00:00
==== Granting permission for the exchange
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
Internal to external token exchange requests will be denied with a 403, Forbidden response until you grant permission for the calling client to exchange tokens with the external identity provider. To grant permission to the client, you go to the identity provider's configuration page to the *Permissions* tab.
2017-10-02 22:35:43 +00:00
.Identity Provider Permission
image:{project_images}/exchange-idp-permission-unset.png[]
2021-12-22 10:28:04 +00:00
.Procedure
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Toggle *Permissions Enabled* to *ON*.
+
2017-11-16 03:51:42 +00:00
.Identity Provider Permission
2017-10-02 22:35:43 +00:00
image:{project_images}/exchange-idp-permission-set.png[]
2021-12-22 10:28:04 +00:00
+
The page displays *token-exchange* link.
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Click the link to start defining the permission.
+
This setup page appears.
+
2017-10-02 22:35:43 +00:00
.Identity Provider Exchange Permission Setup
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-idp-permission-setup.png[Identity Provider Exchange Permission Setup]
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Click the *Authorization* link and go to the *Policies* tab to create a client policy.
+
2017-10-02 22:35:43 +00:00
.Client Policy Creation
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-idp-client-policy.png[Client Policy Creation]
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Enter the starting client that is the authenticated client that is requesting a token exchange.
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Return to the identity provider's *token-exchange* permission and add the client policy you just defined.
+
2017-10-02 22:35:43 +00:00
.Apply Client Policy
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-idp-apply-policy.png[Apply Client Policy]
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
Your client now has permission to invoke. If you do not do this correctly, you will get a 403 Forbidden response if you try to make an exchange.
2017-10-02 22:35:43 +00:00
2019-01-21 17:01:40 +00:00
[[_internal_external_making_request]]
2021-12-22 10:28:04 +00:00
==== Making the request
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
When your client is exchanging an existing internal token to an external one, you provide the `requested_issuer` parameter. The parameter must be the alias of a configured identity provider.
2017-10-02 22:35:43 +00:00
2022-02-08 13:07:16 +00:00
[source,bash,subs="attributes+"]
2017-10-02 22:35:43 +00:00
----
curl -X POST \
-d "client_id=starting-client" \
2021-02-03 12:54:30 +00:00
-d "client_secret=the client secret" \
2017-10-02 22:35:43 +00:00
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=...." \
2018-04-06 05:26:20 +00:00
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
2017-10-02 22:35:43 +00:00
-d "requested_issuer=google" \
2022-02-08 13:07:16 +00:00
http://localhost:8080{kc_realms_path}/myrealm/protocol/openid-connect/token
2017-10-02 22:35:43 +00:00
----
The `subject_token` parameter must be an access token for the target realm. The `requested_token_type` parameter
must be `urn:ietf:params:oauth:token-type:access_token` or left blank. No other requested token type is supported
at this time. Here's
an example successful JSON response you get back from this call.
[source,json]
----
{
"access_token" : "....",
"expires_in" : 3600
"account-link-url" : "https://...."
}
----
If the external identity provider is not linked for whatever reason, you will get an HTTP 400 response code with
this JSON document:
[source,json]
----
{
"error" : "....",
"error_description" : "..."
"account-link-url" : "https://...."
}
----
The `error` claim will be either `token_expired` or `not_linked`. The `account-link-url` claim is provided
2021-12-22 10:28:04 +00:00
so that the client can perform link:{developerguide_link}[Client Initiated Account Linking]. Most, if not all,
providers require linking through browser OAuth protocol. With the `account-link-url` just add a `redirect_uri`
2017-10-02 22:35:43 +00:00
query parameter to it and you can forward browsers to perform the link.
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
=== External token to internal token exchange
2017-09-30 01:45:07 +00:00
2017-10-02 22:35:43 +00:00
You can trust and exchange external tokens minted by external identity providers for internal tokens. This can be
used to bridge between realms or just to trust tokens from your social provider. It works similarly to an identity provider
browser login in that a new user is imported into your realm if it doesn't exist.
NOTE: The current limitation on external token exchanges is that if the external token maps to an existing user an
exchange will not be allowed unless the existing user already has an account link to the external identity
provider.
When the exchange is complete, a user session will be created within the realm, and you will receive an access
2017-12-04 20:03:55 +00:00
and or refresh token depending on the `requested_token_type` parameter value. You should note that this new
2017-10-02 22:35:43 +00:00
user session will remain active until it times out or until you call the logout endpoint of the realm passing this
new access token.
2021-12-22 10:28:04 +00:00
These types of changes required a configured identity provider in the Admin Console.
2017-10-02 22:35:43 +00:00
2017-10-16 22:13:12 +00:00
NOTE: SAML identity providers are not supported at this time. Twitter tokens cannot be exchanged either.
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
==== Granting permission for the exchange
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
Before external token exchanges can be done, you grant permission for the calling client to make the exchange. This
2018-01-05 07:49:03 +00:00
permission is granted in the same manner as <<_grant_permission_external_exchange, internal to external permission is granted>>.
2017-10-02 22:35:43 +00:00
If you also provide an `audience` parameter whose value points to a different client other than the calling one, you
must also grant the calling client permission to exchange to the target client specific in the `audience` parameter. How
to do this is <<_client_to_client_permission, discussed earlier>> in this section.
2021-12-22 10:28:04 +00:00
==== Making the request
2017-09-30 01:45:07 +00:00
2017-10-16 22:13:12 +00:00
The `subject_token_type` must either be `urn:ietf:params:oauth:token-type:access_token` or `urn:ietf:params:oauth:token-type:jwt`.
2021-12-22 10:28:04 +00:00
If the type is `urn:ietf:params:oauth:token-type:access_token` you specify the `subject_issuer` parameter and it must be the
2017-10-16 22:13:12 +00:00
alias of the configured identity provider. If the type is `urn:ietf:params:oauth:token-type:jwt`, the provider will be matched via
the `issuer` claim within the JWT which must be the alias of the provider, or a registered issuer within the providers configuration.
For validation, if the token is an access token, the provider's user info service will be invoked to validate the token. A successful call
will mean that the access token is valid. If the subject token is a JWT and if the provider has signature validation enabled, that will be attempted,
otherwise, it will default to also invoking on the user info service to validate the token.
2017-10-02 22:35:43 +00:00
By default, the internal token minted will use the calling client to determine what's in the token using the protocol
mappers defined for the calling client. Alternatively, you can specify a different target client using the `audience`
parameter.
2022-02-08 13:07:16 +00:00
[source,bash,subs="attributes+"]
2017-10-02 22:35:43 +00:00
----
curl -X POST \
-d "client_id=starting-client" \
2021-02-03 12:54:30 +00:00
-d "client_secret=the client secret" \
2017-10-02 22:35:43 +00:00
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=...." \
-d "subject_issuer=myOidcProvider" \
2018-04-06 05:26:20 +00:00
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
2017-10-02 22:35:43 +00:00
-d "audience=target-client" \
2022-02-08 13:07:16 +00:00
http://localhost:8080{kc_realms_path}/myrealm/protocol/openid-connect/token
2017-10-02 22:35:43 +00:00
----
If your `requested_token_type` parameter
is a refresh token type, then the response will contain both an access token, refresh token, and expiration. Here's
an example JSON response you get back from this call.
[source,json]
----
{
"access_token" : "....",
"refresh_token" : "....",
"expires_in" : 3600
}
----
2017-09-30 01:45:07 +00:00
=== Impersonation
2017-10-02 22:35:43 +00:00
For internal and external token exchanges, the client can request on behalf of a user to impersonate a different user.
For example, you may have an admin application that needs to impersonate a user so that a support engineer can debug
a problem.
2021-12-22 10:28:04 +00:00
==== Granting permission for the exchange
2017-09-30 01:45:07 +00:00
2017-10-02 22:35:43 +00:00
The user that the subject token represents must have permission to impersonate other users. See the
link:{adminguide_link}[{adminguide_name}] on how to enable this permission. It can be done through a role or through
fine grain admin permissions.
2021-12-22 10:28:04 +00:00
==== Making the request
2017-09-30 01:45:07 +00:00
2019-08-12 15:07:44 +00:00
Make the request as described in other chapters except additionally specify the `requested_subject` parameter. The
2017-10-02 22:35:43 +00:00
value of this parameter must be a username or user id.
2022-02-08 13:07:16 +00:00
[source,bash,subs="attributes+"]
2017-10-02 22:35:43 +00:00
----
curl -X POST \
-d "client_id=starting-client" \
2021-02-03 12:54:30 +00:00
-d "client_secret=the client secret" \
2017-10-02 22:35:43 +00:00
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=...." \
2017-10-16 22:13:12 +00:00
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
2017-10-02 22:35:43 +00:00
-d "audience=target-client" \
-d "requested_subject=wburke" \
2022-02-08 13:07:16 +00:00
http://localhost:8080{kc_realms_path}/myrealm/protocol/openid-connect/token
2017-10-02 22:35:43 +00:00
----
2017-09-30 01:45:07 +00:00
=== Direct Naked Impersonation
2017-10-02 22:35:43 +00:00
You can make an internal token exchange request without providing a `subject_token`. This is called a direct
naked impersonation because it places a lot of trust in a client as that client can impersonate any user in the realm.
You might need this to bridge for applications where it is impossible to obtain a subject token to exchange. For example,
you may be integrating a legacy application that performs login directly with LDAP. In that case, the legacy app
is able to authenticate users itself, but not able to obtain a token.
WARNING: It is very risky to enable direct naked impersonation for a client. If the client's credentials are ever
stolen, that client can impersonate any user in the system.
2021-12-22 10:28:04 +00:00
==== Granting permission for the exchange
2017-09-30 01:45:07 +00:00
2017-10-02 22:35:43 +00:00
If the `audience` parameter is provided, then the calling client must have permission to exchange to the client. How
to set this up is discussed earlier in this chapter.
2021-12-22 10:28:04 +00:00
Additionally, the calling client must be granted permission to impersonate users. In the Admin Console.
.Procedure
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Click *Users* in the menu.
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Click the *Permissions* tab.
+
.User Permissions
image:{project_images}/exchange-users-permission-unset.png[User Permissions]
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Toggle *Permissions Enabled* to true.
+
2017-11-16 03:51:42 +00:00
.Identity Provider Permission
2017-10-02 22:35:43 +00:00
image:{project_images}/exchange-users-permission-set.png[]
2021-12-22 10:28:04 +00:00
+
The page displays an *impersonation* link.
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Click that link to start defining the permission.
+
This setup page displays.
+
2017-10-02 22:35:43 +00:00
.Users Impersonation Permission Setup
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-users-permission-setup.png[Users Impersonation Permission Setup]
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Click the *Authorization* link
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Go to the *Policies* tab and create a client policy.
+
2017-10-02 22:35:43 +00:00
.Client Policy Creation
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-users-client-policy.png[Client Policy Creation]
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Enter the starting client that is the authenticated client that is requesting a token exchange.
2017-10-02 22:35:43 +00:00
2021-12-22 10:28:04 +00:00
. Return to the users' *impersonation* permission and add the client policy you just
defined.
+
2017-10-02 22:35:43 +00:00
.Apply Client Policy
2021-12-22 10:28:04 +00:00
image:{project_images}/exchange-users-apply-policy.png[Apply Client Policy]
2017-10-02 22:35:43 +00:00
Your client now has permission to impersonate users. If you do not do this correctly, you will get a 403 Forbidden response if you
try to make this type of exchange.
NOTE: Public clients are not allowed to do direct naked impersonations.
2021-12-22 10:28:04 +00:00
==== Making the request
2017-09-30 01:45:07 +00:00
2017-10-02 22:35:43 +00:00
To make the request, simply specify the `requested_subject` parameter. This must be the username or user id of
a valid user. You can also specify an `audience` parameter if you wish.
2022-02-08 13:07:16 +00:00
[source,bash,subs="attributes+"]
2017-10-02 22:35:43 +00:00
----
curl -X POST \
-d "client_id=starting-client" \
2021-02-03 12:54:30 +00:00
-d "client_secret=the client secret" \
2017-10-02 22:35:43 +00:00
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "requested_subject=wburke" \
2022-02-08 13:07:16 +00:00
http://localhost:8080{kc_realms_path}/myrealm/protocol/openid-connect/token
2017-10-02 22:35:43 +00:00
----
2017-09-30 01:45:07 +00:00
2021-12-22 10:28:04 +00:00
=== Expand permission model with service accounts
2017-10-16 22:13:12 +00:00
2021-12-22 10:28:04 +00:00
When granting clients permission to exchange, you don't necessarily manually enable those permissions for each and every client.
2017-10-16 22:13:12 +00:00
If the client has a service account associated with it, you can use a role to group permissions together and assign exchange permissions
by assigning a role to the client's service account. For example, you might define a `naked-exchange` role and any service account that has that
role can do a naked exchange.
2021-12-22 10:28:04 +00:00
=== Exchange vulnerabilities
2017-10-16 22:13:12 +00:00
2021-12-22 10:28:04 +00:00
When you start allowing token exchanges, there are various things you have to both be aware of and careful of.
2017-10-16 22:13:12 +00:00
The first is public clients. Public clients do not have or require a client credential in order to perform an exchange. Anybody that has a valid
token will be able to __impersonate__ the public client and perform the exchanges that public client is allowed to perform. If there
are any untrustworthy clients that are managed by your realm, public clients may open up vulnerabilities in your permission models.
This is why direct naked exchanges do not allow public clients and will abort with an error if the calling client is public.
It is possible to exchange social tokens provided by Facebook, Google, etc. for a realm token. Be careful and vigilante on what
the exchange token is allowed to do as its not hard to create fake accounts on these social websites. Use default roles, groups, and identity provider mappers to control what attributes and roles
are assigned to the external social user.
Direct naked exchanges are quite dangerous. You are putting a lot of trust in the calling client that it will never leak out
its client credentials. If those credentials are leaked, then the thief can impersonate anybody in your system. This is in direct
contrast to confidential clients that have existing tokens. You have two factors of authentication, the access token and the client
credentials, and you're only dealing with one user. So use direct naked exchanges sparingly.