Table of Contents
This document describes overview of security provisions defined in Financial-grade API Security Profile 1.0 - Part 2: Advanced (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.
In this tutorial, we assume the following components. Note that only Authlete’s console and APIs are up and running, while an authorization server (OIDC identity provider) and a resource server don’t actually exist.
Instead you will use curl command to simulate how these servers make API requests to Authlete on receiving authorization requests, token requests and token introspection requests from clients (OIDC relying party).
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 US Cluster | us.authlete.com |
Authlete Management Console | console.authlete.com |
Authorization Server | as.example.com |
Client | client.example.org |
Resource Server | N/A |
In this document, you will configure Authlete to enable the following token granting process and API access flows in a FAPI-compliant manner.
A FAPI-compliant client has to employ a request object to craft an authorization 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
parameter). 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
parameter. In this document, the client will be using response_type=code id_token
.
response_type=code
.
Other settings, including claims
, are to be explained later.
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.
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.
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.
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.
Log in to the Authlete Management Console at https://console.authlete.com/.
Go to your descired Organization. Click the Create New Service button. This will open the service creation page.
Fill in the following fields:
FAPI Service
.https://as.example.com
.In the Supported Service Profiles section (optional), select FAPI 1
:
After completing the details, click the Create button.
If you have an existing service where you want to enable FAPI, open the Service Settings for your service and navigate to Endpoints > Advanced.
To add a scope with FAPI enforcement:
payment
.This payment scope requires FAPI compliant security
.fapi
rw
In the Authlete Management Console, locate and click on your FAPI Service, then click the Create Client button.
Enter the following details:
FAPI Client
.client_id
.Click the Create button to create the client.
The Client ID will be automatically generated by Authlete. This client_id
will be used by the client to make requests to the authorization server. A Client Secret will also be generated but is not used in this tutorial.
Item | Value |
---|---|
Client Name | FAPI Client |
Client Type | CONFIDENTIAL |
Client ID | Auto-generated (e.g., 591205987816490 ) |
Redirect URIs | https://client.example.org/cb/example.com (if applicable) |
Let’s check how Authlete works with the initial configuration. We will use /auth/authorization
API for that purpose.
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.
Make sure to replace {SERVICE ACCESS TOKEN}
and {Client ID}
with your own values generated in the previous step.
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid&response_type=code&client_id={Client ID}&nonce=n-0S6_WzA2Mj"}' | jq
Make sure to replace {SERVICE ACCESS TOKEN}
and {Client ID}
with your own values generated in the previous step.
If you are using Windows 10's bundled curl.exe command via PowerShell, make sure the command is curl.exe
instead of curl
, escape "
characters and use `
to break lines.
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-H 'Content-Type: application/json' `
-d '{\"parameters\": \"redirect_uri=https://client.example.org/cb/example.com&scope=openid&response_type=code&client_id={Client ID}&nonce=n-0S6_WzA2Mj\"}'
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.
Let’s assume the authorization 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://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code&client_id={Client ID}&nonce=n-0S6_WzA2Mj"}' | jq
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-H 'Content-Type: application/json' `
-d '{\"parameters\": \"redirect_uri=https://client.example.org/cb/example.com&scope=openid+payment&response_type=code&client_id={Client ID}&nonce=n-0S6_WzA2Mj\"}'
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. Advanced security provisions / 5.2.2. Authorization server states as follows.
- shall require
- the
response_type
valuecode id_token
, or- the
response_type
valuecode
in conjunction with theresponse_mode
valuejwt
;
Thus you have to use either response_type=code id_token
, or response_type=code
with response_mode=jwt
in a FAPI-compliant environment. In other words, response_type=code
without the response_mode=jwt
parameter is prohibited i.e. an authorization server must not accept them.
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://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-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={Client ID}&nonce=n-0S6_WzA2Mj"}' | jq
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-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={Client ID}&nonce=n-0S6_WzA2Mj\"}'
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. Advanced security provisions / 5.2.2. Authorization server states as follows.
- shall only use the parameters included in the signed request object passed via the
request
orrequest_uri
parameter;
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.
In this section, we will create an authorization request using a 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 |
---|---|
Tab | EC (Elliptic Curve) |
Curve | P-256 |
Key Use | Signing |
Algorithm | ES256 (ECDSA using P-256 and SHA-256) |
Key ID | An arbitrary value e.g. 1 |
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"
}
]
}
To allow Authlete to verify the signature of a request object from a client, you must register the client’s public key and specify the signing algorithm the client uses. Follow these steps in the Authlete Management Console:
Navigate to Client Settings > Key Management > JWK Set and configure the following:
Item | Value |
---|---|
JWK Set Content | Paste the content of es256_keyset_pub.txt |
Paste the public key in JSON Web Key (JWK) format into the JWK Set Content field.
Navigate to Client Settings > Endpoints > Authorization > Request Object and configure the following:
Item | Value |
---|---|
Request Object Signature Algorithm | ES256 |
Select ES256 as the signature algorithm for the request object.
Now you’ve completed the configuration for Authlete to verify the signature of 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 values of client_id
, nbf
and exp
.
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",
"nbf": <Unix time of the current time> e.g. 1613373232,
"exp": <nbf + 3600> e.g. 1613376832,
"exp":15549730000,
"aud":"https://as.example.com",
"nonce":"n-0S6_WzA2Mj"
}
nbf
and exp
.
nbf
claim: get the current Unix time using some method (e.g. date +%s
) and add it to the payload as a value of nbf
claims. For example, if you would get a value 1613373232
as a result of date +%s
,
you would add the following line to payload.txt
.
"nbf":1613373232,
exp
claim: specify a new value which is a sum of the value of nbf
and 3600
. For example, if "nbf":1613373232
would be used, the new value would be 1613373232 + 3600 = 1613376832
and the line of the exp
claim in the payload.txt
would be as follows.
"exp":1613376832,
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 |
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
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://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-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={Client ID}&nonce=n-0S6_WzA2Mj&request=<Request Object e.g. eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.q_MbfV5qN-gnB93JaQGVrEXu8WvhDuUzWx6DwC50J8AiQGjXDEpw9satUAMN18rrgnGNciiFztoEFJuJjrJoyA>"}' | jq
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-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={Client ID}&nonce=n-0S6_WzA2Mj&request=<Request Object e.g. eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.q_MbfV5qN-gnB93JaQGVrEXu8WvhDuUzWx6DwC50J8AiQGjXDEpw9satUAMN18rrgnGNciiFztoEFJuJjrJoyA>\"}'
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.
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://us.authlete.com/api/{Service ID}/auth/authorization/issue \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"subject":"testuser01","ticket":"rjasCNvemUwamKP0G1h9Fh5Uo_3fgBfQsTF1PU4-GiE"}' | jq
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization/issue `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-H 'Content-Type: application/json' `
-d '{\"subject\":\"testuser01\",\"ticket\":\"rjasCNvemUwamKP0G1h9Fh5Uo_3fgBfQsTF1PU4-GiE\"}'
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. 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.
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.
Parameters for mkjwk are as follows.
Item | Value |
---|---|
Tab | EC (Elliptic Curve) |
Curve | P-256 |
Key Use | Signing |
Algorithm | ES256 (ECDSA using P-256 and SHA-256) |
Key ID | An arbitrary value e.g. 1 |
Navigate to Service Settings > Key Management > JWK Set, paste the generated keypair set into the JWK Set Content field, and use the ID Token Signature Key ID dropdown to select the Key ID (kid
) value of the keypair set (e.g., 1
). Click Save Changes to apply the updates.
Navigate to Client Settings > Tokens and Claims > ID Token, locate the ID Token Algorithms section, and select ES256 from the Select Signature Algorithm dropdown. This configuration ensures that Authlete uses ES256 as the signing algorithm for issuing ID tokens for this client with the registered ES256 key. Click Save Changes to apply the updates.
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://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-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={Client ID}&nonce=n-0S6_WzA2Mj&request=<Request Object e.g. eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7CiAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2YWx1ZXMiOlsidXJuOmV4YW1wbGU6cHNkMjpzY2EiXQogICAgfQogIH0KfSwKIm5vbmNlIjoibi0wUzZfV3pBMk1qIgp9Cg.b5rDSqaI3dh8n4A8hK4B5zSpnZNO_8--W-kTU03CNbCq1I_Vuf3w33ZVUhD0A-rla8cTPlZ25keQBncGWafzOA>"}' | jq | grep ticket
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-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={Client ID}&nonce=n-0S6_WzA2Mj&request=<Request Object e.g. eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoiY2xhaW1zIjp7CiAgImlkX3Rva2VuIjp7CiAgICAiYWNyIjp7CiAgICAgICJlc3NlbnRpYWwiOnRydWUsCiAgICAgICJ2YWx1ZXMiOlsidXJuOmV4YW1wbGU6cHNkMjpzY2EiXQogICAgfQogIH0KfSwKIm5vbmNlIjoibi0wUzZfV3pBMk1qIgp9Cg.b5rDSqaI3dh8n4A8hK4B5zSpnZNO_8--W-kTU03CNbCq1I_Vuf3w33ZVUhD0A-rla8cTPlZ25keQBncGWafzOA>\"}'
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://us.authlete.com/api/{Service ID}/auth/authorization/issue \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"subject":"testuser01","ticket":"3TzdZO2t8qXaQXIEUA5LLN106uVk5fpwL8_UDGlcwUQ"}' | jq
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization/issue `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-H 'Content-Type: application/json' `
-d '{\"subject\":\"testuser01\",\"ticket\":\"3TzdZO2t8qXaQXIEUA5LLN106uVk5fpwL8_UDGlcwUQ\"}'
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.
Here’s a curl version of the request to /auth/token
API.
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/token \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"clientId":"<Client ID e.g. 591205987816490>","clientSecret":"<Client Secret e7iqzq7WE8Kg00yepYnpMTjvDnAnBlq5nfA9DDQLkiYkPQBV6Lr8sLhn7DhUJd17i0O6TwQ2hKFeDAYuU160Vg>","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=<Code e.g. TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ>"}' | jq
curl.exe -s -X POST https://us.authlete.com/api/{Service ID}/auth/token `
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' `
-H 'Content-Type: application/json' `
-d '{\"clientId\":\"<Client ID e.g. 591205987816490>\",\"clientSecret\":\"<Client Secret e7iqzq7WE8Kg00yepYnpMTjvDnAnBlq5nfA9DDQLkiYkPQBV6Lr8sLhn7DhUJd17i0O6TwQ2hKFeDAYuU160Vg>\",\"parameters\": \"grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=<Code e.g. TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ>\"}'
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 using one of the following methods (this overrides FAPI Security Profile 1.0 - Part 1: Baseline clause 5.2.2-4):
tls_client_auth
orself_signed_tls_client_auth
as specified in section 2 of MTLS, orprivate_key_jwt
as specified in section 9 of OIDC;
Thus you have to use either
Mutual TLS for OAuth Client Authentication defined in RFC 8705 or private_key_jwt defined in OpenID Connect Core 1.0. 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 former one and PKI Mutual-TLS Method (tls_client_auth
) defined in the method.
private_key_jwt
.
Note that you have to configure mutual TLS to enable "Holder of Key," even if the client authentication method is employed.
Let’s configure both the service and the client settings to enable TLS client authentication.
CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP
).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.
$ openssl genrsa 2048 > private_key_nopass.pem
Generating RSA private key, 2048 bit long modulus
...............+++
.......................+++
e is 65537 (0x10001)
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-----
$ 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-----
The certificate gets transformed into one line as follows, so that it can be used in requests with curl command.
-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n
Let’s check if Authlete works as expected. Run the same procedure again.
/auth/authorization
API and obtain a value of ticket
from a response/auth/authorization/issue
API and obtain a value of authorizationCode
from a response/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.com/api/auth/token \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "<Client ID e.g. 591205987816490>",
"parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=<Code e.g. TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ>",
"clientCertificate": "-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n"
}' | jq
curl.exe -s -X POST https://api.authlete.com/api/auth/token `
-H "Authorization: Bearer {SERVICE ACCESS TOKEN}" `
-H "Content-Type: application/json" `
-d "{
\"clientId\": \"<Client ID e.g. 591205987816490>\",
\"parameters\": \"grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=<Code e.g. TSRAvPIp6V3RgPOs2O7FpPG1_7t6Xpc_kcIramz8gBQ>\",
\"clientCertificate\": \"-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n\"
}" | 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.
In the Authlete Management Console, navigate to Service Settings for your service. Under the Tokens and Claims section, go to Access Tokens. Enable TLS Client Certificate Binding by toggling the switch as shown in the screenshot below.
In the Authlete Management Console, navigate to Client Settings for your client. Under the Tokens and Claims section, go to Access Token. Enable TLS Client Certificate Binding by toggling the switch as shown in the screenshot below.
Congratulations! You have successfully configured Authlete to support a FAPI-compliant authorization server.
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.
/auth/authorization
APIcurl -s -X POST https://us.authlete.com/api/{SERVICE ID}/authorization \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-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"}'
{
[...]
"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"
[...]
/auth/authorization/issue
APIcurl -s -X POST https://us.authlete.com/api/{SERVICE ID}/authorization/issue \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{
"subject": "testuser01",
"ticket": "b0JGD-ZkT8ElBGw2ck-T-t87Z033jXvhqC2omPT1bQ4"
}' | jq
{
"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"
}
/auth/token
APIcurl -s -X POST https://us.authlete.com/api/{SERVICE ID}/token \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-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-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n"}' |jq
{
"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"
}
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
APIcurl -s -X POST https://us.authlete.com/api/service/<SERVICE ID>/introspection \
-H 'Authorization: Bearer <Service Access Token>' \
-H 'Content-Type: application/json' \
-d '{"token":"SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU","clientCertificate":"-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n"}'|jq
curl.exe -s -X POST https://us.authlete.com/api/service/<SERVICE ID>/introspection `
-H "Authorization: Bearer <Service Access Token>" `
-H "Content-Type: application/json" `
-d '{\"token\":\"SUtEVc3Tj3D3xOdysQtssQxe9egAhI4fimexNVMjRyU\",\"clientCertificate\":\"-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDWNMOIuzwDfzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJK\nUDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\nBkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTE5MTAyODA3\nMjczMFoXDTIwMTAyNzA3MjczMFowYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\na3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\nBAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAK2Oyc+BV4N5pYcp47opUwsb2NaJq4X+d5Itq8whpFlZ9uCCHzF5TWSF\nXrpYscOp95veGPF42eT1grfxYyvjFotE76caHhBLCkIbBh6Vf222IGMwwBbSZfO9\nJ3eURtEADBvsZ117HkPVdjYqvt3Pr4RxdR12zG1TcBAoTLGchyr8nBqRADFhUTCL\nmsYaz1ADiQ/xbJN7VUNQpKhzRWHCdYS03HpbGjYCtAbl9dJnH2EepNF0emGiSPFq\ndf6taToyCr7oZjM7ufmKPjiiEDbeSYTf6kbPNmmjtoPNNLeejHjP9p0IYx7l0Gkj\nmx4kSMLp4vSDftrFgGfcxzaMmKBsosMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA\nqzdDYbntFLPBlbwAQlpwIjvmvwzvkQt6qgZ9Y0oMAf7pxq3i9q7W1bDol0UF4pIM\nz3urEJCHO8w18JRlfOnOENkcLLLntrjOUXuNkaCDLrnv8pnp0yeTQHkSpsyMtJi9\nR6r6JT9V57EJ/pWQBgKlN6qMiBkIvX7U2hEMmhZ00h/E5xMmiKbySBiJV9fBzDRf\nmAy1p9YEgLsEMLnGjKHTok+hd0BLvcmXVejdUsKCg84F0zqtXEDXLCiKcpXCeeWv\nlmmXxC5PH/GEMkSPiGSR7+b1i0sSotsq+M3hbdwabpJ6nQLLbKkFSGcsQ87yL+gr\nSo6zun26vAUJTu1o9CIjxw==\n-----END CERTIFICATE-----\n\"}'
{
"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.
In this tutorial, we reviewed security provisions defined in Financial-grade API Security Profile 1.0 - Part 2: Advanced and configuration instructions of Authlete through steps for building a FAPI compliant authorization server.