From 94ef46b29b6f6d400bc4dc07702b931284781b0b Mon Sep 17 00:00:00 2001 From: Erik Cederstrand Date: Tue, 31 May 2022 12:53:49 +0200 Subject: [PATCH] feat: Support Token Exchange. Fixes #305 --- CHANGELOG.md | 1 + README.md | 3 +++ src/keycloak/keycloak_openid.py | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b757b..4916c27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,3 +46,4 @@ All notable changes to this project will be documented in this file. ## [master] * Renamed `KeycloakOpenID.well_know` to `KeycloakOpenID.well_known` + * Add `KeycloakOpenID.token_exchange` to support Token Exchange diff --git a/README.md b/README.md index 85e3d34..d3572f5 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,9 @@ config_well_known = keycloak_openid.well_known() token = keycloak_openid.token("user", "password") token = keycloak_openid.token("user", "password", totp="012345") +# Get token using Token Exchange +token = keycloak_openid.exchange_token(token['access_token'], "my_client", "other_client", "some_user") + # Get Userinfo userinfo = keycloak_openid.userinfo(token['access_token']) diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index e73e963..a1f1f0a 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -254,6 +254,30 @@ class KeycloakOpenID: data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError) + def exchange_token(self, token: str, client_id: str, audience: str, subject: str) -> dict: + """ + Use a token to obtain an entirely different token. See + https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange + + :param token: + :param client_id: + :param audience: + :param subject: + :return: + """ + params_path = {"realm-name": self.realm_name} + payload = { + "grant_type": ["urn:ietf:params:oauth:grant-type:token-exchange"], + "client_id": client_id, + "subject_token": token, + "requested_token_type": "urn:ietf:params:oauth:token-type:refresh_token", + "audience": audience, + "requested_subject": subject, + } + payload = self._add_secret_key(payload) + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), data=payload) + return raise_error_from_response(data_raw, KeycloakGetError) + def userinfo(self, token): """ The userinfo endpoint returns standard claims about the authenticated user,