Implementing FAPI 2.0 Security Profile with Authlete

Overview

This article explains FAPI 2.0 Security Profile and how to implement it with Authlete. For a detailed explanation, we illustrate FAPI 2.0 Security Profile using Authorization Code Flow depicted below.

Authorization Code Flow in FAPI2 Security profile

The following sections explain how to setup the service and the client and demonstrate this flow through actual API calls.

Service Configurations

At the service owner console, configure the service as follows.

Basic > Token Issuer Identifier

Set your authorization server’s issuer identifier.

Authorization > Supported Grant Types

Include AUTHORIZATION_CODE.

Authorization > Supported Response Types

Include CODE.

Authorization > Supported Service Profiles

Include FAPI.

Authorization > Authorization Endpoint > iss Response Parameter

Select Included. This is required by FAPI 2.0 Security Profile, 5.3.1.2. Authorization Code Flow:

“FAPI 2.0 Security Profile, 5.3.1.2. Authorization Code Flow”

For the Authorization Code flow, Authorization servers

7. shall return an iss parameter in the authorization response according to [RFC9207]

Authorization > Token Endpoint > Token Endpoint URI

Set your authorization server’s token endpoint URI.

Authorization > Token Endpoint > Supported Client Authentication Methods

MTLS or private_key_jwt is required as the client authentication method, according to FAPI2 Security profile, 5.3.1.1. General Requirements:

“FAPI2 Security profile, 5.3.1.1. General Requirements”

Authorization servers

6. shall authenticate clients using one of the following methods:

- MTLS as specified in section 2 of [RFC8705]

- private_key_jwt as specified in section 9 of [OIDC]

In the following API call simulation, we use private_key_jwt for client authentication. Then, this property must include PRIVATE_KEY_JWT.

Token > Access Token > Access Token Signature Algorithm

Select PS256, ES256 or EdDSA, according to FAPI2 Security Profile, 5.4. Cryptography and Secrets:

“FAPI2 Security profile, 5.4. Cryptography and Secrets”

Authorization Servers, Clients, and Resource Servers when creating or processing JWTs shall

2. use PS256, ES256, or EdDSA (using the Ed25519 subtype) algorithms

Token > Refresh Token > Refresh Token Continuous Use

Select Kept, if Disabled is set for “Token > Refresh Token > Idempotency”, according to FAPI2 Security profile, 5.3.1.1. General Requirements:

“FAPI2 Security profile, 5.3.1.1. General Requirements”

Authorization servers

9. shall not use refresh token rotation unless, in the case a response with a new refresh token is not received and stored by the client, retrying the request (with the previous refresh token) will succeed.

Token > Scope > Supported Scopes

Create a scope attribute and set the key to fapi2 and the value to sp. Then, link it to one of the supported scope. The below is an example of such a scope:

{
  "name": "myscope",
  "attributes": [
    {
      "key": "fapi2",
      "value": "sp"
    }
  ]
  ...
}

JWK Set > JWK Set Content

Set a JWK set containing required JWKs (e.g. JWT access token sign key).

JWK Set > JWK Set Endpoint URI

Set a URI that starts with https. The URI needs to point to a JWK set containing required JWKs (e.g. JWT access token sign key).

Client Configurations

At the client developer console, configure the client as follows.

Basic > Client Type

Select CONFIDENTIAL.

Authorization > Grant Types

Include AUTHORIZATION_CODE.

Authorization > Response Types

Include CODE.

Authorization > Redirect URIs

Create at least one redirect URI.

Authorization > Client Authentication Method

MTLS or private_key_jwt is required as the client authentication method, according to FAPI2 Security profile, 5.3.2.1. General Requirements:

“FAPI2 Security profile, 5.3.2.1. General Requirements”

Clients

2. shall support client authentication using one of the following methods:

- MTLS as specified in section 2 of [RFC8705]

- private_key_jwt as specified in section 9 of [OIDC]

In the following API call simulation, we use private_key_jwt for client authentication. Then, this property must be set to PRIVATE_KEY_JWT.

Authorization > Token Endpoint > Assertion Signature Algorithm

