Repository navigation
API Resource Modeling Principles #38379
Replies: 2 comments 1 reply
|
IMHO, the best APIs are those including hypermedia controls (e.g. HAL Forms). |
|
The API should not be broken down into too many endpoints; for example there should be a single client endpoint, not subendpoints for protocol mappers, etc. Credentials for clients may make sense as a separate endpoint, but I'm not even convinced about this. Breaking it up into many subendpoints results in a lot of extra endpoints to understand, implement, test, etc. You will also end up with having to have a more coarse grained endpoint + the broken down endpoints, as no I don't want to make 10 calls to create or update a client; I want a single endpoint. Essentially we should have as few endpoints as possible (/users, /clients, etc.), and have an absolutely clear need to further break it up only when absolutely necessary. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Table of Contents
Description
The current Realm Admin API is inconsistent. Additionally, some areas of the API only allow coarse-grained management where others are overly fragmented. This initiative aims at improving Keycloak API for better usability and evolvability.
A general discussion about the shortcomings of the current and suggestions for the new Admin API can be found here: #37655
Problem to solve
Define a clear blueprint to determine whether a resource in the new API should be separate, or incorporated in a parent resource. Our goal is to maintain granular updates ("I don't want to update the whole Client resource just to update its Secret") and permission control while minimizing unnecessary resources ("I want to be able to create a Client in a single request"). Striking the right balance is important.
Example: Structure of a concrete Client "aggregate" resource (with grouped resources)

Proposal:
The resources are designed in a granular way rather than monolith and in a strict hierarchical parent-child structure. Each resource can be managed (CRUD) independently but at the same time can be recursively included in the parent resources (this can be controlled via a flag in the request. This allows granular updates but at the same keeps the design declarative config friendly by supporting a monolithic approach ("I want to manage all fields/subresources of a Client in a single request").
When deciding whether to group API resources, the key question is: "Does it make sense in a scenario to manage this resource independently from its parent?" If yes, they should be kept separate.
In the example below, we structured a Client "aggregate" resource using three sub-resources. Users can request a complete representation or fetch each sub-resource individually. This enables fine-grained updates to a selected Client sub-resource but at the same time allows managing a Client as a single resource.
🚀 Idea 1: Control resource representation via query parameter
Based on the structure defined above, we could use a query parameter to control the desired resource handling.
Example: Full Client update (including subresources)
{ "clientId": "myclient", "name": "My Client", "description": "Client for demonstration", "icon": "https://keycloak.org/icon.png", "owner": "admin", "generalSettings": { "termsUrl": "https://keycloak.org/terms", "policyUrls": ["https://keycloak.org/privacy"] }, "authorization": { "roles": [ { "name": "admin", "description": "Administrator role" } ], "policies": [ { "name": "admin-access", "type": "role", "roles": ["admin"] } ], "resources": [ { "name": "dashboard", "description": "Access to dashboard" } ] } }Since includeSubresources=true, this request will update both the client and all its nested subresources in a single operation.
Example: Updating just a subresource
{ "roles": [ { "name": "editor", "description": "Editor role" }, { "name": "viewer", "description": "Read-only access" } ] }This request only updates the roles without modifying other client attributes.
Example: GET Client with all subresources
Response:
{ "clientId": "myclient", "name": "My Client", "description": "Client for demonstration", "icon": "https://keycloak.org/icon.png", "owner": "admin", "generalSettings": { ... }, "protocolSettings": { ... }, "oidcSettings": { ... }, "scopeConfiguration": { ... }, "credentials": { ... }, "authorization": { "roles": [ { "name": "admin", "description": "Administrator role" }, { "name": "editor", "description": "Editor role" } ], "policies": [ { "name": "admin-access", "type": "role", "roles": ["admin"] } ], "resources": [ { "name": "dashboard", "description": "Access to dashboard" } ] } }Because includeSubresources=true is set, this request retrieves the entire client structure, including subresources.
Example: GET Client without subresources
Response:
{ "clientId": "myclient", "name": "My Client", "description": "Client for demonstration", "icon": "https://keycloak.org/icon.png", "owner": "admin" }Returns only the main client object, excluding subresources.
NOTE: The examples contain fields that do not represent the final design of the Admin API v2 and are subject to change.
🎉 Idea 2: Use dedicated resources for import / export and management of (sub-)resources
Key Ideas:
The central resources currently managed via API within a realm are, for example:
Resource Group
A logical grouping of a set of resources that belong together.
Collection Resource
A collection of (Aggregate) resources is called a "collection resource". A collection resource can serve as the entrypoint for listing exist 8000 ing (aggregate) resources. Collection resources might support resource specific search/query/find operations.
For simpler resources, the collection resource might be used for creating new resources. For complex resources that might require complex validation / migration logic or support different source formats (yaml, json, etc.) should use the dedicated import resource provided by the respective collection resouce.
Aggregate Resources
Those "core" resources could be modelled as "aggregate resources" and grouped in a logical "resource group" or "resource domain". Aggregate resources can provide access to fine grained resource management via sub-resources.
We could allow users to manage an aggregate resource in course or fine grained mode.
An aggregate resource could be a concrete client, e.g.
myclientthat encapsulates multiple subresources.The aggregate resource serves as the entry point to manage a concrete resource as a whole.
Users can use this resource to remove the whole resource or update base information.
clients/myclient- "aggregate" / overview resourceAggregate resources can have resource /use-case specific subresources to allow fine-grained management and authorization. Note that the aggregate resource might not allow updating subresources through the aggregate resource. In this case the sub-resources should be used.
For a concrete client aggregate resource, we could have the following sub-resources:
clients/myclient/settings- all (protocol) settingsclients/myclient/credentials- all client settingsclients/myclient/authorization- all authorization settings (policies, etc...)clients/myclient/roles- client role managementclients/myclient/scopes- scope managementQuestions when modeling subresources:
To answer what should be "grouped" in a subresource you should look at the use-cases for managing / updating the resource.
Import resource
To create a new concrete aggregate resource, users could choose to import a complete client configuration (json, yaml) with a dedicated import endpoint. Purpose of the import endpoint on a collection resource like
/clientsis to provide a simple way to create a new aggregate resource in one API call.clients/import- import a client definition as a wholeThe response would then return the (full) representation of the imported resource including the concrete clientUuid and the storage location URI. Note that the import input is not required to contain default parameters. However, the import could indicate the Keycloak version that was used to create the export. This helps the import mechanism to perform potential migrations and select the proper defaults.
Export resource
A user can also request to export a client configuration, with an
/exportendpoint available on the aggregate resource.clients/myclient/export- export client configuration (optionally with defaults, perhaps via query parameters)The
/exportresource allows configuring the inclusion of default settings via query parameter. The output format should be json by default, but could also support other formats such as yaml.All reactions