Implementing FAPI 2.0 Security Profile with Authlete

Overview

This article explains how to implement FAPI 2.0 Security Profile (Final) using Authlete. Since FAPI 2.0 Security Profile places emphasis on Authorization Code Flow, the following sections provide guidance on configuring services and clients for this flow and demonstrate it through actual API calls.

Preconditions

We assume the following preconditions for the upcoming steps:

  • private_key_jwt is used for client authentication (at the pushed authorization endpoint & the token endpoint).
  • DPoP is used for sender-constrained access tokens.

Service Settings

Configure your service as follows.

☑️ Basic > Token Issuer Identifier

Set the authorization server’s issuer identifier.

☑️ Basic > Supported Service Profile

Include FAPI.

☑️ Authorization > Supported Grant Types

Include AUTHORIZATION_CODE.

☑️ Authorization > Supported Response Types

Include CODE.

☑️ Authorization > Authorization Endpoint > Authorization Endpoint URI

Set the authorization endpoint URL.

☑️ Authorization > Authorization Endpoint > iss Response Parameter

Select Included.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.2. Authorization endpoint flows”

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

☑️ Authorization > Authorization Endpoint > Variability of Loopback Redirection URI

Select Variable.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.2. Authorization endpoint flows”

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 Section 7.3 of [RFC8252];

☑️ Authorization > Token Endpoint > Token Endpoint URI

Set the token endpoint URL.

☑️ Authorization > Token Endpoint > Supported Client Authentication Methods

Include PRIVATE_KEY_JWT.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements”

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

- MTLS as specified in Section 2 of [RFC8705], or

- private_key_jwt as specified in Section 9 of [OIDC];

☑️ Authorization > Pushed Authorization Request Endpoint > Pushed Authorization Request Endpoint

Set the PAR endpoint URL.

☑️ Authorization > Pushed Authorization Request Endpoint > Pushed Authorization Request Duration

Set the duration (the expiration time for request URIs) to less than 600 seconds.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.2. Authorization endpoint flows” 12. shall issue pushed authorization requests request_uri with expires_in values of less than 600 seconds;

☑️ Token > Refresh Token > Refresh Token Continuous Use

Select Kept.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements”

9. shall not use refresh token rotation except in extraordinary circumstances (see Note 1 below);

NOTE 1: The use of refresh token rotation does not provide security benefits when used with confidential clients and sender-constrained access tokens. This specification prohibits the use of refresh token rotation for security reasons as it causes user experience degradation and operational issues whenever the client fails to store or receive the new refresh token and has no option to retry.

However, as refresh token rotation may be required from time to time for infrastructure migration or similar extraordinary circumstances, this specification allows it, provided that authorization servers offer clients the time-limited option to retry with the old refresh token in case of failure. Implementers need to consider a secure mechanism for clients to recover from a loss of a new refresh token on issue. The details of this mechanism are outside the scope of this specification.

☑️ Token > Scope > Supported Scopes

Create a scope named myscope and add a scope attribute with the key fapi2 and the value sp.

☑️ Basic Settings > Issuer Identifier

Set the authorization server’s issuer identifier.

☑️ Basic Settings > FAPI Profile

Enable this setting.

☑️ Endpoints > Global Settings > Supported Grant Types

Include AUTHORIZATION_CODE.

☑️ Endpoints > Global Settings > Supported Response Types

Include CODE.

☑️ Endpoints > Authorization > Authorization Endpoint URL

Set the authorization endpoint URL.

☑️ Endpoints > Authorization > Issuer Identification Response Parameter

Disable this setting.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.2. Authorization endpoint flows”

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

☑️ Endpoints > Authorization > Loopback Redirection URI

Enable this setting.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.2. Authorization endpoint flows”

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 Section 7.3 of [RFC8252];

☑️ Endpoints > Token > Token Endpoint URI

Set the token endpoint URI.

☑️ Endpoints > Token > Supported Client Authentication Methods