Select PS256, ES256 or EdDSA, according to FAPI2 Security Profile, 5.4. Cryptography and Secrets:

FAPI2 Security profile, 5.4. Cryptography and Secrets

Authorization Servers, Clients, and Resource Servers when creating or processing JWTs shall

2. use PS256, ES256, or EdDSA (using the Ed25519 subtype) algorithms

ID Token > ID Token Signature Algorithm

Select an encryption algorithm other than NONE, according to FAPI2 Security Profile, 5.4. Cryptography and Secrets:

“FAPI2 Security profile, 5.4. Cryptography and Secrets”

Authorization Servers, Clients, and Resource Servers when creating or processing JWTs shall

not use or accept the none algorithm

ID Token > ID Token Encryption Algorithm

Select an encryption algorithm other than RSA1_5. This is required by FAPI2 Security Profile, 5.4. Cryptography and Secrets:

“FAPI2 Security profile, 5.4. Cryptography and Secrets”

Authorization Servers, Clients, and Resource Servers when creating or processing JWTs shall

1. adhere to [RFC8725]

Note that RFC8725, 3.2. Use Appropriate Algorithms states as follows:

“RFC8725, 3.2. Use Appropriate Algorithms”

Applications SHOULD follow these algorithm-specific recommendations:

- Avoid all RSA-PKCS1 v1.5 encryption algorithms ([RFC8017], Section 7.2), preferring RSAES-OAEP ([RFC8017], Section 7.1).

JWK Set > JWK Set Content

Set a JWK set containing required JWKs (e.g. client assertion sign key).

JWK Set > JWK Set URI

Set a URI that starts with https. The URI needs to point to a JWK set containing required JWKs (e.g. client assertion sign key).

API Call Simulation

In this section, we simulate API calls that the authorization server makes against Authlete APIs in the context of Authorization Code Flow in FAPI2 Security Profile.

1. /pushed_auth_req API

Suppose that a FAPI2 Security profile compliant client sends a valid request to the pushed authorization request endpoint of authorization server. The pushed authorization request endpoint must authenticate the client using MTLS or private_key_jwt at the pushed authorization request endpoint. This is required by FAPI2 Security profile, 5.3.2.1. General Requirements:

“FAPI2 Security profile, 5.3.2.1. General Requirements”

Clients

2. shall support client authentication using one of the following methods:

- MTLS as specified in section 2 of [RFC8705]

- private_key_jwt as specified in section 9 of [OIDC]

In this simulation, we use private_key_jwt as the client authentication method.

Additionally, request parameters in the request must meet the following requirements:

scope parameter

The scope parameter must include a scope associated with an attribute whose key is fapi2 and value is sp, respectively. See this for more details.

response_type parameter

The response_type parameter must be set to code, according to FAPI2 Security profile, 5.3.2.2. Authorization Code Flow:

“FAPI2 Security profile, 5.3.2.2. Authorization Code Flow”

For the Authorization Code flow, Clients

1. shall use the authorization code grant described in [RFC6749]

code_challenge parameter and code_challenge_method parameter

The code_challenge parameter must be set and the code_challenge_method parameter must be set to S256, according to FAPI2 Security profile, 5.3.2.2. Authorization Code Flow:

“FAPI2 Security profile, 5.3.2.2. Authorization Code Flow”

For the Authorization Code flow, Clients

3. shall use PKCE [RFC7636] with S256 as the code challenge method

redirect_uri parameter

The redirect_uri parameter must be a URI starting with https, according to FAPI2 Security profile, 5.3.1.2. Authorization Code Flow:

“FAPI2 Security profile, 5.3.2.2. Authorization Code Flow”

For the Authorization Code flow, Authorization servers

6. shall require the redirect_uri parameter in pushed authorization requests

8. shall not transmit authorization responses over unencrypted network connections, and, to this end, shall not allow redirect URIs that use the "http" scheme except for native clients that use Loopback Interface Redirection as described in [RFC8252], Section 7.3,

Taking all of the requirements above into account, the request that the client send to the pushed authorization endpoint would be like below.

