Merge pull request #209 from patriot1burke/master
[KEYCLOAK-5519] Token Exchange Docs
BIN
securing_apps/keycloak-images/exchange-idp-apply-policy.png
Normal file
After Width: | Height: | Size: 345 KiB |
BIN
securing_apps/keycloak-images/exchange-idp-client-policy.png
Normal file
After Width: | Height: | Size: 290 KiB |
BIN
securing_apps/keycloak-images/exchange-idp-permission-set.png
Normal file
After Width: | Height: | Size: 263 KiB |
BIN
securing_apps/keycloak-images/exchange-idp-permission-setup.png
Normal file
After Width: | Height: | Size: 331 KiB |
BIN
securing_apps/keycloak-images/exchange-idp-permission-unset.png
Normal file
After Width: | Height: | Size: 234 KiB |
After Width: | Height: | Size: 338 KiB |
After Width: | Height: | Size: 362 KiB |
After Width: | Height: | Size: 330 KiB |
After Width: | Height: | Size: 245 KiB |
BIN
securing_apps/keycloak-images/exchange-target-client-policy.png
Normal file
After Width: | Height: | Size: 280 KiB |
BIN
securing_apps/keycloak-images/exchange-users-apply-policy.png
Normal file
After Width: | Height: | Size: 305 KiB |
BIN
securing_apps/keycloak-images/exchange-users-client-policy.png
Normal file
After Width: | Height: | Size: 284 KiB |
BIN
securing_apps/keycloak-images/exchange-users-permission-set.png
Normal file
After Width: | Height: | Size: 325 KiB |
After Width: | Height: | Size: 292 KiB |
After Width: | Height: | Size: 222 KiB |
BIN
securing_apps/rhsso-images/exchange-idp-apply-policy.png
Normal file
After Width: | Height: | Size: 337 KiB |
BIN
securing_apps/rhsso-images/exchange-idp-client-policy.png
Normal file
After Width: | Height: | Size: 280 KiB |
BIN
securing_apps/rhsso-images/exchange-idp-permission-set.png
Normal file
After Width: | Height: | Size: 251 KiB |
BIN
securing_apps/rhsso-images/exchange-idp-permission-setup.png
Normal file
After Width: | Height: | Size: 321 KiB |
BIN
securing_apps/rhsso-images/exchange-idp-permission-unset.png
Normal file
After Width: | Height: | Size: 223 KiB |
After Width: | Height: | Size: 331 KiB |
After Width: | Height: | Size: 350 KiB |
After Width: | Height: | Size: 317 KiB |
After Width: | Height: | Size: 212 KiB |
BIN
securing_apps/rhsso-images/exchange-target-client-policy.png
Normal file
After Width: | Height: | Size: 273 KiB |
BIN
securing_apps/rhsso-images/exchange-users-apply-policy.png
Normal file
After Width: | Height: | Size: 294 KiB |
BIN
securing_apps/rhsso-images/exchange-users-client-policy.png
Normal file
After Width: | Height: | Size: 273 KiB |
BIN
securing_apps/rhsso-images/exchange-users-permission-set.png
Normal file
After Width: | Height: | Size: 315 KiB |
BIN
securing_apps/rhsso-images/exchange-users-permission-setup.png
Normal file
After Width: | Height: | Size: 281 KiB |
BIN
securing_apps/rhsso-images/exchange-users-permission-unset.png
Normal file
After Width: | Height: | Size: 211 KiB |
|
@ -91,3 +91,4 @@ include::topics/saml/mod-auth-mellon.adoc[]
|
||||||
include::topics/docker/docker-overview.adoc[]
|
include::topics/docker/docker-overview.adoc[]
|
||||||
include::topics/client-registration.adoc[]
|
include::topics/client-registration.adoc[]
|
||||||
include::topics/client-registration/client-registration-cli.adoc[]
|
include::topics/client-registration/client-registration-cli.adoc[]
|
||||||
|
include::topics/token-exchange/token-exchange.adoc[]
|
||||||
|
|
463
securing_apps/topics/token-exchange/token-exchange.adoc
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
|
||||||
|
[[_token-exchange]]
|
||||||
|
|
||||||
|
== Token Exchange
|
||||||
|
|
||||||
|
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.
|
||||||
|
A client may want to exchange a {project_token} for a token stored for a linked social provider account.
|
||||||
|
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
|
||||||
|
|
||||||
|
Token exchange in {project_name} is a very loose implementation of the link:http://www.ietf.org/id/draft-ietf-oauth-token-exchange-09.txt[OAuth Token Exchange] specification at the IETF.
|
||||||
|
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.
|
||||||
|
|
||||||
|
----
|
||||||
|
/realms/{realm}/protocol/openid-connect/token
|
||||||
|
----
|
||||||
|
|
||||||
|
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
|
||||||
|
realm. Here's a list of form parameters
|
||||||
|
|
||||||
|
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
|
||||||
|
and OpenID Connect token types are supported. The default value for this depends on whether the
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
a JSON document as described in the link:http://www.ietf.org/id/draft-ietf-oauth-token-exchange-09.txt[OAuth Token Exchange] specification.
|
||||||
|
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
clients requesting a an external issuer through the `requested_issuer`paramater.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
permission to exchange. This is discusssed more later in this chapter.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
=== Internal Token to Internal Token Exchange
|
||||||
|
|
||||||
|
With an internal token to token exchange you have an existing token minted to a specific client and you want to exchange
|
||||||
|
this token for a new one minted for a different target client. Why would you want to do this? This generally happens
|
||||||
|
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.
|
||||||
|
|
||||||
|
[[_client_to_client_permission]]
|
||||||
|
==== Granting Permission for the Exchange
|
||||||
|
|
||||||
|
Clients that want to exchange tokens for a different client need to be authorized in the admin console to do so.
|
||||||
|
You'll need to define a `token-exchange` fine grain permission in the target client you want permission to exchange to.
|
||||||
|
|
||||||
|
.Target Client Permission
|
||||||
|
image:{project_images}/exchange-target-client-permission-unset.png[]
|
||||||
|
|
||||||
|
Toggle the `Permissions Enabled` switch to true.
|
||||||
|
|
||||||
|
.Target Client Permission
|
||||||
|
image:{project_images}/exchange-target-client-permission-set.png[]
|
||||||
|
|
||||||
|
You should see a `token-exchange` link on the page. Click that to start defining the permission. It will bring you
|
||||||
|
to this page.
|
||||||
|
|
||||||
|
.Target Client Exchange Permission Setup
|
||||||
|
image:{project_images}/exchange-target-client-permission-setup.png[]
|
||||||
|
|
||||||
|
You'll have to define a policy for this permission. Click the `Authorization` link, go to the `Policies` tab and create
|
||||||
|
a `Client` Policy.
|
||||||
|
|
||||||
|
.Client Policy Creation
|
||||||
|
image:{project_images}/exchange-target-client-policy.png[]
|
||||||
|
|
||||||
|
Here you enter in the starting client, that is the authenticated client that is requesting a token exchange. After you
|
||||||
|
create this policy, go back to the target client's `token-exchange` permission and add the client policy you just
|
||||||
|
defined.
|
||||||
|
|
||||||
|
.Apply Client Policy
|
||||||
|
image:{project_images}/exchange-target-client-exchange-apply-policy.png[]
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
==== Making the Request
|
||||||
|
|
||||||
|
When your client is exchanging an existing token for a token targeting another client, you must use the `audience` parameter.
|
||||||
|
This parameter must be the client identifier for the target client that you configured in the admin console.
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
curl -X POST \
|
||||||
|
-d "client_id=starting-client" \
|
||||||
|
-d "client_secret=geheim" \
|
||||||
|
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
|
||||||
|
-d "subject_token=...." \
|
||||||
|
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token"
|
||||||
|
-d "audience=target-client" \
|
||||||
|
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
an example JSON response you get back from this call.
|
||||||
|
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"access_token" : "....",
|
||||||
|
"refresh_token" : "....",
|
||||||
|
"expires_in" : 3600
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Internal Token to External Token Exchange
|
||||||
|
|
||||||
|
You can exchange a realm token for an externl token minted by an external identity provider. This external identity provider
|
||||||
|
must be configured within the `Identity Provider` section of the admin console. Currently only OAuth/OpenID Connect based external
|
||||||
|
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.
|
||||||
|
|
||||||
|
[[_grant_permission_external_exchange]]
|
||||||
|
==== Granting Permission for the Exchange
|
||||||
|
|
||||||
|
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 must go to the identity provider's configuration page to the `Permissions` tab.
|
||||||
|
|
||||||
|
.Identity Provider Permission
|
||||||
|
image:{project_images}/exchange-idp-permission-unset.png[]
|
||||||
|
|
||||||
|
Toggle the `Permissions Enabled` switch to true.
|
||||||
|
|
||||||
|
.Identity Provier Permission
|
||||||
|
image:{project_images}/exchange-idp-permission-set.png[]
|
||||||
|
|
||||||
|
You should see a `token-exchange` link on the page. Click that to start defining the permission. It will bring you
|
||||||
|
to this page.
|
||||||
|
|
||||||
|
.Identity Provider Exchange Permission Setup
|
||||||
|
image:{project_images}/exchange-idp-permission-setup.png[]
|
||||||
|
|
||||||
|
You'll have to define a policy for this permission. Click the `Authorization` link, go to the `Policies` tab and create
|
||||||
|
a `Client` Policy.
|
||||||
|
|
||||||
|
.Client Policy Creation
|
||||||
|
image:{project_images}/exchange-idp-client-policy.png[]
|
||||||
|
|
||||||
|
Here you enter in the starting client, that is the authenticated client that is requesting a token exchange. After you
|
||||||
|
create this policy, go back to the identity providers's `token-exchange` permission and add the client policy you just
|
||||||
|
defined.
|
||||||
|
|
||||||
|
.Apply Client Policy
|
||||||
|
image:{project_images}/exchange-idp-apply-policy.png[]
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[[_internal_external_making_request]]
|
||||||
|
==== Making the Request
|
||||||
|
|
||||||
|
When your client is exchanging an existing internal token to an external one, you must provide the
|
||||||
|
`requested_issuer` parameter. The parameter must be the alias of a configured identity provider.
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
curl -X POST \
|
||||||
|
-d "client_id=starting-client" \
|
||||||
|
-d "client_secret=geheim" \
|
||||||
|
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
|
||||||
|
-d "subject_token=...." \
|
||||||
|
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token"
|
||||||
|
-d "requested_issuer=google" \
|
||||||
|
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
so that the client can perform link:{developerguide_link}[Client Initiated Account Linking]. Most (all?)
|
||||||
|
providers requiring linking through browser OAuth protocol. With the `account-link-url` just add a `redirect_uri`
|
||||||
|
query parameter to it and you can forward browsers to perform the link.
|
||||||
|
|
||||||
|
=== External Token to Internal Token Exchange
|
||||||
|
|
||||||
|
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
|
||||||
|
and or refresh token depending on the `requested_toke_type` parameter value. You should note that this new
|
||||||
|
user session will remain active until it times out or until you call the logout endpoint of the realm passing this
|
||||||
|
new access token.
|
||||||
|
|
||||||
|
These types of changes required a configured identity provider in the admin console.
|
||||||
|
|
||||||
|
NOTE: Only OIDC identity providers are support at this time. Validate signatures switch is required and you must
|
||||||
|
provide either the public key to validate external token signatures, or a valid key URL to lookup key
|
||||||
|
identifiers contained in the token.
|
||||||
|
|
||||||
|
|
||||||
|
==== Granting Permission for the Exchange
|
||||||
|
|
||||||
|
Before external token exchanges can be done, you must grant permission for the calling client to make the exchange. This
|
||||||
|
permission is granted in the same manner as <<_grant_permission_external_exchange, interal to external permission is granted>>.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
==== Making the Request
|
||||||
|
|
||||||
|
When your client is exchanging an existing internal token to an external one, you must provide the
|
||||||
|
`subject_issuer` parameter. This parameter must be the alias of a configured identity provider. The
|
||||||
|
`subject_token_type` parameter must be specified and either be an identity token, `urn:ietf:params:oauth:token-type:id_token`,
|
||||||
|
or a JWT access token type `urn:ietf:params:oauth:token-type:jwt:access_token`. The `subject_token` must be a JWT
|
||||||
|
irregardless and must be signed using Json Web Signatures.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
curl -X POST \
|
||||||
|
-d "client_id=starting-client" \
|
||||||
|
-d "client_secret=geheim" \
|
||||||
|
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
|
||||||
|
-d "subject_token=...." \
|
||||||
|
-d "subject_issuer=myOidcProvider" \
|
||||||
|
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:id_token"
|
||||||
|
-d "audience=target-client" \
|
||||||
|
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
=== Impersonation
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
==== Granting Permission for the Exchange
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
==== Making the Request
|
||||||
|
|
||||||
|
Make the request as described in other chapters except additionally specify the `request_subject` parameter. The
|
||||||
|
value of this parameter must be a username or user id.
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
curl -X POST \
|
||||||
|
-d "client_id=starting-client" \
|
||||||
|
-d "client_secret=geheim" \
|
||||||
|
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
|
||||||
|
-d "subject_token=...." \
|
||||||
|
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token" \
|
||||||
|
-d "audience=target-client" \
|
||||||
|
-d "requested_subject=wburke" \
|
||||||
|
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Direct Naked Impersonation
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
==== Granting Permission for the Exchange
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Additionaly, the calling client must be granted permission to impersonate users. In the admin console, go to the
|
||||||
|
`Users` screen and click on the `Permissions` tab.
|
||||||
|
|
||||||
|
.Users Permission
|
||||||
|
image:{project_images}/exchange-users-permission-unset.png[]
|
||||||
|
|
||||||
|
Toggle the `Permissions Enabled` switch to true.
|
||||||
|
|
||||||
|
.Identity Provier Permission
|
||||||
|
image:{project_images}/exchange-users-permission-set.png[]
|
||||||
|
|
||||||
|
You should see a `impersonation` link on the page. Click that to start defining the permission. It will bring you
|
||||||
|
to this page.
|
||||||
|
|
||||||
|
.Users Impersonation Permission Setup
|
||||||
|
image:{project_images}/exchange-users-permission-setup.png[]
|
||||||
|
|
||||||
|
You'll have to define a policy for this permission. Click the `Authorization` link, go to the `Policies` tab and create
|
||||||
|
a `Client` Policy.
|
||||||
|
|
||||||
|
.Client Policy Creation
|
||||||
|
image:{project_images}/exchange-users-client-policy.png[]
|
||||||
|
|
||||||
|
Here you enter in the starting client, that is the authenticated client that is requesting a token exchange. After you
|
||||||
|
create this policy, go back to the users' `impersonation` permission and add the client policy you just
|
||||||
|
defined.
|
||||||
|
|
||||||
|
.Apply Client Policy
|
||||||
|
image:{project_images}/exchange-users-apply-policy.png[]
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
==== Making the Request
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
curl -X POST \
|
||||||
|
-d "client_id=starting-client" \
|
||||||
|
-d "client_secret=geheim" \
|
||||||
|
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
|
||||||
|
-d "requested_subject=wburke" \
|
||||||
|
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|