How to register and manage OAuth2 clients?

Source — https://depositphotos.com/247966288/stock-photo-high-angle-view-diverse-business.html

In the first post of this blog series about the OAuth2, I provided a comprehensive overview of the OAuth2 core specification and its supporting specification. In the 2nd post, I discussed the OAuth2 Authorization Server Metadata specification. In this post, I will discuss the OAuth2 Dynamic Client Registration (DCR) and the OAuth2 Dynamic Client Registration Management (DCR-M) protocols that play and vital role in the practical use of OAuth2.

Having a basic understanding of the Authorization Code grant type is a prerequisite for this current post, and you refer to my OAuth2 overview post from here.

As you know, the Authorization Code grant type consists of two distinct steps and let’s pay attention to the following requirements of each step.

Step 1 (Authorization code request ) — in this step, when the client is making the authorization request, it should pass the client_id to the AS for the client identification.

Step 2 (Access token request) — In this step, when the client is making the authorization request, it should send both client_id and client_secret so that the AS can authenticate the client.

Well, this post’s base problem is how a client obtains this client_id and client_secret in the first place? Because without these, the client can’t be involved in any successful communication with the AS.

To give you a high-level answer, there are two mechanisms available:

Regardless of whatever approach is used, it’s required to provide some mandatory information to the AS during the client registration; the most important values are Application Name and Callback URL.

Client registration via out-of-band channels

In this approach, there is a web portal maintained by the OAuth2 provider. There is a web portal available with the AS so that the client developers can logged-in and register an application themselves. Once the registration is completed, the client developer is provided with the client_id and client_secret. There is no standard around this approach; however, most of these web portals are self-explanatory and do not require any prior knowledge.

Here is an example: OAuth2 application page from GitHub.

his approach provides a great user experience for humans, but lack of standard makes it hard for a non-human software agent to deal with the AS; that is the main downside of this approach. One possible solution would be to develop a simple RESTful API so that software agents can easily interact with the AS, and that is what exactly has achieved in DCR and DCR-M protocols.

The following are the two examples of client registration portals that are available with the ASs.

OAuth2 app registration portal of WSO2 Identity Server
OAuth2 app registration portal of keycloak

OAuth2 DCR and DCR-M specifications

Both OAuth2 Dynamic Client Registration (OAuth2 DCR ) and OAuth2 Dynamic Client Registration Management (OAuth2 DCR-M) are generalizations of OpenID Connect Dynamic Client Registration (OIDC DCR) protocol to cover a wider scope than the OpenID Connect protocol. In other words, OIDC DCR was first developed, and then OAuth2 DCR and DCRM were developed by generalizing OIDC DCR protocol. The following diagram depicts the relationship between these protocols.

OAuth2 Dynamic Client Registration

The objective of the OAuth2 DCR protocol is to define a mechanism for dynamically registering OAuth2 Client with authorization servers (ASs). The OAuth2 DCR defines:

The message format can take two forms: a plain JSON format or singed JWT format, which also known as Software Statement. In the case of singed JWT, the signing party, also known as the issuer, is vouching for the data’s validity. Either the client development organization itself or an authorized 3rd party entity can issue a Software Statement. Use of Software Statement for OAuth2 DCR is becoming popular in some industries such as Open Banking.

The client registration endpoint is a RESTful endpoint that should be available on ASs so that client can register themselves with the AS. The registration endpoint should be a protected endpoint using some kind of a security mechanism. Initial access tokens and API tokens are some popular options; however, this initial credential’s distribution and format are not defined in the OAuth2 DCR specification.

The following are some of the widely-used JSON values used in both request and response messages; you can find the complete list of these attributes here.

client_id — This is a unique identifier that can be used to identify the client within the AS scope. Generally, the AS generate client_id and included in the response message. However, some OAuth2 services accept client provided client_id as well. The client_id is a required value in the response message. In simple words, without this identifier, the client can’t perform any communication with the AS.

client_secret — Like client_id, this is usually generated by the AS and communicates back to the client. The purpose of client_secret is to authenticate the client when necessary when this present along with the client_id. For example, the token request of the authorization code grant type uses client authentication.

redirect_uris — This is one of the most important value that the client should provide within the registration request. The AS use this URL to send access tokens and authorization grants. For instance, in the authorization code grant type, this client endpoint location is used to send both ‘code’ and access token to the client.

grant_types — supported grant types of clients such as authorization_code, client_credentials etc.