POST /par HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJraWQi...
&client_id={Client ID}
&response_type=code
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&scope=myscope
&code_challenge=E9Melhoa...
&code_challenge_method=S256

After the authorization server receives the request from the client, the authorization server calls Authlete /pushed_auth_req API including the received parameters. Below is a curl command that simulates a request from the authorization server to Authlete /pushed_auth_req API.

curl -s -X POST https://api.authlete.com/api/pushed_auth_req \
-u '{Service API Key}:{Service API Secret}' \
-H 'Content-type: application/json' \
-d '{"parameters":"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJraWQi...&client_id={Client ID}&response_type=code&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb&scope=myscope&code_challenge=E9Melhoa...&code_challenge_method=S256"}'
curl -s -X POST https://api.authlete.com/api/pushed_auth_req \
-H 'Bearer {Authlete API Access Token}'
-H 'Content-type: application/json' \
-d '{"parameters":"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJraWQi...&client_id={Client ID}&response_type=code&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb&scope=myscope&code_challenge=E9Melhoa...&code_challenge_method=S256"}'

A successful response from the API contains a request URI. The response would look like below.

{
  "resultCode": "A245001",
  "resultMessage": "[A245001] Successfully registered a request object for client ({Client ID}), URI is urn:ietf:params:oauth:request_uri:QbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg.",
  "action": "CREATED",
  "requestUri": "urn:ietf:params:oauth:request_uri:QbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg",
  "responseContent": "{\"expires_in\":600,\"request_uri\":\"urn:ietf:params:oauth:request_uri:QbGjdHSfZi_m6W_ldrqjZhmYjQo3QcQOd3Hx2RcGODg\"}"
}

2. /auth/authorization API

After obtaining a request URI in step 1, the client sends an authorization request, including the request URI, to the authorization endpoint of the authorization server. The request appears as below.

GET /authorization?client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:6uOtgZjBYPPspJLf630c8iJfi4xql_G3R7MKMjgTxRk HTTP/1.1
Host: server.example.com

When the authorization server receives the request from the client, the authorization sever calls Authlete /auth/authorization API. Below is a curl command that simulates a request from the authorization server to Authlete /auth/authorization API.

curl -s -X POST https://api.authlete.com/api/auth/authorization \
-u '{Service API Key}:{Service API Secret}' \
-H 'Content-type: application/json' \
-d '{"parameters":"client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:6uOtgZjBYPPspJLf630c8iJfi4xql_G3R7MKMjgTxRk"}'
curl -s -X POST https://api.authlete.com/api/auth/authorization \
-H 'Bearer {Authlete API Access Token}'
-H 'Content-type: application/json' \
-d '{"parameters":"client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:6uOtgZjBYPPspJLf630c8iJfi4xql_G3R7MKMjgTxRk"}'

A successful response from the API would be like below.

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

3. /auth/authorization/issue API

After the authorization server receives a successful response from /auth/authorization API, the end-user authorizes/denies the client in the browser. The authorization result is then conveyed to the authorization server and the authorization server calls Authlete /auth/authorization/issue API with the result. Below is a curl command that simulates a request from the authorization server to Authlete /auth/authorization/issue API.

curl -s -X POST https://api.authlete.com/api/auth/authorization/issue \
-u '{Service API Key}:{Service API Secret}' \
-H 'Content-type: application/json' \
-d '{"ticket":"CBKnPeMO...","subject":"john","result":"AUTHORIZED"}'
curl -s -X POST https://api.authlete.com/api/auth/authorization/issue \
-H 'Bearer {Authlete API Access Token}'
-H 'Content-type: application/json' \
-d '{"ticket":"CBKnPeMO...","subject":"john","result":"AUTHORIZED"}'

A successful response from the API would be like below.

{
  "type": "authorizationIssueResponse",
  "resultCode": "A040001",
  "resultMessage": "[A040001] The authorization request was processed successfully.",
  "authorizationCode": "smseP17u..."
  ...
}

4. /auth/token API

After obtaining an authorization code in step 3, the client sends a token request, including the authorization code, to the token endpoint of the authorization server. Similar to the requirement at the pushed authorization request endpoint, the client must authenticate itself using either MTLS or private_key_jwt at the token endpoint. In this simulation, we use private_key_jwt for client authentication.

