[KEYCLOAK-5519] Token Exchange Docs

This commit is contained in:
Bill Burke 2017-10-02 18:35:43 -04:00
parent 76c101c88f
commit 1d71a4803c
21 changed files with 267 additions and 20 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

View file

@ -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
----