Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programatic username/password access with KeyCloak using external IDP brokering

I'm using Identity Brokering feature and external IDP. So, user logs in into external IDP UI, then KeyCloak broker client receives JWT token from external IDP and KeyCloak provides JWT with which we access the resources. I've set up Default Identitiy Provider feature, so external IDP login screen is displayed to the user on login. That means that users and their passwords are stored on external IDP.

The problem occurs when I need to log in using "Direct Access Grant" (Resource Owner Password grant) programatically in tests. As password is not stored on KeyCloak, I always get 401 Unauthorized error from KeyCloak on login. When I tried to change user password it started to work, so the problem is that user password is not provisioned on KeyCloak and using "Direct Access Grant" KeyCloak doesn't invoke external IDP on programatic login.

I use the following code to obtain access token, but get 401 error everytime I pass valid username/password.

org.keycloak.authorization.client.util.HttpResponseException: Unexpected response from server: 401 / Unauthorized

Direct access grant is enabled for that client.

public static String login(final Configuration configuration) {
    final AuthzClient authzClient = AuthzClient.create(configuration);
    final AccessTokenResponse accessTokenResponse = authzClient.obtainAccessToken(USERNAME, PASSWORD);
    return accessTokenResponse.getToken();
  }

Is there any way it can be fixed? For example to call identity broker on "Direct Access Grant", so that KeyCloak provides us it's valid token?

like image 688
Yuriy Yunikov Avatar asked Nov 29 '17 16:11

Yuriy Yunikov


2 Answers

The problem was that KeyCloak has no information about passwords from initial identity provider. They have a token exchange feature which should be used for programmatic token exchange.

External Token to Interanal Token Exchange should be used to achieve it.

Here is an example code in Python which does it (just place correct values in placeholders):

def login():
    idp_access_token = idp_login()
    return keycloak_token_exchange(idp_access_token)

def idp_login():
    login_data = {
        "client_id": <IDP-CLIENT-ID>,
        "client_secret": <IDP-CLIENT-SECRET>,
        "grant_type": <IDP-PASSWORD-GRANT-TYPE>,
        "username": <USERNAME>,
        "password": <PASSWORD>,
        "scope": "openid",
        "realm": "Username-Password-Authentication"
    }
    login_headers = {
        "Content-Type": "application/json"
    }
    token_response = requests.post(<IDP-URL>, headers=login_headers, data=json.dumps(login_data))
    return parse_response(token_response)['access_token']

def keycloak_token_exchange(idp_access_token):
    token_exchange_url = <KEYCLOAK-SERVER-URL> + '/realms/master/protocol/openid-connect/token'
    data = {
        'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
        'subject_token': idp_access_token,
        'subject_issuer': <IDP-PROVIDER-ALIAS>,
        'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token',
        'audience': <KEYCLOAK-CLIENT-ID>
    }
    response = requests.post(token_exchange_url, data=data,
                             auth=(<KEYCLOAK-CLIENT-ID>, <KEYCLOAK-CLIENT-SECRET>))
    logger.info(response)
    return parse_response(response)['access_token']
like image 150
Yuriy Yunikov Avatar answered Nov 06 '22 22:11

Yuriy Yunikov


This example was very useful for me, i only want to add more info about the KEYCLOAK-CLIENT used for the token-exchange (for me authorization_client). I have KEYCLOAK as a broker for IDP ADFS.

  1. First you need to enable the token-exchange feature adding 2 parameters in your keycloak startup command line (depending of how you do it) -Dkeycloak.profile=preview -Dkeycloak.profile.feature.token_exchange=enabled The IDP (for me ADFS) tab Permissions with the token-exchange permission will be available.
  2. Add a policy to the "token-exchange" provider permission, to the client KEYCLOAK-CLIENT
  3. Add this previous policy to the "token-exchange" client permission

With POSTMAN you can test the authentication flow:

External IDP ADFS Login Username/password Token Exchange

like image 38
Lorenzo Gardoni Avatar answered Nov 06 '22 21:11

Lorenzo Gardoni