[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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
@ -147,12 +148,12 @@ 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 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.
|
||||
|
||||
==== 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.
|
||||
|
||||
[source,bash]
|
||||
|
@ -167,10 +168,187 @@ curl -X POST \
|
|||
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:
|
||||
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" : "....",
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
|