From 1606b909019942877bd0091799569b1968a28121 Mon Sep 17 00:00:00 2001 From: Tamas Mako <1412056+tomako@users.noreply.github.com> Date: Tue, 17 Dec 2024 21:38:29 +0100 Subject: [PATCH] Support client_credentials grant type --- README.md | 31 ++++++++++++++++++++++++++----- src/flask_multipass_keycloak.py | 14 +++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1eb826f..0a82695 100644 --- a/README.md +++ b/README.md @@ -35,17 +35,38 @@ MULTIPASS_IDENTITY_PROVIDERS = { 'title': 'Keycloak Identity Provider', 'identifier_field': 'email', 'keycloak_args': { - 'client_name': '', + 'grant_type': 'client_credentials', + 'client_id': '', 'client_secret': '', - 'username': , - 'password': , - 'access_token_url': , - 'realm_api_url': + 'access_token_url': '', + 'realm_api_url': '' } } } ``` +The configuration values are following: + +1. `grant_type` + + Default value is `client_credentials`. In Keycloak, "Service accounts roles" must be enabled in client config (Client details/Settings/Capability). + + `password` is also supported. In Keycloak, "Direct access grants" must be enabled in client config (Client details/Settings/Capability). In this case 2 additional fields must be added: `username` and `password`. + +2. `client_id` + + In Keycloak, Client details/Setting/Client ID field. +3. `client_secret` + + In Keycloak, Client details/Credentials/Client Secret field. +4. `access_token_url` + + In Keycloak, Realm settings/General/Endpoints/OpenID Endpoint Configuration/"token_endpoint". +5. `realm_api_url` + + The URL format is `/admin/realms/`, where the realm is where the users and user groups are configured. + + ### Performance The library needs to get an API access token from Keycloak which typically takes 200-300ms. Set the `cache` key of the multipass identity provider configuration to the import path of a Flask-Caching instance or a function returning such an instance, or the instance itself to enable caching of tokens (until they expire) and group data (30 minutes). diff --git a/src/flask_multipass_keycloak.py b/src/flask_multipass_keycloak.py index fac776e..83091ee 100644 --- a/src/flask_multipass_keycloak.py +++ b/src/flask_multipass_keycloak.py @@ -209,12 +209,16 @@ def _get_api_session(self): if api_token: api_session.headers.update({'Authorization': f'Bearer {api_token}'}) return api_session - basic = HTTPBasicAuth(self.keycloak_settings['client_name'], self.keycloak_settings['client_secret']) - data = {'username': self.keycloak_settings['username'], - 'password': self.keycloak_settings['password'], - 'grant_type': 'password'} + data = {'client_id': self.keycloak_settings['client_id'], + 'client_secret': self.keycloak_settings['client_secret'], + 'grant_type': self.keycloak_settings['grant_type']} + # Supported grant types: password and client_credentials + if data['grant_type'] == 'password': + data |= {'username': self.keycloak_settings['username'], + 'password': self.keycloak_settings['password']} + self.logger.info('Requesting access token') - response = api_session.post(self.keycloak_settings['access_token_url'], auth=basic, data=data) + response = api_session.post(self.keycloak_settings['access_token_url'], data=data) if response.status_code != 200: error_message = self._get_error_message(response) self.logger.error(f'{error_message} (URL: %s)', response.url)