response_types — supported response types of clients such as code, token etc.

client_name — A human-readable client name.

Extending protocols such as OpenId Connect Dynamic Client registration protocol and the protocol implementers can also define and use additional parameters.

Dynamic Client Registration Flow

Dynamic Client Registration Flow

This is a very simple flow that consists of just two steps; but, I have made it into 3 steps so that you can understand the flow easily.

Step 0 — The client should have some sort of initial credential to access the registration EP; this could be an initial access token, API key etc. Any out-of-band channel can be used to distribute this initial credential.

Step 1 — The client sends the client’s configuration data within the registration request message. As we already discussed, this can be either plain JSON or a signed JWT message. An HTTP POST should be used to send this message to the AS’s registration EP over HTTPS.

Here is a sample request message.

POST /register HTTP/1.1Content-Type: application/jsonAccept: application/jsonHost: server.example.com{“redirect_uris”: [      “https://client.example.org/callback",       “https://client.example.org/callback2"],“client_name”: “My Example Client”,“token_endpoint_auth_method”: “client_secret_basic”,“logo_uri”: “https://client.example.org/logo.png",“jwks_uri”: “https://client.example.org/my_public_keys.jwks",“example_extension_parameter”: “example_value”}

Step 2 — Once the AS validated and registered by the client, the AS reply back to the client with registered client data as a JSON payload; this is known as the client registration response message. Generally, the AS generates client_id & client_secret and are included in the registration response message. However, some AS vendors accepts client provided client_id & client_secret as well; in such cases, the client registration response message can be used to send the client_id & client_secret.

Here is a sample response message.

HTTP/1.1 201 Created
Content-Type: application/json
{“client_id”: “s6BhdRkqt3”,“client_secret”: “cf136dc3c1fc93f31185e5885805d”,“client_id_issued_at”: 2893256800,“client_secret_expires_at”: 2893276800,“redirect_uris”: [ “https://client.example.org/callback", “https://client.example.org/callback2"],“grant_types”: [“authorization_code”, “refresh_token”],“client_name”: “My Example Client”,“token_endpoint_auth_method”: “client_secret_basic”,“example_extension_parameter”: “example_value”}

OAuth2 Dynamic Client Registration Management

As you probably have noticed, the OAuth2 DCR protocol only facilitates registering OAuth2 client applications with the AS. It does not facilitate updating, deleting or reading OAuth2 applications. The purpose of the OAuth2 DCRM protocol is to fill this gap by defining a mechanism to read, update and delete OAuth2 client dynamically.

Dynamic Client Registration Management Flows

As shown in the above diagram, OAuth2 DCRM defines an endpoint called Client Configuration Endpoint. Still, according to REST best practice, the registration endpoint that is defined in OAuth2 DCR protocol is used as the Client Configuration Endpoint. The Client Configuration Endpoint is secured with a special token known as Registration Access Token, which is issued by the client registration endpoint during the client registration. However, in practice, Registration Access Token is not widely used; instead of initial access tokens, API tokens are pretty common.

DCR defines three main flows. However, all these flows assumed that the client has already registered with the OAuth2 AS via OAuth2 DCR protocol or some other means.

registration_access_token — This can be used with subsequent flows such as reading, updating and deleting the client.

registration_client_uri — An URI pointing to the client configuration endpoint.

However, some implementors tend to have some relaxation on registration_access_token and registration_client_uri and more focus on the RESTfull functionality of the protocol.

Here is a sample response message.

{
“registration_access_token”: “reg-23410913-abewfq.123483”,
“registration_client_uri”:
“https://server.example.com/register/s6BhdRkqt3",
“client_id”: “s6BhdRkqt3”, “client_secret”: “cf136dc3c1fc93f31185e5885805d”, “client_id_issued_at”: 2893256800, “client_secret_expires_at”: 2893276800, “client_name”: “My Example Client”, “redirect_uris”: [ “https://client.example.org/callback"], “grant_types”: [“authorization_code”, “refresh_token”], “token_endpoint_auth_method”: “client_secret_basic”}

2. ) An OAuth2 client can make an HTTP GET request to the Configuration EP of the AS with the client_id to receive registered client data from the AS as a JSON document.

3.) An OAuth2 client can make an HTTP PUT request to the Configuration EP of the AS with the client_id and up-to-date client details. The AS should update the client registration data and replay the client with the client’s updated data. This response also takes JSON format.

4.) An OAuth2 client can make an HTTP DELETE request to the Configuration EP of the AS with the client_id to delete the client data kept in the AS. After removing the client data, the AS should reply with HTTP 204 status code.

As we have covered many theoretical details about these protocols, let’s try to discuss some practical examples using two DCR and DCR-M implementations.

DCR and DCR-M on WSO2 IS

First, let’s run an instance of the WSO2 Identity Server (IS).

docker run — rm -it -p 9443:9443 — name is wso2/wso2is:5.10.0

You can reach the registration endpoint of the WSO2 IS via the following URL. Also, note that WSO2 IS expects a prior registered user account for authentication of these requests by default. For simplicity, we use the admin: admin account for this demonstration.

https://localhost:9443/api/identity/oauth2/dcr/v1.1/register

Now, let’s see how we could register, read, update and delete an OAuth2 client application dynamically.

You can register a client with the following cURL command.

curl -k — location — request POST ‘https://localhost:9443/api/identity/oauth2/dcr/v1.1/register' \ 
— header ‘Content-Type: application/json’ \
user ‘admin:admin’ \
— data-raw ‘{ “client_name”: “simple-application”, “redirect_uris”: [“server.example.com/callback”], “grant_types”: [“authorization_code”]}’

Here is the expected response message; you can use the server-generated client_id and client_secret in the response message.

{ “client_id”: “Ak32fw4v16Uv_5FNfv9fg2n7Ei8a”, “client_secret”: “MTZrCpVvaP_VpjZE6uFYQuxXH88a”, “client_secret_expires_at”: null, “redirect_uris”: [“server.example.com/callback”], “client_name”: “simple-application”}

Alternatively, you can provide your own client_id and client_secret with the request message. In that case, WSO2 IS uses ext_param_client_id and ext_param_client_secret extension parameters with the request message. You can use the following cURL command.

curl -k — location — request POST ‘https://localhost:9443/api/identity/oauth2/dcr/v1.1/register' \ 
— header ‘Content-Type: application/json’ \
user ‘admin:admin’ \
— data-raw ‘{“client_name”: “provided-application”, “grant_types”: [“authorization_code”], “redirect_uris”: [“server.example.com/callback”], “ext_param_client_id”:”provided_client_id0001", “ext_param_client_secret”:”provided_client_secret0001"}

2.) Read Client configuration

You can use one of the two following cURL commands to read client configuration data with the client_id as a URL parameter or client_name as a query string.

curl -k — location — user ‘admin:admin’ — request GET ‘https://localhost:9443/api/identity/oauth2/dcr/v1.1/register/Ak32fw4v16Uv_5FNfv9fg2n7Ei8a'curl -k — location — user ‘admin:admin’ — request GET ‘https://localhost:9443/api/identity/oauth2/dcr/v1.1/register?client_name=simple-application'

Here is the expected response message for the above calls.

{
“client_id”: “Ak32fw4v16Uv_5FNfv9fg2n7Ei8a”,
“client_secret”: “MTZrCpVvaP_VpjZE6uFYQuxXH88a”,
“client_secret_expires_at”: null,
“redirect_uris”: [ “server.example.com/callback”],
“client_name”: “simple-application
}

3.) Update the client configuration

You can use the following cURL command to update the client configuration with the client_id as a URL parameter.

curl -k — location — request PUT ‘https://localhost:9443/api/identity/oauth2/dcr/v1.1/register/Ak32fw4v16Uv_5FNfv9fg2n7Ei8a' \ 
— header ‘Content-Type: application/json’ \
— user ‘admin:admin’ \
— data-raw ‘{ “client_name”: “simple-application”, “redirect_uris”: [“server.example.com/callback”], “grant_types”: [“authorization_code”, “password”]}’

Here is the expected response message.

{
“client_id”: “Ak32fw4v16Uv_5FNfv9fg2n7Ei8a”,
“client_secret”: “MTZrCpVvaP_VpjZE6uFYQuxXH88a”,
“client_secret_expires_at”: null,
“redirect_uris”: [ “server.example.com/callback”]
“client_name”: “simple-application”
}

4.) Delete the client configuration

