When hosting services via API or propagating identities to relying parties, OAuth and OpenID Connect are an essential way of granting authentication and authorization to a consumer, on behalf of a user. Depending on the size of this provider, the number of consumers may be huge, so much so that it is not feasible for a single administrator to manage the application details and client credentials of the consuming third parties. In this instance, it becomes essential to provide a method for an application owner to be able to self service their application registration. There are also cases where headless devices such as payment terminals, smart devices, or other embedded systems need to acquire a set of client credentials in order to request authorization to APIs. In both of these scenarios, an API which allows for programmatic registration is the logical solution to this issue.
New in ISAM 9.0.5.0 is support for dynamic client registration, an Open ID Connect Foundation specification. This allows an application or device to make a request to register as an OAuth client.
This document will go over some of the things which can be done using the new capabilities:
- Configuration
- Runtime Operations
- Managing clients via the LMI
- User self care of clients
- API Protection Client changes
- Consent Enhancements
- Differences between client types
Configuration
When configuring an API protection definition there are now two new configuration options. These are available under the OpenID Connect pane of the definition.

The two values are:
Enable Client Registration – Should this definition allow dynamic registration requests.
Issue Secret – If dynamic client registration is enabled, should a client secret be issued to those clients
There is also an additional option on the reverse proxy OAuth configuration API, which alters which ACL is attached to the client registration endpoint. By default only authenticated access is permitted.
When an API protection definition is configured to allow dynamic client registration, it is highly recommended that consent also be required. This recommendation stems from the fact that clients have not been registered by a trusted administrative user, therefore it is more likely that the trust relationship between the OpenID Connect Provider and the Relying Party is not so strong.
Actions to Perform in Mapping Rules
In mapping rules a custom client id or client secret may be set during dynamic client registration requests. There is a code snippet in the default pre-token mapping rule which demonstrates this. The client_access_token
may also be set to a custom value in the same manner as custom access tokens may be set in other flows.
Additionally in both the pre-token and post-token mapping rules the javascript context will now contain the variable oauth_client
, which is of the same type currently returned from OAuthMappingExtUtils.getClient(String clientId)
. This variable may be accessed to retrieve any registered attribute of the client, for example:
var client_id = oauth_client.getClientId();
var client_secret = oauth_client.getClientSecret();
Note: API specifications may be found via the LMI under file downloads -> access_control -> doc -> ISAM-javadoc.zip in the file downloads section of an appliance. Look for the class com.tivoli.am.fim.trustserver.sts.oauth20.Client
Runtime Operations
There is a new endpoint available at runtime to register dynamic clients. This endpoint is advertised in OpenID Connect metadata as shown:
{
...
"registration_endpoint":"https://appliance.ibm.com/mga/sps/oauth/oauth20/register/{definitionName}",
...
}
Note: ‘{definitionName}’ will be the name of your API protection definition.
The registration endpoint accepts JSON, as described in the dynamic client specification, Only the redirect_uris
property is required, all others are optional. The specification defines a set of well-known/recommended properties, however additional user-defined properties are permitted. Additionally properties typically provided for ISAM registered clients may be submitted as part of dynamic client registration, including:
- jwks_uri – the JWKs uri to use for the client.
- company_name – The company name of this client.
- company_url – company URL.
- contact_person – Name of the contact person for this client.
- email_address – Contact email for the client.
- phone_number – Contact number for this client.
- contact_type – The role of this contact. Should be one of:
TECHNICAL, SUPPORT, ADMINISTRATIVE, BILLING, OTHER
- isPkce – Whether or not this client wishes to enforce RFCE 7636.
Making registration request
The following is an example payload to the registration endpoint:
{
"redirect_uris": [ "https://application.ibm.com/redirect"],
"tos_uri":"https://application.ibm.com/tos",
"company_name":"Generic Corp.",
"client_name":"Generic Corp Dynamic client"
}
The request must be made using the Accept: application/json
header.
The response will include all of the provided values, as well as the following:
- Client ID
- Client Access Token
- Client Resource URI
- Optionally – Client Secret
Here is an example request made with cURL:
curl -H 'Accept: application/json' \
-H 'Content-type: application/json' \
-d '{ "redirect_uris": [ "https:\/\/application.ibm.com\/register"], "tos_uri":"https://application.ibm.com\/tos", "company_name":"Generic Corp ", "client_name":"A dynamic client #1" }' \
https://appliance.ibm.com/mga/sps/oauth/oauth20/register/{definitionName}
HTTP/1.1 200 OK
Pragma: no-cache
Content-Type: application/json
{
"client_secret_expires_at":0,
"company_name":"Generic Corp",
"registration_client_uri":"https:\/\/application.ibm.com\/mga\/sps\/oauth\/oauth20\/register\/{definitionName}?client_id=TbV2vjE4Iln7YD48lAe0",
"client_secret":"vKSOfz1noK9gRmg5DK0Q",
"tos_uri":"https:\/\/application.ibm.com\/tos",
"client_id_issued_at":1524629217,
"redirect_uris":"https:\/\/application.ibm.com\/redirect",
"client_name":"A dynamic client #1",
"registration_access_token":"gaaccgdgcdceafggbdc",
"client_id":"TbV2vjE4Iln7YD48lAe0"
}
Note: Definition name will need to be the name of the API protection definition
The returned client credentials may now be used for API Protection flows and Browser SSO.
Common registration errors
HTTP Code |
Reason |
404 |
The definitionName in the request does not exist |
401 |
The definition specified does not allow dynamic registration |
406 |
The headerAccept: application/json was not present in the request |
Managing Dynamic Clients via the LMI
Dynamically registered clients may be managed by an administrative user using management APIs:
- Viewing a dynamic client by
client_id
- Deleting a dynamic client by
client_id
- Listing dynamic clients (includes pagination / filtering)
The dynamic clients management API endpoint is: https://lmi.appliance.ibm.com/iam/access/v8/dynamic_clients
Viewing a dynamic client by client_id
Requests include the client_id
in the url. On success the response includes all attributes of the client
curl --user admin:admin -H 'Accept: application/json' \
https://lmi.appliance.ibm.com/iam/access/v8/dynamic_clients/jyPcBg01OgIsyz5k5mVI
HTTP/1.1 200 OK
Pragma: no-cache
Content-Type: application/json
{
"owner": "testuser",
"clientId": "jyPcBg01OgIsyz5k5mVI",
"definitionId": 1,
"data": {
"company_name": "Generic Corp",
"registration_client_uri": "https://appliance.ibm.com/mga/sps/oauth/oauth20/register/testDef?client_id=jyPcBg01OgIsyz5k5mVI",
"tos_uri": "https://application.ibm.com",
"redirect_uris": [
"https://application.ibm.com/redirect"
],
"client_id_issued_at": 1524629326,
"client_secret": "1ZMzztR2bOgsW0jfoeob",
"client_name": "A dynamic client 19715"
}
}
Deleting a Dynamic Client by client_id
curl --user admin:admin -H 'Accept: application/json' -X DELETE https://lmi.appliance.ibm.com/iam/access/v8/dynamic_clients/jyPcBg01OgIsyz5k5mVI
HTTP/1.1 204 No Content
Note: There are no pending changes when deleting dynamic clients.
Listing Dynamic Clients
When retrieving a list of dynamic clients, a filter can be provided, to filter on the owning user or definition. Here is an example of listing all including a filter on the owner of the client:
curl --user admin:admin -H 'Accept: application/json' https://lmi.appliance.ibm.com/iam/access/v8/dynamic_clients?filter=owner%20equals%20testuser
HTTP/1.1 200 OK
Pragma: no-cache
Content-Type: application/json
[
{
"owner": "testuser",
"clientId": "TbV2vjE4Iln7YD48lAe0",
"definitionId": 1,
"data": {
"company_name": "Generic Corp",
"registration_client_uri": "https://appliance.ibm.com/mga/sps/oauth/oauth20/register/testDef?client_id=TbV2vjE4Iln7YD48lAe0",
"tos_uri": "https://application.ibm.com",
"redirect_uris": [
"https://application.ibm.com/redirect"
],
"client_id_issued_at": 1524629217,
"client_secret": "vKSOfz1noK9gRmg5DK0Q",
"client_name": "A dynamic client #1"
}
},
...
]
User Self-Care of Alients
A user may manage their dynamic clients from the user self care page:https://appliance.ibm.com/mga/sps/mga/user/mgmt/html/device/device_selection.html

