Financial-grade API Basics

Preface

This document describes overview of security provisions defined in Financial-grade API - Part 2: Read and Write API Security Profile (hereinafter called “FAPI”) and configuration instructions of Authlete through steps for building a FAPI compliant authorization server.

It is strongly advised that you have basic knowledge of OpenID Connect and Authlete by doing the following tutorial.

Components

In this tutorial, we assume the following components. Note that only Authlete’s consoles and APIs are up and running, while an authorization server, a client and a resource server don’t actually exist. Instead you will use curl command to act as those three ones.

Components in this tutorial

FODNs for each component are as follows. The authorization server and the client don’t exist as stated above, but their FQDNs are at least needed to explain the OAuth flow.

Component FQDN
Authlete API TBA by Authlete personnel; api.authlete.test in this document
Authlete Service Owner Console TBA by Authlete personnel; so.authlete.test in this document
Authlete Developer Console TBA by Authlete personnel; cd.authlete.test in this document
Authorization Server (OIDC Identity Provider) as.example.com
Client (OIDC Relying Party) client.example.org
Resource Server N/A

Interaction between API client and API server using FAPI

In this document, you will configure Authlete to enable the following token granting process and API access flows in a FAPI-compliant manner.

FAPI-compliant token granting process and API access flows

1. Authorization request

A FAPI-compliant client has to employ a request object to craft an authorzation request to a FAPI-compliant authorization server.

The request object is passed to the server by either value (using request parameter) or reference (request_uri paramter). In this document, the client will be using the former one.

In addition to the request object, you have to comply with other security provisions of FAPI, such as response_type and claims parameters. In this document, the client will be using code id_token for response_type parameter. Other settings are to be explained later.

2. Authorization response

The FAPI security provisions doesn’t allow an authorization server to include an authorization code (code) as a query string in an authorization response. In this document, the server will be making a response include the code as a fragment since response_type=code id_token is specified in the previous step.

3. Token request

An authorization server has to employ public key methods to authenticate clients on receiving a token request. In this document, the server will be using mutual TLS authentication.

4. Token response

An authorization server has to bind an access token with a client certificate obtained from mutual TLS communication at token endpoint of the server, and make a token response to the client.

5. API request

An client and a resource server have to establish a mutual TLS communication. The resource server will be verifying the binding between the client certificate and the access token in an API request and then making a response if the binding is valid.

Initial setup

Adding a new Authlete service with a client

Adding a new Authlete service and enabling FAPI support

Log into Authlete’s Service Owner Console https://so.authlete.test/ and click “Create Service” button. You will see the service creation page. Enter Service Name and Token Issuer Identifier as follows, and click “Create” button. Press “OK” in a dialog for confirmation.

Item Value
Service Name An arbitrary value e.g. FAPI Service
Token Issuer Identifier https://as.example.com

The new service has been created. Automatically generated values of “API Key” and “API Secret” will be used as “Login ID” and “Password” to log in to Developer Console, as well as credential for your authorization server to make requests to Authlete APIs.

Item Value
API Key Auto-generated e.g. 174381609020
API Secret Auto-generated e.g. LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k

Let’s enable FAPI support of the service. Click “Edit” button in the bottom of the page to make settings editable and go to Authorization tab. There should be FAPI with a checkbox, which is unchecked, on Supported Service Profiles. Check the box and click “Update” button in the bottom of the page. Press “OK” in a dialog for confirmation.

Supported Service Profiles in Service Details

Click “Edit” button to make settings editable again, and go to Token tab. There should be Supported Scopes and some predefined scopes such as address and openid. Click “Create Scope” button on the right side so that you can see a dialog to define a scope. Add a new scope with the following parameters.

Item Value
Scope Name payment
Default Entry Choose Non default
Description An arbitrary value
Attributes (Click “New Attribute” button to add) Key: fapi / Value: rw

Create Scope in Service Owner Console

Adding a new client and basic settings