Additionally, access tokens must be sender-constrained with MTLS or DPoP, according to FAPI2 Security profile, 5.3.1.1. General Requirements and FAPI2 Security profile, 5.3.2.1. General Requirements:

“FAPI2 Security profile, 5.3.1.1. General Requirements”

Authorization servers

4. shall only issue sender-constrained access tokens,

5. shall use one of the following methods for sender-constrained access tokens:

- MTLS as described in [RFC8705]

- DPoP as described in [I-D.ietf-oauth-dpop]

“FAPI2 Security profile, 5.3.2.1. General Requirements”

For the Authorization Code flow, Clients

1. shall support sender-constrained access tokens using one of the following methods:

- MTLS as described in [RFC8705]

- DPoP as described in [I-D.ietf-oauth-dpop]

In this simulation, we use DPoP as the sender-constrained access token mechanism. Therefore, the client must present a DPoP proof JWT for the authorization server to obtain a sender-constrained access token.

Note that DPoP proof JWT must be signed with PS256, ES256 or EdDSA, according to FAPI2 Security Profile, 5.4. Cryptography and Secrets.

FAPI2 Security profile, 5.4. Cryptography and Secrets

Authorization Servers, Clients, and Resource Servers when creating or processing JWTs shall

2. use PS256, ES256, or EdDSA (using the Ed25519 subtype) algorithms

Taking the requirements above into account, the token request would look like as below.

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAi...

client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGci...
&client_id=2990629246
&code=smseP17u...
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&grant_type=authorization_code
&code_verifier=ErRt0wrt...

After the token endpoint receives a request from the client, the authorization server calls Authlete /auth/token API. Below is a curl command that simulates a request from the authorization server to Authlete /auth/token API.

curl -s -X POST https://api.authlete.com/auth/token \
-u '{Service API Key}:{Service API Secret}' \
-H 'Content-type: application/json' \
-d '{"parameters":"client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=eyJraWQi...&client_id={Client ID}&code=smseP17u...&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&grant_type=authorization_code&code_verifier=ErRt0wrt...","dpop":"eyJ0eXAi..."}'
curl -s -X POST https://api.authlete.com/auth/token \
-H 'Bearer {Authlete API Access Token}'
-H 'Content-type: application/json' \
-d '{"parameters":"client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=eyJraWQi...&client_id={Client ID}&code=smseP17u...&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&grant_type=authorization_code&code_verifier=ErRt0wrt...","dpop":"eyJ0eXAi..."}'

A successful response from the API would be like below.

{
  "resultCode": "A050001",
  "resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
  "accessToken": "7i9xPkbk...",
  ...
}

5. /auth/introspection API

After completing all the steps above, the client gets an access token and can access a resource server’s endpoint with the access token like below.

GET /api/sample HTTP/1.1
Authorization: DPoP 7i9xPkbk...
DPoP: eyJ0eXAi...
Host: resource.example.com

Note that the client needs to present a DPoP proof JWT to the resource server’s endpoint along with the access token.

When the resource server receives the request from the client, it calls Authlete /auth/introspection API to verify the access token. Below is a curl command that simulates a request from the resource server to Authlete /auth/introspection API.

curl -s -X POST https://api.authlete.com/api/auth/introspection \
-u '{Service API Key}:{Service API Secret}' \
-H 'Content-type: application/json' \
-d '{"token":"7i9xPkbk...","dpop":"eyJ0eXAi...","htm":"GET","htu":"https://resource.example.com/api/sample"}'
curl -s -X POST https://api.authlete.com/api/auth/introspection \
-H 'Bearer {Authlete API Access Token}'
-H 'Content-type: application/json' \
-d '{"token":"7i9xPkbk...","dpop":"eyJ0eXAi...","htm":"GET","htu":"https://resource.example.com/api/sample"}'

A successful response from the API would be like below.

{
  "resultCode": "A056001",
  "resultMessage": "[A056001] The access token is valid.",
  "action": "OK",
  ...
}