Here is the cURL command to delete the client configuration with the client_id as a URL parameter. If it worked, it should return an HTTP 204 status code.

curl -k — location — user ‘admin:admin’ — request DELETE ‘https://localhost:9443/api/identity/oauth2/dcr/v1.1/register/Ak32fw4v16Uv_5FNfv9fg2n7Ei8a'

DCR and DCR-M on Okta

Okta is an Identity as a Service provider, and you need to be signed up for a developer account and should have the following details to proceed with the next steps.

— Your Okta organization domain.

— API token.

You can register a client with the following cURL command.

curl -v -X POST \
-H “Accept: application/json” \
-H “Content-Type: application/json” \-H “Authorization: SSWS ${api_token}” \-d ‘{“client_name”: “simple-application”,“redirect_uris”: [“https://server.example.com/callback"],“response_types”: [“code”],“grant_types”: [“authorization_code”,“refresh_token”]}’ “https://${yourOktaDomain}/oauth2/v1/clients"

Here is the expected response message; you can use the server-generated client_id and client_secret in the response message.

{
“client_secret” : “5gdsgdfhdfhmaBbgdgsdgsdgsdg”,
“client_id_issued_at” : 1585695068,“redirect_uris” : [ “https://server.example.com/callback"],“grant_types” : [ “authorization_code”, “refresh_token”],“client_secret_expires_at” : 0,“logo_uri” : null,“application_type” : “web”,“client_name” : “simple-application”,“client_uri” : null,“token_endpoint_auth_method” : “client_secret_basic”,“response_types” : [ “code”],“client_id” : “0oagdggh5l8i8b2bJ2wnfd}}

2.) Read Client configuration

You can use one of the two following cURL commands to read client configuration data with the client_id as a URL parameter or client_name as a query string.

curl -v -X GET \
-H “Accept: application/json” \
-H “Content-Type: application/json” \
-H “Authorization: SSWS ${api_token}” “https://${yourOktaDomain}/oauth2/v1/clients/0oagdggh5l8i8b2bJ2wnfd"

Here is the expected response message for the above call.

{
“grant_types” : [
“authorization_code”,
“refresh_token”
],
“client_id” : “0oagdggh5l8i8b2bJ2wnfd”,
“application_type” : “web”,
“logo_uri” : null,
“client_id_issued_at” : 1585695068,
“client_secret_expires_at” : 0,
“redirect_uris” : [
https://server.example.com/callback"
],
“response_types” : [
“code”
],
“token_endpoint_auth_method” : “client_secret_basic”,
“client_name” : “simple-application”,
“client_uri” : null
}

3.) Update the client configuration

You can use the following cURL command to update the client configuration with the client_id as a URL parameter.

curl -v -X PUT \
-H “Accept: application/json” \
-H “Content-Type: application/json” \
-H “Authorization: SSWS ${api_token}” \
-d ‘{ “grant_types” : [ “authorization_code”, “refresh_token”, “password” ], “client_id” : “0oa55l8i8b2bJ2wnP4x6”, “application_type” : “web”, “logo_uri” : null, “client_id_issued_at” : 1585695068, “client_secret_expires_at” : 0, “redirect_uris” : [ “https://server.example.com/callback"], “response_types” : [ “code”], “token_endpoint_auth_method” : “client_secret_basic”, “client_name” : “simple-application”, “client_uri” : null }’ “https://${yourOktaDomain}/oauth2/v1/clients/0oagdggh5l8i8b2bJ2wnfd"

Here is the expected response message.

{“client_uri” : null,“logo_uri” : null,“response_types” : [“code”],“redirect_uris” : [“https://server.example.com/callback"],“client_name” : “simple-application”,“client_secret_expires_at” : 0,“client_secret” : “5gdsgdfhdfhmaBbgdgsdgsdgsdg”,“client_id” : “0oagdggh5l8i8b2bJ2wnfd”,“token_endpoint_auth_method” : “client_secret_basic”,“client_id_issued_at” : 1585695068,“application_type” : “web”,“grant_types” : [“authorization_code”,“refresh_token”,“password”]}

4.) Delete the client configuration

Here is the cURL command to delete the client configuration with the client_id as a URL parameter. If it worked it should return an HTTP 204 status code.

curl -v -X DELETE \
-H “Accept: application/json” \
-H “Content-Type: application/json” \
-H “Authorization: SSWS ${api_token}” \
“https://${yourOktaDomain}/oauth2/v1/clients/0oagdggh5l8i8b2bJ2wnfd"

As a part of my OAuth2 blog series, In this post, I’ve discussed client registration and management patterns and how they can be implemented using two popular Identity solutions. I hope this was useful, and stay tuned for the next post of this series.

Here are the previous posts of this series.

Integration and Identity Architect & PMC Member @ The Apache Software Foundation, was a Director @ WSO2