IBM Verify

IBM Verify

Join this online user group to communicate across Security product users and IBM experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only

OAuth: Dynamic Client Registration

By Leo Farrell posted Mon June 18, 2018 12:00 AM

  
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.

OAuth: Dynamic Client Registration, Image 1

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_urisproperty 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

usc

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.

OAuth: Dynamic Client Registration, Image 2

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

OAuth: Dynamic Client Registration, Image 3


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.

OAuth: Dynamic Client Registration, Image 4

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


OAuth: Dynamic Client Registration, Image 5

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.

OAuth: Dynamic Client Registration, Image 6



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:


OAuth: Dynamic Client Registration, Image 7

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
0 comments
28 views

Permalink