From this page, the user now has the option to:
- Register a new dynamic client
- View an existing dynamic client
- Delete a dynamic client
Runtime operations use two API endpoints:
https://appliance.ibm.com/mga/sps/mga/user/mgmt/clients
– this will return a list of the client definitions owned the currently authenticated user.
- The
registration_client_uri
property of a client – this URI is unique to each client and is returned when the client is registered. Requests to this URI require authentication as either the client itself, (for example by using the client_access_token issued when the client is created), or as the client owner.
Creating a Dynamic Client
The following form is presented to create a dynamic client. Whilst it may be as is, typically you would want to customise it. Based on the configured API protection definition name(s) and the properties you wish to collect during client registration.

The template page for this page is under the path mga/user/mgmt/create_client.html

Viewing a Dynamic Client
Viewing a client will displays the owner, definition and clientId, as well as any other registered attributes. The registration_client_uri
is used to retrieve these values.

This is a templated page, and may be modified under the path mga/user/mgmt/client.html

API Protection Client Changes
When administratively registering or modifying regular API protection clients there is now a new JSON field which allows storing of arbitrary data associated with a client. This may include values typically presented when registering a dynamic client, for example the tos_uri
. Look for the new “Extension Properties” tab in the create an API Protection Client window.
This is similar in intended purpose to the otherInfo
field, however it does not have the same length restriction (of 2048 characters) and is explicitly validated as JSON Data. The primary motivator for adding this new property to client configuration is so that the customisation of the consent template page can more easily be made consistent for both dynamic clients and regular clients.