Include PRIVATE_KEY_JWT.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements”

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

- MTLS as specified in Section 2 of [RFC8705], or

- private_key_jwt as specified in Section 9 of [OIDC];

☑️ Endpoints > General > Pushed Authorization Request (PAR)

  • Set PAR Endpoint URL.
  • Set PAR Endpoint Duration (the expiration time for request URIs). The duration value must be less than 600 seconds.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.2. Authorization endpoint flows” 12. shall issue pushed authorization requests request_uri with expires_in values of less than 600 seconds;

☑️ Tokens and Claims > Refresh Token > Refresh Token Rotation

Disable Refresh Token Rotation.

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements”

9. shall not use refresh token rotation except in extraordinary circumstances (see Note 1 below);

NOTE 1: The use of refresh token rotation does not provide security benefits when used with confidential clients and sender-constrained access tokens. This specification prohibits the use of refresh token rotation for security reasons as it causes user experience degradation and operational issues whenever the client fails to store or receive the new refresh token and has no option to retry.

However, as refresh token rotation may be required from time to time for infrastructure migration or similar extraordinary circumstances, this specification allows it, provided that authorization servers offer clients the time-limited option to retry with the old refresh token in case of failure. Implementers need to consider a secure mechanism for clients to recover from a loss of a new refresh token on issue. The details of this mechanism are outside the scope of this specification.

☑️ Tokens and Claims > Advanced > Supported Scopes

Create a scope named myscope and add a scope attribute with the key fapi2 and the value sp.

Client Configurations

Configure your client as follows.

☑️ Basic > Client Type

Select CONFIDENTIAL.

☑️ Authorization > Grant Types

Include AUTHORIZATION_CODE.

☑️ Authorization > Response Types

Include CODE.

☑️ Authorization > Redirect URIs

Register at least one redirect URI starting with https.

☑️ Authorization > Token Endpoint > Client Authentication Method

Select PRIVATE_KEY_JWT.

“FAPI 2.0 Security Profile, 5.3.3. Requirements for clients, 5.3.3.1. General requirements”

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

- MTLS as specified in Section 2 of [RFC8705],

- private_key_jwt as specified in Section 9 of [OIDC];

☑️ Authorization > Token Endpoint > Assertion Signature Algorithm

Select ES256.

“FAPI 2.0 Security Profile, 5.4. Cryptography and secrets, 5.4.1. General requirements”

1. Authorization servers, clients, and resource servers when creating or processing JWTs shall

  1. adhere to [RFC8725];

  2. use PS256, ES256, or EdDSA (using the Ed25519 variant) algorithms; and

  3. not use or accept the none algorithm.

☑️ JWK Set > JWK Set Content

Set a JWK set. Since we use private_key_jwt for client authentication, the JWK set must include a signing key for client assertions. Note that keys contained in the JWK set must satisfy the following requirements.

“FAPI 2.0 Security Profile, 5.4. Cryptography and secrets, 5.4.1. General requirements”

3. RSA keys shall have a minimum length of 2048 bits.

4. Elliptic curve keys shall have a minimum length of 224 bits.

☑️ Basic Settings > Client Type

Select CONFIDENTIAL.

☑️ Endpoints > Global Settings > Supported Grant Types

Include AUTHORIZATION_CODE.

☑️ Endpoints > Global Settings > Supported Response Types

Include CODE.

☑️ Endpoints > Global Settings > Redirect URIs

Register at least one redirect URI starting with https.

☑️ Endpoints > Token > Client Authentication Method

Select PRIVATE_KEY_JWT.

“FAPI 2.0 Security Profile, 5.3.3. Requirements for clients, 5.3.3.1. General requirements”

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

- MTLS as specified in Section 2 of [RFC8705],

- private_key_jwt as specified in Section 9 of [OIDC];

☑️ Authorization > Token Endpoint > Assertion Signature Algorithm

Select ES256.

“FAPI 2.0 Security Profile, 5.4. Cryptography and secrets, 5.4.1. General requirements”

