Table of Contents
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.
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).Configure your service as follows.
Set the authorization server’s issuer identifier.
Include FAPI
.
Include AUTHORIZATION_CODE
.
Include CODE
.
Set the authorization endpoint URL.
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];
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];
Set the token endpoint URL.
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];
Set the PAR endpoint URL.
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;
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.
Create a scope named myscope
and add a scope attribute with the key fapi2
and the value sp
.
Set the authorization server’s issuer identifier.
Enable this setting.
Include AUTHORIZATION_CODE
.
Include CODE
.
Set the authorization endpoint URL.
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];
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];
Set the token endpoint URI.
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];
“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;
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.
Create a scope named myscope
and add a scope attribute with the key fapi2
and the value sp
.
Configure your client as follows.
Select CONFIDENTIAL
.
Include AUTHORIZATION_CODE
.
Include CODE
.
Register at least one redirect URI starting with https
.
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];
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
adhere to [RFC8725];
use
PS256
,ES256
, orEdDSA
(using theEd25519
variant) algorithms; andnot use or accept the none algorithm.
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.
Select CONFIDENTIAL
.
Include AUTHORIZATION_CODE
.
Include CODE
.
Register at least one redirect URI starting with https
.
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];
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
adhere to [RFC8725];
use
PS256
,ES256
, orEdDSA
(using theEd25519
variant) algorithms; andnot use or accept the none algorithm.
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.
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.
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.
The client uses private_key_jwt
for client authentication at the pushed authentication request endpoint (as configured above).
The scope
request parameter includes myscope
, which is linked to an attribute fapi2=sp
as configured above.
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];
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
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 requests8. 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...\"}"
}
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..."
...
}
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
adhere to [RFC8725];
use
PS256
,ES256
, orEdDSA
(using theEd25519
variant) algorithms; andnot 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...",
...
}
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",
...
}