[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 |
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 |
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 |
|
@ -65,7 +65,7 @@ scope::
|
||||||
is requesting. It is not implemented at this time but will be once {project_name} has better support for
|
is requesting. It is not implemented at this time but will be once {project_name} has better support for
|
||||||
scopes in general.
|
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 feature depending on user demand.
|
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
|
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
|
depends on the `requested-token-type` and `requested_issuer` the client asks for. OAuth requested token types will return
|
||||||
|
@ -115,6 +115,7 @@ claims and permissions within the access token. Other reasons this type of exch
|
||||||
need to perform a "permission downgrade" where your app needs to invoke on a less trusted app and you don't want
|
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.
|
to propagate your current access token.
|
||||||
|
|
||||||
|
[[_client_to_client_permission]]
|
||||||
==== Granting Permission for the Exchange
|
==== 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.
|
Clients that want to exchange tokens for a different client need to be authorized in the admin console to do so.
|
||||||
|
@ -147,12 +148,12 @@ defined.
|
||||||
.Apply Client Policy
|
.Apply Client Policy
|
||||||
image:{project_images}/exchange-target-client-exchange-apply-policy.png[]
|
image:{project_images}/exchange-target-client-exchange-apply-policy.png[]
|
||||||
|
|
||||||
Your client now has permission to invoke. If you do not do this correct, you will get a 403 Forbidden response if you
|
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.
|
try to make an exchange.
|
||||||
|
|
||||||
==== Making the Request
|
==== Making the Request
|
||||||
|
|
||||||
When your client is exchange an existing token for a token targeting another client, you must use the `audience` parameter.
|
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.
|
This parameter must be the client identifier for the target client that you configured in the admin console.
|
||||||
|
|
||||||
[source,bash]
|
[source,bash]
|
||||||
|
@ -167,10 +168,187 @@ curl -X POST \
|
||||||
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token
|
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
|
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
|
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:
|
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" : "....",
|
"access_token" : "....",
|
||||||
|
@ -180,35 +358,104 @@ an example JSON response you get back from this call:
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
=== Internal Token to External Token Exchange
|
|
||||||
|
|
||||||
==== Granting Permission for the Exchange
|
|
||||||
|
|
||||||
==== Making the Request
|
|
||||||
|
|
||||||
|
|
||||||
=== External Token to Internal Token Exchange
|
|
||||||
|
|
||||||
==== Granting Permission for the Exchange
|
|
||||||
|
|
||||||
==== Making the Request
|
|
||||||
|
|
||||||
|
|
||||||
=== External Token to External Token Exchange
|
|
||||||
|
|
||||||
=== Impersonation
|
=== 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
|
==== 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
|
==== 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
|
=== 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
|
==== 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
|
==== 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
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|