1. Authorization servers, clients, and resource servers when creating or processing JWTs shall

  1. adhere to [RFC8725];

  2. use PS256, ES256, or EdDSA (using the Ed25519 variant) algorithms; and

  3. not use or accept the none algorithm.

☑️ Key Management > JWK Set > JWK Set Content

Set a JWK set. Since we use private_key_jwt for client authentication, the JWK set must include a client assertion signing key. Note that keys contained in the JWK set must satisfy the following requirements.

“FAPI 2.0 Security Profile, 5.4. Cryptography and secrets, 5.4.1. General requirements”

3. RSA keys shall have a minimum length of 2048 bits.

4. Elliptic curve keys shall have a minimum length of 224 bits.

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 Authorization Request & Response

Suppose that a FAPI2 Security Profile compliant client sends a valid request to the pushed authorization request endpoint of the authorization server. The client and the request should meet the following requirements.

☑️ Client Authentication

The client uses private_key_jwt for client authentication at the pushed authentication request endpoint (as configured above).

☑️ Scope

The scope request parameter includes myscope, which is linked to an attribute fapi2=sp as configured above.

☑️ Response Type

The response_type request parameter is set to code.

“FAPI 2.0 Security Profile, 5.3.3. Requirements for clients, 5.3.3.2. Authorization code flow”

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

☑️ PKCE

The code_challenge request parameter must be set and the code_challenge_method request parameter must be set to S256.

“FAPI 2.0 Security Profile, 5.3.3. Requirements for clients, 5.3.3.2. Authorization code flow”

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

☑️ Redirect URI

The redirect_uri request parameter must be set and it must be a URI starting with https. In this simulation, we use the redirect URI registered in the client settings.

“FAPI2 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.2. Authorization endpoint flows”

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 Section 7.3 of [RFC8252];

Taking all of the requirements above into account, the request sent by the client to the pushed authorization endpoint would look like the following.

POST /oauth/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:QbG....",
  "action": "CREATED",
  "requestUri": "urn:ietf:params:oauth:request_uri:QbG...",
  "responseContent": "{\"expires_in\":600,\"request_uri\":\"urn:ietf:params:oauth:request_uri:QbG...\"}"
}

2. Authorization Request & Response

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 /oauth/authorize?client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:QbG... 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:QbG..."}'
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:QbG..."}'

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 = {Service API Key}) for the authorization request from the client (ID = {Client ID}). [response_type=code, openid=false]",
  "ticket": "CBKnPeMO...",
  ...
}

After the authorization server receives a successful response from /auth/authorization API, the end-user authorizes/denies the client’s request 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. Token Request & Response

After obtaining an authorization code in step 3, the client sends a request, including the authorization code, to the token endpoint. Similar to the requirement at the pushed authorization request endpoint, the token endpoint authenticates the client using private_key_jwt. Also, we assume that the authorization server issues access tokens that are sender-constrained with DPoP based on the following requirements:

“FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements”

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 [RFC9449];

“FAPI 2.0 Security Profile, 5.3.3. Requirements for clients, 5.3.3.1. General requirements”

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

- MTLS as described in [RFC8705],

- DPoP as described in [RFC9449];

Therefore, the client must present a DPoP proof JWT for the authorization server to obtain a sender-constrained access token.

Note that DPoP proof JWTs must be signed with PS256, ES256 or EdDSA.

“FAPI 2.0 Security Profile, 5.4. Cryptography and secrets, 5.4.1. General requirements”

1. Authorization servers, clients, and resource servers when creating or processing JWTs shall

  1. adhere to [RFC8725];

  2. use PS256, ES256, or EdDSA (using the Ed25519 variant) algorithms; and

  3. not use or accept the none algorithm.

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

POST /oauth/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={Client ID}
&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. Protected Resource

After completing all the steps above, the client gets an access token and can access a protected resource endpoint of the resource server with the obtained access token as follows.

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, the resource server 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",
  ...
}