Open the link to Authlete’s Developer Console for the service (https://cd.authlete.test/<API Key> e.g. https://cd.authlete.test/174381609020) and log in to the console with your API Key and API Secret as Login ID and Password respectively.

Click “Create App” button on the right side and enter/choose the following values in the create app page.

Item Value
Client Name An arbitrary value e.g. FAPI Client
Client Type Choose CONFIDENTIAL

Then click Authorization tab next to Basic, and enter the following value for Redirect URIs.

Item Value
Redirect URIs https://client.example.org/cb/example.com

Click “Create” button in the bottom of the page. Press “OK” in a dialog for confirmation.

Now you’ve done registration of the client to the service. Automatically generated values of “Client ID” and “Client Secret” will be used as client_id and client_secret for the client to make requests to the authorization server. Also make sure other values are set as expected.

Item Value
Client ID Auto-generetad e.g. 591205987816490
Client Secret Auto-generated e.g. e7iqzq7WE8Kg00yepYnpMTjvDnAnBlq5nfA9DDQLkiYkPQBV6Lr8sLhn7DhUJd17i0O6TwQ2hKFeDAYuU160Vg
Client Type CONFIDENTIAL
Redirect URIs https://client.example.org/cb/example.com

Testing the initial configuration

/auth/authorization API

Let’s check how Authlete works with the initial configuration. We will use /auth/authorization API for that purpose.

Authorization request for non-FAPI scope

Let’s assume that the auhtorization server receives an authorization request (scope=openid) that doesn’t include any scopes subject to FAPI. The server will make a request to /auth/authorization API. Here’s a curl version of the request.

curl -s -X POST https://api.authlete.test/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid&response_type=code&client_id=591205987816490&nonce=n-0S6_WzA2Mj"}' |jq

Authlete makes the following response (folded for readability).

{
  "type": "authorizationResponse",
  "resultCode": "A004001",
  "resultMessage": "[A004001] Authlete has successfully issued a ticket
   to the service (API Key = 174381609020) for the authorization request
   from the client (ID = 591205987816490). [response_type=code, openid=true]",
[...]

According to the value of resultMessage, Authlete accepted the authorization request when its scopes are not FAPI-related. So what happens if the request includes FAPI scopes? Let’s try it in the next section.

Authorization request for FAPI scope (Part 1)

Let’s assume the auhtorization server receives an authorization request (scope=openid payment) that does include a scope (payment) subject to FAPI. The server will make a request to /auth/authorization API. Here’s a curl version of the request.

curl -s -X POST https://api.authlete.test/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code&client_id=591205987816490&nonce=n-0S6_WzA2Mj"}' |jq

Authlete makes the following error response (folded for readability).

{
  "type": "authorizationResponse",
  "resultCode": "A150312",
  "resultMessage": "[A150312] The value of 'response_type' (code) is not allowed.",
[...]

According to the value of resultMessage, response_type=code is not allowed.

This is due to security provisions of FAPI. 5.2 Read and write API security provisions / 5.2.2. Authorization server states as follows.

shall require the response_type values code id_token or code id_token token;

Thus you have to use either response_type=code id_token or response_type=code id_token token in a FAPI-compliant environment. In other words, other types such as response_type=code, response_type=token are prohibited i.e. an authorization server must not accept them.

Authorization request for FAPI scope (Part 2)

So, how about an authorization request that includes response_type=code id_token instead of response_type=code? Let’s make a request to /auth/authorization API as follows.

curl -s -X POST https://api.authlete.test/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj"}' |jq

Again Authlete makes another error response.

{
  "type": "authorizationResponse",
  "resultCode": "A150301",
  "resultMessage": "[A150301] A request object is required.",
[...]

According to the value of resultMessage, a request object is required to proceed.

This is due to security provisions of FAPI. The same section, 5.2 Read and write API security provisions / 5.2.2. Authorization server states as follows.

shall require the request or request_uri parameter to be passed as a JWS signed JWT as in clause 6 of [OIDC];

An authorization server has to mandate clients to pass a request object by value (request) or by reference (request_uri).

So let’s craft an authorization request with a request object in the next section to make the request FAPI-compliant.

Making a FAPI-compliant authorization request

In this section, we will create an authorization request using a a request object. We will also prepare an additional claim required by FAPI.

Request object settings

Configuring a signing key used for a request object

We have to prepare a key for a client to sign to a request object. In this document, we will use mkjwk to create an ES256 key pair, and two types of key sets; One includes a private key and other one doesn’t.

Parameters for mkjwk are as follows.

Item Value
Type tab Elliptic Curve
Curve P-256
Key Use Signing
Algorithm ES256
Key ID An arbitrary value e.g. 1

Generating a JWK Set with mkjwk

Here are examples of a generated key set and a derived set without a private key.

  • es256_keyset.txt (including a row of a private key "d")
{
  "keys": [
    {
      "kty": "EC",
      "d": "L6KxA-db4oh5NKYEpO6IulUDSRXP7fqNAmScu6fygIE",
      "use": "sig",
      "crv": "P-256",
      "kid": "1",
      "x": "icP8p_AigyTzwSpLRyv_bBQTSGu_NG7pMVXd-RAxwYE",
      "y": "06tC0MJeBlZNYlnY8g4bCA9wJ34XN-rWfWlmmlhf-F0",
      "alg": "ES256"
    }
  ]
}
  • es256_keyset_pub.txt (excluding a row of a private key "d" from es256_keyset.txt)
{
  "keys": [
    {
      "kty": "EC",
      "use": "sig",
      "crv": "P-256",
      "kid": "1",
      "x": "icP8p_AigyTzwSpLRyv_bBQTSGu_NG7pMVXd-RAxwYE",
      "y": "06tC0MJeBlZNYlnY8g4bCA9wJ34XN-rWfWlmmlhf-F0",
      "alg": "ES256"
    }
  ]
}

Additional client settings

In order for Authlete service to verify a signature of a request object coming from a client, you have to register its public key to the client’s settings and specify a signing algorithm that the client uses.

Log into Developer Console for the service (https://cd.authlete.test/<API Key> e.g. https://cd.authlete.test/174381609020) and configure the client’s settings as follows.

  • JWK Set tab
Item Value
JWK Set Content Paste content of es256_keyset_pub.txt

JWK Set Content in Developer Console

  • Authorization tab
Item Value
Request Object Signature Algorithm ES256

Request Object Signature Algorithm in Developer Console

Now we have finished preparation to have Authlete verify a signature of a request object.

Creating a request object

Let’s act as a client generating a request object and crafting an authorization request with the object. Here is a sample payload in this document. Enter the correct value of client_id, which was auto-generated in your environment.

payload.txt

{
"redirect_uri":"https://client.example.org/cb/example.com",
"response_type":"code id_token",
"client_id":"<client_id> e.g. 591205987816490",
"scope":"openid payment",
"exp":15549730000,
"aud":"https://as.example.com",
"nonce":"n-0S6_WzA2Mj"
}

The client will be creating a signed JWT with the private key generated in the previous section. In this document, we use mkjose for example. Enter/choose values for each item and click “Generate” so that you can find the signed JWT in Output section.

Item Value
Payload Paste content of payload.txt
Signlng Algorithm Choose EC256
Signing Key Remove the first two lines ("keys":, [) and the last two lines (], }) from the content of es256_keyset.txt (as it must be a JWK, not a JWK set) and paste it

Generating a signed JWT with mkjose

Another example using step CLI is as follows.

cat payload.txt | \
step crypto jws sign --jwks=es256_keyset.txt --kid=1

In this document, the following result was made. This will be used as a value of request parameter in an authorizaiton request.

eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.q_MbfV5qN-gnB93JaQGVrEXu8WvhDuUzWx6DwC50J8AiQGjXDEpw9satUAMN18rrgnGNciiFztoEFJuJjrJoyA

Testing the configuration

Let’s assume that the auhtorization server receives an authorization request, including a request object, from a client. The server will make a request to /auth/authorization API. Here’s a curl version of the request.

curl -s -X POST https://api.authlete.test/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj&request=eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.q_MbfV5qN-gnB93JaQGVrEXu8WvhDuUzWx6DwC50J8AiQGjXDEpw9satUAMN18rrgnGNciiFztoEFJuJjrJoyA"}' |jq

Authlete makes the following error response despite specifying the request object.

{
  "type": "authorizationResponse",
  "resultCode": "A150317",
  "resultMessage": "[A150317] The 'acr' claim is not contained in the request.",
[...]

According to the value of resultMessage, a client has to include an acr (authentication context reference) claim in the request.

This is due to security provisions of FAPI. 5.2 Read and write API security provisions / 5.2.3 Public client states as follows.

shall request user authentication at LoA 3 or greater by requesting the acr claim as an essential claim as defined in section 5.5.1.1 of [OIDC];”

Thus a client has to request an authorization server to authenticate users at LoA 3 and higher. The acr claim is the paramater to specify the LoA.

So let’s try it again by configuring acr support in Authlete service and acting as a client to include the acr claim in another authorization request.

Authentication context reference (acr) settings

Adding an authentication context reference support

Log into Authlete’s Service Owner Console https://so.authlete.test/, click “Edit” button in the bottom of the page to make settings editable, and go to User Authentication tab. There should be Supported Authentication Context Class References section. Add the following values as a new acr. We use an example URN urn:example:psd2:sca for indicating LoA 3 and higher.

Check the box and click “Update” button in the bottom of the page. Press “OK” in a dialog for confirmation.

項目
Supported Authentication Context Class References urn:example:psd2:sca

Supported Authentication Context Class References in Service Owner Console

With the instructions above, a client can request this authorzation server to authenticate users in accordance with the acr.

Improving the incomplete request object

Let’s add acr claims to the payload prepared in the previous section as follows.

payload-including-acr.txt

{
"redirect_uri":"https://client.example.org/cb/example.com",
"response_type":"code id_token",
"client_id":"591205987816490",
"scope":"openid payment",
"exp":15549730000,
"aud":"https://as.example.com",
"claims":{
  "id_token":{
    "acr":{
      "essential":true,
      "values":["urn:example:psd2:sca"]
    }
  }
},
"nonce":"n-0S6_WzA2Mj"
}

Generate another signed JWT with the new payload. Here is an example to generate the JWT with mkjose. Enter/choose values for each item and click “Generate” so that you can find the signed JWT in Output section.

Item Value
Payload Paste content of payload-including-acr.txt
Signlng Algorithm Choose EC256
Signing Key Remove the first two lines ("keys":, [) and the last two lines (], }) from the content of es256_keyset.txt (as it must be a JWK, not a JWK set) and paste it

Generating a signed JWT with mkjose (including acr in the payload)

Another example using step CLI is as follows.

cat payload-including-acr.txt | \
step crypto jws sign --jwks=es256_keyset.txt --kid=1

In this document, thw following result was made. This new one will be used as a value of request parameter in an authorizaiton request.

eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZ
XhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iL
AoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZ
XhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7C
iAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2Y
Wx1ZXMiOlsidXJuOmV4YW1wbGU6cHNkMjpzY2EiXQogICAgfQogIH0KfSwKIm5vbmNlIjoibi0wUzZfV
3pBMk1qIgp9Cg.b5rDSqaI3dh8n4A8hK4B5zSpnZNO_8--W-kTU03CNbCq1I_Vuf3w33ZVUhD0A-rla8
cTPlZ25keQBncGWafzOA

Testing the configuration (with corrected request object)

Let’s replace the value of request parameter in the previous section with the new one including the acr claim and make a request to /auth/authorization API again.

curl -s -X POST https://api.authlete.test/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj&request=eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7CiAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2YWx1ZXMiOlsidXJuOmV4YW1wbGU6cHNkMjpzY2EiXQogICAgfQogIH0KfSwKIm5vbmNlIjoibi0wUzZfV3pBMk1qIgp9Cg.b5rDSqaI3dh8n4A8hK4B5zSpnZNO_8--W-kTU03CNbCq1I_Vuf3w33ZVUhD0A-rla8cTPlZ25keQBncGWafzOA"}' | jq

Authlete will make a response (folded for readability) like this.

{
  "type": "authorizationResponse",
  "resultCode": "A004001",
  "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service
   (API Key = 174381609020) for the authorization request from the client
   (ID = 591205987816490). [response_type=code id_token, openid=true]",
[...]
  "ticket": "rjasCNvemUwamKP0G1h9Fh5Uo_3fgBfQsTF1PU4-GiE"
[...]

According to the value of resultMessage, Authlete accepted the authorization request. The response includes ticket as expected. An authorization server is to store the value of ticket into the user’s login session, and attempt to authenticate the user and obtain consent.

Once completed, the server will make a request to Authlete’s /auth/authorization/issue API to generate an authorization response. In this document, the authorization request shown in the previous section included response_type=code id_token. So Authlete is expected to generate an authorization response that includes an authorization code code and an ID token id_token in fragment.

/auth/authorization/issue API

Let’s assume the auhtorization server authenticates the user, obtains consent and determines that the user’s unique identifier (subject) is testuser01. The server will be making a request to Authlete’s /auth/authorization/issue API with the ticket and the subject. Here’s a curl version of the request.

curl -s -X POST https://api.authlete.test/api/auth/authorization/issue \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"subject":"testuser01","ticket":"rjasCNvemUwamKP0G1h9Fh5Uo_3fgBfQsTF1PU4-GiE"}' | jq

Authlete makes the following response (folded for readability).

{
  "type": "authorizationIssueResponse",
  "resultCode": "A151301",
  "resultMessage": "[A151301] The algorithm
   ('HS256' for 'id_token_signed_response_alg')
    to sign the ID token is not allowed.",
[...]

According to the value of resultMessage, Authlete doesn’t allow HS256 (Authlete’s default settings) as a signing algorithm for ID token.

This is due to security provisions of FAPI. 8.6. JWS algorithm considerations states as follows.

shall use PS256 or ES256 algorithms;

Thus a client and an authorization server have to employ either ES256 or PS256 as JWS algorithm. In this document, we will be configuring Authlete to use ES256 for ID token signing algorithm.

Making a FAPI-compliant authorization response

Changing ID token signing algorithm

Adding a signing key for ID token

Now we are going to generate an ES256 key set and register it to Authlete as a signing key for ID token. We will be also updating the client settings to specify ES256 as a signing algorithm for the client. See the following article in Authlete Knowledge Base for instructions.

Parameters for mkjwk are as follows.

Item Value
Type tab Elliptic Curve
Curve P-256
Key Use Signing
Algorithm ES256
Key ID An arbitrary value e.g. 1

Register the generated “Keypair set” to the service by logging into Authlete’s Service Owner Console https://so.authlete.test/, adding the keypair set to “JWK Set Content” section in JWK Set tab, and enter a value of kid of the keypair set, 1 in this example, to “ID Token Signature Key ID” section in the same page.

JWK Set Content and ID Token Signature Key ID in Service Owner Console

Then log into Developer Console for the service (https://cd.authlete.test/<API Key> e.g. https://cd.authlete.test/174381609020), click a link to the client, click “Edit” button in the bottom of the page to make settings editable, and go to ID Token tab. There should be ID Token Signature Algorithm section. Choose ES256 from dropdown list and click “Update” button.

Those configuration will have Authlete select ES256 as a signing algorithm for issuing ID token for this client and use the registered ES256 key.

ID Token Signature Algorithm in Developer Console

Testing the configuration (ES256 for ID token)

Let’s check if Authlete works as expected. Make the same request again to /auth/authorization API to get a ticket at first.

curl -s -X POST https://api.authlete.test/api/auth/authorization \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj&request=eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7CiAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2YWx1ZXMiOlsidXJuOmV4YW1wbGU6cHNkMjpzY2EiXQogICAgfQogIH0KfSwKIm5vbmNlIjoibi0wUzZfV3pBMk1qIgp9Cg.b5rDSqaI3dh8n4A8hK4B5zSpnZNO_8--W-kTU03CNbCq1I_Vuf3w33ZVUhD0A-rla8cTPlZ25keQBncGWafzOA"}' \
| jq | grep ticket

You should be able to obtain a response (folded for readability) including ticket.

{
[...]
  "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service
   (API Key = 174381609020) for the authorization request from the client
   (ID = 591205987816490). [response_type=code id_token, openid=true]",
  "ticket": "3TzdZO2t8qXaQXIEUA5LLN106uVk5fpwL8_UDGlcwUQ"
[...]

Make a request with the value of the ticket and an arbitrary value of subject (testuser01 in this example) to /auth/authorization/issue API.

curl -s -X POST https://api.authlete.test/api/auth/authorization/issue -u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' -H 'Content-Type: application/json' -d '{"subject":"testuser01","ticket":"3TzdZO2t8qXaQXIEUA5LLN106uVk5fpwL8_UDGlcwUQ"}' | jq

Authlete makes the following response (folded for readability).

{
  "type": "authorizationIssueResponse",
  "resultCode": "A040001",
  "resultMessage": "[A040001] The authorization request was processed successfully.",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "LOCATION",
  "authorizationCode": "TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
   Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJZQjloU01CWkJLdnFobnFaWWRWTXJnIiwiaXNzIjoiaHR0cHM6L
   y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMDUyNiwiaWF0IjoxNTcyMzI0MTI2LCJub25jZSI6I
   m4tMFM2X1d6QTJNaiJ9.ZY5XK4TqAfcnLsMhkigNRpyM6CvwD7SdX-f9TQ18pwMUdh7eoGc6ijlfEnc4
   I3l0jYhlm22yuEeffV6XZhdL0A",
  "responseContent": "https://client.example.org/cb/example.com#
   code=TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ&
   id_token=eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
   Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJZQjloU01CWkJLdnFobnFaWWRWTXJnIiwiaXNzIjoiaHR0cHM6L
   y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMDUyNiwiaWF0IjoxNTcyMzI0MTI2LCJub25jZSI6I
   m4tMFM2X1d6QTJNaiJ9.ZY5XK4TqAfcnLsMhkigNRpyM6CvwD7SdX-f9TQ18pwMUdh7eoGc6ijlfEnc4
   I3l0jYhlm22yuEeffV6XZhdL0A"
}

According to the value of resultMessage, Authlete successfully processed the request. There are an issued ID token and an authorization code in idToken and authorizationCode respectively, and HTTP response content in responseContent. The content includes these token and code as fragment and is intended to be sent from an authorizartion server to a client as an authorization response.

Once the client received the response from the authorization server it will verify the ID token. If the verification is done successfully, it will extract c_hash from the ID token and use it to verify the authorization code. If the second verification is also done successfully, the client will make a token request with the code to the authorzation server. The authorization server will make a request including the token request, to Authlete’s /auth/token API to have the API generate a token response which should include an access token.

/auth/token API

Here’s a curl version of the request to /auth/token API.

curl -s -X POST https://api.authlete.test/api/auth/token \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"clientId":"591205987816490","clientSecret":"e7iqzq7WE8Kg00yepYnpMTjvDnAnBlq5nfA9DDQLkiYkPQBV6Lr8sLhn7DhUJd17i0O6TwQ2hKFeDAYuU160Vg","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ"}'|jq

Authlete makes the following error response (folded for readability).

{
  "type": "tokenResponse",
  "resultCode": "A157301",
  "resultMessage": "[A157301] The client type of the client is 'confidential'
   but the client authentication method is 'none'.",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "INVALID_CLIENT",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "grantType": "AUTHORIZATION_CODE",
  "refreshTokenDuration": 0,
  "refreshTokenExpiresAt": 0,
  "responseContent": "{\"error_description\":\"[A157301] The client type
   of the client is 'confidential' but the client authentication method is 'none'.\",
   \"error\":\"invalid_client\",\"error_uri\":\"https://docs.authlete.com/#A157301\"}"
}

According to the value of resultMessage, the client authentication method of none, which is the default value of Authlete, is not allowed for confidential clients.

This is due to security provisions of FAPI. 5.2 Read and write API security provisions / 5.2.2. Authorization server states as follows.

shall authenticate the confidential client at the token endpoint using one of the following methods (this overrides FAPI part 1 clause 5.2.2.4):

  1. Mutual TLS for OAuth Client Authentication as specified in section 2 of [MTLS];

  2. private_key_jwt as specified in section 9 of [OIDC];

Thus you have to use either OAuth 2.0 Token Binding [OAUTB] or OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens [MTLS] in a FAPI-compliant environment. That is, other methods such as none, client_secret_basic that is popular one for confidential clients, are prohibited i.e. an authorization server must not employ them to authenticate clients.

In this document, we will be having Authlete use the latter one and PKI Mutual-TLS Method (tls_client_auth) defined in the method.

Making a FAPI-compliant token request

TLS client authentication configuration

Let’s configure both the service and the client settings to enable TLS client authentication.

Service settings for TLS client authentication configuration

Log into Authlete’s Service Owner Console https://so.authlete.test/, click “Edit” button in the bottom of the page to make settings editable, and go to Authorization tab. There should be Toen Endpoint section.

Check the box at Supported Client Authentication Methods and click “Update” button in the bottom of the page. Press “OK” in a dialog for confirmation.

Item Value
Supported Client Authentication Methods TLS_CLIENT_AUTH

Supported Client Authentication Methods in Service Owner Console

Client settings for TLS client authentication configuration

Log into Developer Console for the service (https://cd.authlete.test/<API Key> e.g. https://cd.authlete.test/174381609020) and configure the client’s settings as follows.

  • Token Endpoint section in Authorization tab
Item Value
Client Authentication Method TLS_CLIENT_AUTH

Client Authentication Method in Developer Console

  • JWK Set tab
Item Value
TLS Client Auth Subject DN CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP

TLS Client Auth Subject DN in Developer Console

WIth those settings above, Authlete will support mutual TLS authentication for client authentication and apply the method to process token requests from the client. Subject DN CN=client.example.org, ... is used as the identifier of the client.

Generating a self-signed certificate

You have to prepare a digital certificate for the client to be authenticated by the authorization server. The subject DN of the certificate must be the same as one that has been specified in the previous section, CN=client.example.org, ... in this document.

The following example illustrates that generating a self-signed certificate by using OpenSSL.

  • Generating a private key
$ openssl genrsa 2048  > private_key_nopass.pem
Generating RSA private key, 2048 bit long modulus
...............+++
.......................+++
e is 65537 (0x10001)
  • Generating a CSR
    • Using CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP as Subject DN
$ openssl req -new -key private_key_nopass.pem -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) []:Chiyoda-ku
Organization Name (eg, company) []:Client
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:client.example.org
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
$ cat server.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICpTCCAY0CAQAwYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRva3lvMRMwEQYD
VQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNVBAMMEmNsaWVu
[...]
r4MUVOwPNWOM6UGYQZwjvtJ2rmKr8cQrbfvbcFiY4s6lLQGOz5yLzmO8GUdmfzUd
p5BW1iL+SpjS
-----END CERTIFICATE REQUEST-----
  • Generating a certificate
$ openssl x509 -days 365 -req -signkey private_key_nopass.pem -in server.csr -out server.crt
Signature ok
subject=/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Client/CN=client.example.org
Getting Private key

The following one is a certificate generated in this example.

server.crt

-----BEGIN CERTIFICATE-----
MIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3
MjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF
XrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9
J3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL
msYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq
df6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj
mx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
qzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM
z3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9
R6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf
mAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv
lmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr
So6zun26vAUJTu1o9CIjxw==
-----END CERTIFICATE-----

Testing the configuration (Mutual TLS)

Let’s check if Authlete works as expected. Run the same procedure again.

  1. Make a request to /auth/authorization API and obtain a value of ticket from a response
  2. Make a request to /auth/authorization/issue API and obtain a value of authorizationCode from a response
  3. Make a request with the following modification to /auth/token API
    • Add clientCertificate parameter with the client certificate as its value
    • Remove clientSecret parameter (it is no longer required as mutual TLS authentication is effective)

Here’s a curl version of the request to /auth/token API.

curl -s -X POST https://api.authlete.test/api/auth/token \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"clientId":"591205987816490","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=HVIza0dGG9nDKGStAzMObYH9GkXME0aRSaLEcToHEI8","clientCertificate":"-----BEGIN CERTIFICATE-----
MIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3
MjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF
XrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9
J3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL
msYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq
df6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj
mx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
qzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM
z3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9
R6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf
mAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv
lmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr
So6zun26vAUJTu1o9CIjxw==
-----END CERTIFICATE-----"}' |jq

Authlete makes the following response with caution (folded for readability).

{
  "type": "tokenResponse",
  "resultCode": "A152305",
  "resultMessage": "[A152305] The service and the client are not configured
   so that the required Holder of Key methods are performed.",
  "accessToken": "RCqjF4tlffJ7-n92sAEFQNIwrRm0syOUrBu0cNLAIJU",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "BAD_REQUEST",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "grantType": "AUTHORIZATION_CODE",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNTcyNDEyMTcwL
  CJpYXQiOjE1NzIzMjU3NzAsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.x4XmPTh698AbNEjCaNcD5k54q
  S249BSPkc9EkwZuUI17AL8z593GYTg3GVQQdhF9k0HYLRA17c3m39OxYDrx3g",
  "refreshToken": "jy5lN7TXZrAlIfgFbOaMMkzDtoJUu4prBtNa3HcoRRE",
  "refreshTokenDuration": 0,
  "refreshTokenExpiresAt": 0,
  "responseContent": "{\"error_description\":\"[A152305] The service and the client are
   not configured so that the required Holder of Key methods are performed.\",
   \"error\":\"invalid_request\",\"error_uri\":\"https://docs.authlete.com/#A152305\"}"
}

According to the value of resultMessage, Authlete didn’t perform “Holder of Key” method, which is mandatory to comply with the security provisions of FAPI, while an access token has been issued. Thus the issued access token is not bound with the TLS client certificate of the client.

Making a FAPI-compliant token response

Holder of Key configuration

Service settings for access token configuration

Log into Authlete’s Service Owner Console https://so.authlete.test/, click “Edit” button in the bottom of the page to make settings editable, and go to Token tab. There should be Access Token section. Choose the following option for TLS Client Certificate Bound Access Tokens.

Item Value
TLS Client Certificate Bound Access Tokens Choose Supported

TLS Client Certificate Bound Access Tokens in Service Owner Console

Client settings for access token configuration

Log into Developer Console for the service (https://cd.authlete.test/<API Key> e.g. https://cd.authlete.test/174381609020), click a link to the client, click “Edit” button in the bottom of the page to make settings editable, and go to Basic tab. Choose the following option for TLS Client Certificate Bound Access Tokens.

Item Value
TLS Client Certificate Bound Access Tokens Choose Enabled

TLS Client Certificate Bound Access Tokens in Developer Console

Congraturations! Finally you have finished configuration of Authlete to support a FAPI-compliant authorization server.

A complete example walk through

FAPI-compliant token granting process and API access flows using Authlete

Once the configuration is done you are able to check if Authlete works as expected. Make requests, which are the same as the ones in the previous section, to /auth/authorization API, /auth/authorization/issue API and /auth/token API.

In addition to these three requests, make another request to /auth/introspection API to see if the access token used for API requests to a resource server is bound with the TLS client certificate of the client.

Authorization request

curl -s -X POST https://api.authlete.test/api/auth/authorization -u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' -H 'Content-Type: application/json' -d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code+id_token&client_id=591205987816490&nonce=n-0S6_WzA2Mj&request=eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7CiAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2YWx1ZXMiOlsidXJuOm1hY2U6aW5jb21tb246aWFwOnNpbHZlciJdCiAgICB9CiAgfQp9LAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.50gewunAqCITD6p2kI52GDXdUgQP-EzLDjjjoDT9C4zY8YCKgLzN7sR2ZvkAQ_pimLpwFh2QYjjyPskvvtnC9g"}' |jq|grep ticket
  • Response (folded for readability)
{
[...]
  "resultMessage": "[A004001] Authlete has successfully issued a ticket
   to the service (API Key = 174381609020) for the authorization request
  from the client (ID = 591205987816490). [response_type=code id_token, openid=true]",
  [...]
  "ticket": "b0JGD-ZkT8ElBGw2ck-T-t87Z033jXvhqC2omPT1bQ4"
  [...]

Authorization response

curl -s -X POST https://api.authlete.test/api/auth/authorization/issue -u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' -H 'Content-Type: application/json' -d '{"subject":"testuser01","ticket":"b0JGD-ZkT8ElBGw2ck-T-t87Z033jXvhqC2omPT1bQ4"}' | jq
  • Response (folded for readability)
{
  "type": "authorizationIssueResponse",
  "resultCode": "A040001",
  "resultMessage": "[A040001] The authorization request was processed successfully.",
  "accessTokenDuration": 0,
  "accessTokenExpiresAt": 0,
  "action": "LOCATION",
  "authorizationCode": "DxiKC0cOc_46nzVjgr41RWBQtMDrAvc0BUbMJ_v7I70",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJqR2kyOElvYm5HcjNNQ3Y0UUVQRTNnIiwiaXNzIjoiaHR0cHM6L
  y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMjY4MiwiaWF0IjoxNTcyMzI2MjgyLCJub25jZSI6I
  m4tMFM2X1d6QTJNaiJ9.1PFmc0gAsBWtLBriq3z9a4Tsi_ioEYlOqOYbicGEXWIS1WGX5ffGOyZNSzVB
  MamZbltZmSys0jlYmmYYLqgGsg",
  "responseContent": "https://client.example.org/cb/example.com#
  code=DxiKC0cOc_46nzVjgr41RWBQtMDrAvc0BUbMJ_v7I70&
  id_token=eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJjX2hhc2giOiJqR2kyOElvYm5HcjNNQ3Y0UUVQRTNnIiwiaXNzIjoiaHR0cHM6L
  y9hcy5leGFtcGxlLmNvbSIsImV4cCI6MTU3MjQxMjY4MiwiaWF0IjoxNTcyMzI2MjgyLCJub25jZSI6I
  m4tMFM2X1d6QTJNaiJ9.1PFmc0gAsBWtLBriq3z9a4Tsi_ioEYlOqOYbicGEXWIS1WGX5ffGOyZNSzVB
  MamZbltZmSys0jlYmmYYLqgGsg"
}

Token request and token response

curl -s -X POST https://api.authlete.test/api/auth/token \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"clientId":"591205987816490","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=DxiKC0cOc_46nzVjgr41RWBQtMDrAvc0BUbMJ_v7I70","clientCertificate":"-----BEGIN CERTIFICATE-----
MIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3
MjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF
XrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9
J3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL
msYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq
df6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj
mx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
qzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM
z3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9
R6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf
mAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv
lmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr
So6zun26vAUJTu1o9CIjxw==
-----END CERTIFICATE-----"}' |jq
  • Response (folded for readability)
{
  "type": "tokenResponse",
  "resultCode": "A050001",
  "resultMessage": "[A050001] The token request (grant_type=authorization_code)
   was processed successfully.",
  "accessToken": "SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU",
  "accessTokenDuration": 86400,
  "accessTokenExpiresAt": 1572412769390,
  "action": "OK",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "grantType": "AUTHORIZATION_CODE",
  "idToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNTcyNDEyNzY5L
  CJpYXQiOjE1NzIzMjYzNjksIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.9EQojck-Cf2hnKAZWR164kr21
  o5lPKehvIHyViZgRg4CY_ZGmnyFooG4FCwlZxu-QOTtaDCffCsuCdz4GqknTA",
  "refreshToken": "tXZjYfoK35I-djg9V3n6s58zsrVqRIzTNMXKIS_wkj8",
  "refreshTokenDuration": 864000,
  "refreshTokenExpiresAt": 1573190369390,
  "responseContent": "{\"access_token\":\"SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU\",
  \"refresh_token\":\"tXZjYfoK35I-djg9V3n6s58zsrVqRIzTNMXKIS_wkj8\",\"scope\":\"openid payment\",
  \"id_token\":\"eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjU5MTIwN
  Tk4NzgxNjQ5MCJdLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNTcyNDEyNzY5L
  CJpYXQiOjE1NzIzMjYzNjksIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.9EQojck-Cf2hnKAZWR164kr21
  o5lPKehvIHyViZgRg4CY_ZGmnyFooG4FCwlZxu-QOTtaDCffCsuCdz4GqknTA\",
  \"token_type\":\"Bearer\",\"expires_in\":86400}",
  "scopes": [
    "openid",
    "payment"
  ],
  "subject": "testuser01"
}

API request

After the procedure above, we have got an access token whose value is SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU in this example.

Let’s assume that the resource server receives an API request, including the value as the access token, from the client. The token in the request would be in Authorization: Bearer header.

The resource server is to verify the token and obtain related information with it, by making a request to /auth/introspection API. The resource server will also include the client certificate, which should be able to obtained from mutual TLS communication for the API request, into the request to Authlete.

/auth/introspection API

curl -s -X POST https://api.authlete.test/api/auth/introspection \
-u '174381609020:LszYEVDLM5Bu4lRjO9Vaj0tMSMVerWiPf_zcdy-vu4k' \
-H 'Content-Type: application/json' \
-d '{"token":"SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU","clientCertificate":"-----BEGIN CERTIFICATE-----
MIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3
MjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF
XrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9
J3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL
msYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq
df6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj
mx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
qzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM
z3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9
R6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf
mAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv
lmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr
So6zun26vAUJTu1o9CIjxw==
-----END CERTIFICATE-----"}'|jq
  • Response (folded for readability)
{
  "type": "introspectionResponse",
  "resultCode": "A056001",
  "resultMessage": "[A056001] The access token is valid.",
  "action": "OK",
  "certificateThumbprint": "cBNP0zNH0fkcIQdVHdB8GDQAbaZyIjKXB0EVRTByJMU",
  "clientId": 591205987816490,
  "clientIdAliasUsed": false,
  "existent": true,
  "expiresAt": 1572412769000,
  "refreshable": true,
  "responseContent": "Bearer error=\"invalid_request\"",
  "scopes": [
    "openid",
    "payment"
  ],
  "subject": "testuser01",
  "sufficient": true,
  "usable": true
}

The resource server is now able to find that the access token from the client has been verified and get the associated information with the token such as subject and scopes.

Conclusion

In this tutorial, we reviewed security provisions defined in Financial-grade API - Part 2: Read and Write API Security Profile and configuration instructions of Authlete through steps for building a FAPI compliant authorization server.