Table of Contents
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.
The following sections explain how to setup the service and the client and demonstrate this flow through actual API calls.
At the service owner console, configure the service as follows.
Set your authorization server’s issuer identifier.
Include AUTHORIZATION_CODE
.
Include CODE
.
Include FAPI
.
iss
Response ParameterSelect 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]
Set your authorization server’s token endpoint URI.
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
.
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
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.
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"
}
]
...
}
Set a JWK set containing required JWKs (e.g. JWT access token sign key).
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).
At the client developer console, configure the client as follows.
Select CONFIDENTIAL
.
Include AUTHORIZATION_CODE
.
Include CODE
.
Create at least one redirect URI.
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
.
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
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
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).
…
Set a JWK set containing required JWKs (e.g. client assertion sign key).
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).
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 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\"}"
}
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...",
...
}
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..."
...
}
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...",
...
}
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",
...
}