[keycloak-dev] token service
Bill Burke
bburke at redhat.com
Tue Mar 14 18:32:45 EDT 2017
There seems to be momentum building around token services, particular
features around:
* Token downgrades. Reducing the scope of an access token when
delegating to a separate less trusted service. For example, you have a
token with admin priveleges and you want to remove those privleges
before re-using the token against another service.
* Token exchanges. Ability to convert a foreign token to and from a
Keycloak one. For example, if you want to trust tokens issued by some
proprietary IBM IDM.
* Trusting tokens from other Keycloak domains. (Although I think this
can fall under token exchanges).
* Token revalidation (I think we have this).
There are some specs around this that Pedro pointed me to:
[1]https://tools.ietf.org/html/draft-richer-oauth-chain-00
[2]https://tools.ietf.org/html/draft-campbell-oauth-sts-01
<https://tools.ietf.org/html/draft-campbell-oauth-sts-01>
I don't think they are either missing things we need or too complex for
our needs.
* Token downgrades, or token redelgation/chaining
I don't want to require apps to know the exact scope they have to
downgrade to if they want to reduce the scope when interacting with
another service. Let's provide an additional extension to [1] and
supply a "client" parameter in which the clientId of the redelegation
you want to perform is used. The token returned would be a union of the
access token's scope and the configured scope of the target client.
* Token exchanges
For [2] Keycloak just doesn't have all the concepts that are spoken
about here. I also don't think the spec is good enough. Coverting
tokens would be handled by a Token Exchange SPI. A provider would be
configured per realm and implemented on top of the ComponentModel SPI.
Each of these provider instances would handle either converting from an
external token to a realm token and/or from a realm token to an external
token. There will also be a rest endpoint on the realm to convert from
external to Keycloak and a separate REST endpoint for converting from
Keycloak to an external token.
From externl to Keycloka:
This would be a form POST to /token/convert-from with these additional
form parameters
"token" - REQUIRED. string rep of the token
"provider" - REQUIRED. id of transformer register in the realm for the
token type
"requested-token-type" - OPTIONAL. "id", "access", "offline", or
"refresh". Default is "access".
"scope" - OPTIONAL. Same as oauth scope.
This operation is analogous to the code to token flow. Here we are
creating a token tailored to the authenticated client. So all scope
configurations and mappers that the client has are applied. This means
that the client must be registered as an OIDC client. The SPI would look
something like this:
interface TokenExchangeFromProvider extends Provider {
Transformer parse(ClientModel client, Map<String, String>
formParameters);
interface Transformer {
UserModel getUser();
IDToken convert(IDToken idToken);
AccessToken convert(AccessToken accessToken);
}
}
The getUser() method returns a user that was authenticated from the
external token. The convert() methods just gives the provider the
flexibility to do further transformations on the returned token.
The runtime would do something like this:
ClientModel authenticatedClient = ...;
ComponentModel model = realm.getComponent(formParams.get("provider"));
TokenExchangeFromProvider provider =
session.getProvider(TokenExchangeFromProvider.class, model);
Transformer transformer = provider.parse(formParams);
UserModel user = transformer.getUser();
if (formParam.get("requested-token-type").equals("access")) {
AccessToken accessToken = generateAccessToken(authenticatedClient,
user, ...);
accessToken = transformer.convert(accessToken).
}
Something similar would be done for converting a Keycloak token to an
external token:
This would be a form POST to /token/convert-to with these additional
form parameters
"token" - REQUIRED. string rep of the token
"provider" - REQUIRED. id of transformer register in the realm for the
token type
interface TokenExchangeToProvider extends Provider {
ResponseBuilder parse(ClientModel client, Map<String, String>
formParameters);
}
Since we're crafting something for an external token system, we give the
provider complete autonomy in crafting the HTTP response to this operation.
More information about the keycloak-dev
mailing list