Problem
All async Admin API methods (a_get_sessions, a_get_credentials, a_get_user, etc.) return bare list or dict without type parameters. Consumers must manually index into untyped dicts with string keys, losing all static analysis guarantees.
# Current — untyped, no IDE support, no static verification
sessions: list = await admin.a_get_sessions(user_id)
session_id = sessions[0].get("id") # Any
ip = sessions[0].get("ipAddress") # Any
start = sessions[0].get("start") # Any — is this seconds or milliseconds?
clients = sessions[0].get("clients") # Any — is this a list or dict?
Every consumer must read the Keycloak REST API docs to know:
- Which keys exist
- What types the values are (
start is epoch milliseconds, not seconds)
- Which fields are nullable
- What the
clients structure is (dict[str, str], not list)
This is error-prone and defeats the purpose of using a typed Python client.
What we're asking for
Return Pydantic models (or dataclasses/TypedDicts) from Admin API methods. Example for a_get_sessions:
class UserSessionRepresentation(BaseModel):
"""Keycloak UserSessionRepresentation.
See: https://www.keycloak.org/docs-api/latest/rest-api/index.html#_usersessionrepresentation
"""
id: str
username: str
userId: str
ipAddress: str | None = None
start: int # Epoch milliseconds
lastAccess: int # Epoch milliseconds
rememberMe: bool = False
clients: dict[str, str] = Field(default_factory=dict)
transientUser: bool = False
# After — typed, IDE autocomplete, static verification
async def a_get_sessions(self, user_id: str) -> list[UserSessionRepresentation]:
...
Concrete example: a_get_sessions
The Keycloak REST API spec defines UserSessionRepresentation with 9 fields:
| Field |
Type |
Notes |
id |
String |
Session UUID |
username |
String |
|
userId |
String |
User UUID |
ipAddress |
String |
Nullable |
start |
long |
Epoch milliseconds |
lastAccess |
long |
Epoch milliseconds |
rememberMe |
boolean |
|
clients |
Map<String, String> |
Client ID to client name |
transientUser |
boolean |
|
Today a_get_sessions returns list (bare, no type param). The return annotation says -> list — not list[dict[str, Any]], not list[UserSessionRepresentation], just list.
Scope
This applies to all async methods that return Keycloak representations:
a_get_sessions → list[UserSessionRepresentation]
a_get_credentials → list[CredentialRepresentation]
a_get_user → UserRepresentation
a_get_users → list[UserRepresentation]
a_get_realm_roles → list[RoleRepresentation]
- etc.
Keycloak's REST API spec already defines all these representations. The Java source at core/src/main/java/org/keycloak/representations/idm/ has the canonical field definitions.
Benefits
- IDE autocomplete —
session.ipAddress instead of guessing session["ipAddress"] vs session["ip_address"]
- Static type checking — mypy/pyright catch typos and type mismatches at development time
- Self-documenting — no need to cross-reference Keycloak REST API docs for every field
- Refactoring safety — if Keycloak renames a field, type checkers flag all consumers
Implementation approach
The representations could be Pydantic models, dataclasses, or TypedDicts — whatever fits the library's style. The key requirement is that return types are specific (not list or dict), with field names and types matching the Keycloak REST API spec.
A backward-compatible approach would be to have the models extend dict (or use TypedDict) so existing session["id"] code continues to work while also supporting session.id.
Environment
- python-keycloak (tested on 5.x, applies to all versions)
- Python 3.12+
- Keycloak 26.x
Problem
All async Admin API methods (
a_get_sessions,a_get_credentials,a_get_user, etc.) return barelistordictwithout type parameters. Consumers must manually index into untyped dicts with string keys, losing all static analysis guarantees.Every consumer must read the Keycloak REST API docs to know:
startis epoch milliseconds, not seconds)clientsstructure is (dict[str, str], notlist)This is error-prone and defeats the purpose of using a typed Python client.
What we're asking for
Return Pydantic models (or dataclasses/TypedDicts) from Admin API methods. Example for
a_get_sessions:Concrete example:
a_get_sessionsThe Keycloak REST API spec defines
UserSessionRepresentationwith 9 fields:idusernameuserIdipAddressstartlastAccessrememberMeclientstransientUserToday
a_get_sessionsreturnslist(bare, no type param). The return annotation says-> list— notlist[dict[str, Any]], notlist[UserSessionRepresentation], justlist.Scope
This applies to all async methods that return Keycloak representations:
a_get_sessions→list[UserSessionRepresentation]a_get_credentials→list[CredentialRepresentation]a_get_user→UserRepresentationa_get_users→list[UserRepresentation]a_get_realm_roles→list[RoleRepresentation]Keycloak's REST API spec already defines all these representations. The Java source at
core/src/main/java/org/keycloak/representations/idm/has the canonical field definitions.Benefits
session.ipAddressinstead of guessingsession["ipAddress"]vssession["ip_address"]Implementation approach
The representations could be Pydantic models, dataclasses, or TypedDicts — whatever fits the library's style. The key requirement is that return types are specific (not
listordict), with field names and types matching the Keycloak REST API spec.A backward-compatible approach would be to have the models extend
dict(or useTypedDict) so existingsession["id"]code continues to work while also supportingsession.id.Environment