An example of programmatically registering a client with extension data defined:
curl --user admin:admin \
-H 'Accept: application/json' \
-H 'Content-type: application/json' \
-d '{ "clientId": "clientId",\
"name": "Example Client",\
"redirectUri": ["https://application.ibm.com/redirect"],\
"companyName": "Generic Corp",\
"companyUrl": "https://application.ibm.com/",\
"contactPerson": "",\
"contactType": "ADMINISTRATIVE",\
"email": "api@application.ibm.com",\
"phone": "555123",\
"otherInfo": "",\
"definition": "1", \
"extProperties": {"tos_uri": "https://application.ibm.com/tos"}}' \
https://lmi.appliance.ibm.com/iam/access/v8/clients
HTTP/1.1 201 Created
Location: https://lmi.appliance.ibm.com/iam/access/v8/clients/3
This extension data may be accessed from the consent page, as well as from a Client objects in javascript mapping rules. For example in the oauth mapping rules you may use the call oauth_client.getExtendedData()
to get the extra data as a string, and use the inbuilt javascript method JSON.parse()
to get a native object that is easily accessed. This pattern of accessing the extension data works for both dynamic and regular clients.
Consent Enhancements
It is common for properties of a dynamically registered client (and for that matter a regular client) to be presented to the resource owner as part of consent. This includes for examples properties like the logo_uri
and tos_uri
.
In order for a client registration property to appear on the consent page it must be included in the whitelist of keys in the advanced configuration property oauth20.clientDataToInclude
. The default value for this property includes all static data fields defined by ISAM and well-known fields defined in the dynamic client registration specification:
ISAM data fields: contact_type, email_address, contact_person, company_name, company_url, phone_number, other_info
.
Dynamic client registration specification data fields: application_type, client_name, logo_uri, client_uri, policy_uri, tos_uri
.
Values for the white listed attributes will be included in the macro @OAUTH_CLIENT_DATA_MACRO@
in the user_consent.html
template file. From here template page scripting may be used to modify the template using these values. This permits additional selected information to be shown on the consent page. For example:

