diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7ffbd10d..2b8cc9541 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Changed
 - Fixed Azure AD Tenant authentication with custom signing keys
 - Added CAS OIDC backend
+- Made Keycloak `ID_KEY` configurable
 
 ## [4.4.1](https://github.com/python-social-auth/social-core/releases/tag/4.4.1) - 2023-03-30
 
diff --git a/social_core/backends/keycloak.py b/social_core/backends/keycloak.py
index 494ddabdb..d5d2751f8 100644
--- a/social_core/backends/keycloak.py
+++ b/social_core/backends/keycloak.py
@@ -96,7 +96,6 @@ class KeycloakOAuth2(BaseOAuth2):  # pylint: disable=abstract-method
     """
 
     name = "keycloak"
-    ID_KEY = "username"
     ACCESS_TOKEN_METHOD = "POST"
     REDIRECT_STATE = False
 
@@ -121,6 +120,9 @@ def public_key(self):
             ]
         )
 
+    def id_key(self):
+        return self.setting("ID_KEY", default="username")
+
     def user_data(
         self, access_token, *args, **kwargs
     ):  # pylint: disable=unused-argument
@@ -149,5 +151,11 @@ def get_user_details(self, response):
         }
 
     def get_user_id(self, details, response):
-        """Get and associate Django User by the field indicated by ID_KEY"""
-        return details.get(self.ID_KEY)
+        """Get and associate Django User by the field indicated by ID_KEY
+
+        The ID_KEY can be any field in the user details or the access token.
+        """
+        id_key = self.id_key()
+        if id_key in details:
+            return details[id_key]
+        return response.get(id_key)