token-exchange

This commit is contained in:
Bill Burke 2017-09-29 21:45:07 -04:00
parent 4cb5b9dd2d
commit 76c101c88f
12 changed files with 217 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

View file

@ -88,3 +88,4 @@ include::topics/saml/mod-auth-mellon.adoc[]
include::topics/docker/docker-overview.adoc[]
include::topics/client-registration.adoc[]
include::topics/client-registration/client-registration-cli.adoc[]
include::topics/token-exchange/token-exchange.adoc[]

View file

@ -0,0 +1,216 @@
[[_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 feature 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.
==== 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 correct, 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.
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:
----
{
"access_token" : "....",
"refresh_token" : "....",
"expires_in" : 3600
}
----
=== 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
==== Granting Permission for the Exchange
==== Making the Request
=== Direct Naked Impersonation
==== Granting Permission for the Exchange
==== Making the Request