In addition to the oauth20.clientDataToInclude
the user_consent.html
template should be updated. By default these values are not exposed (so as to not unintentionally leak client information). The following code block should be updated if these values are to be used in the consent page. The sample code below shows how the values may first be accessed via server-side template page scripting (inside the <% %>
tags) and then transferred as a variable to client-side javascript. You would typically use server-side template page scripting to filter out only the attributes you wish to share and transfer that subset to the client via javascript variables:
<script type="text/javascript">
<%
// The macro '@OAUTH_CLIENT_DATA_MACRO@' will be the dynamic data of the client.
// This will include any statically configured client values such as 'company_name',
// as well as any dynamic values whether they be from a dynamically registered
// client, or from a extended client portion, for example 'tos_uri'.
//
// These fields are sanitised by default, change the advanced configuration
// 'oauth20.clientDataToInclude' to add or remove values as necessary.
//
// Take note this block is in a server side scripting tag, by default we send an empty object.
// If this data is to be shared with the browser, remove the currently assigned value and use
// the commented out snippet.
var to_browser = "{}" //templateContext.macros["@OAUTH_CLIENT_DATA_MACRO@"];
%>
var dynamic_client_data = <%=to_browser + ";"%>
</script>
Differences Between Client Types
There are now two types of clients which may exist on an ISAM appliance, the following tables identify some of the limitations and differences between the two. Both clients identical functionally at runtime.
|
Regular Client |
Dynamic Client |
Client is persisted in: |
Configuration database |
Runtime database |
Client can be updated: |
Yes – Support for updates through both LMI and javascript API |
No – Delete and re-register the client |
Extended client properties supported: |
Yes |
Yes |
Client management through developer portal: |
Yes – Javascript APIs are exposed (Cannot be done under docker due to no configDB replication) |
Yes – update page templates to suit user experience |
Lookup at runtime via JavaScript |
Yes, using OAuthMappingExtUtils.getClient(String clientId) |
Partial – Only in the OAuth pre/post token rules via the populated oauth_client variable. Not possible in InfoMap or STS-Map rules |
Lookup at runtime via REST |
No |
The client_registration_uri may be used to retrieve the client.
(Note: Authorization requirements are encforced, must own the client, or be authenticated as the client)
|
Lookup via the LMI |
Use https://lmi.appliance.com/iam/access/v8/clients |
Use https://lmi.appliance.com/iam/access/v8/dynamic_clients |
Impact on runtime when performing management operations:
(Note: Clients are cached, entries will live for the configured lifetime.
|
Deployment of changes when done via LMI, re-deploy of snapshot under docker. Runtime javascript API changes are insant |
Immediate, no pending changes or snapshot publishing needed. |
Note: When using dynamic clients, the definition must be configured with OpenID Connect enabled.
Note: When using static clients, a developer portal cannot be created under docker.
FAQ:
If unauthenticated client registration is allowed, is it possible to assign them to a default user?
Yes, use the following snippet to set the principle during register requests in the pre-token mapping rule:
if(request_type == "client_register") {
var username = stsuu.getPrincipalName();
if(username == null) {
stsuu.setPrincipalName("default_client_owner");
}
}
How can I restrict access to registering clients to a specific user or group membership?
Check the principal username or groups of the STSUniversalUser(stsuu)
, the following can be used to raise a 401 with a message:
OAuthMappingExtUtils.throwSTSAccessDeniedMessageException("DENIED");
#ISAM