[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:


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> 

     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> 

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