> ## Agent Instructions
> ### Authlete versions
> Authlete 3.0 is the current version. Unless the user says they use another version, answer for Authlete 3.0 using the 3.x documentation.
> Documentation for Authlete 2.x (pages under `/v2/` and `/ja/v2/`) is for the legacy version. Refer to it only when the user is on Authlete 2.x.
>
> ### Authlete architecture
> Authlete is a backend engine for OAuth 2.0 and OpenID Connect processing. End users and client applications do not call it directly; the service provider's authorization server and API server call Authlete's APIs. Authlete handles protocol processing and token, client, and service management, while the service provider owns the OAuth/OIDC endpoints and user authentication.
> See [Architecture](https://developers.authlete.com/get-started/introduction-to-authlete/architecture.md).
>
> ### Concepts
> If the user is new to Authlete, read these pages first:
> - [Request and Response](https://developers.authlete.com/get-started/concepts/request-and-response.md): Core API requests pass the client's request parameters as-is, and responses return `resultCode`, `action`, and `responseContent`.
> - [Action Handling](https://developers.authlete.com/get-started/concepts/action-handling.md): Branch on `action`, never on `resultCode`, and map each action to the HTTP response your server returns.
> - [Two-Step API Calls](https://developers.authlete.com/get-started/concepts/two-step-api-calls.md): Actions such as `INTERACTION` are completed with a second call (`/auth/authorization/issue` or `/auth/authorization/fail`) linked by a `ticket`.
> - [Management API Overview](https://developers.authlete.com/get-started/concepts/overview.md): The Management API configures services, manages clients, and operates issued tokens, separately from the protocol flows handled by the Core API.
> - [API Authentication](https://developers.authlete.com/get-started/concepts/authentication.md): Authlete APIs use bearer tokens: a Service Access Token (one service) or an Organization Token (the whole organization).
# Get Audit Log Event Types
Source: https://developers.authlete.com/api-reference/audit-logs/get-audit-log-event-types
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/audit/types
Returns the list of available audit log event types for this environment.
Accessible with a user access token or an organization token that has the
`view_audit_log` permission (e.g. a token created with the `admin` or
`audit_reader` preset).
This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`),
not on the regional API clusters.
# Get Audit Logs
Source: https://developers.authlete.com/api-reference/audit-logs/get-audit-logs
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/audit/entries
Retrieve audit logs as a cursor-paginated list. Results can be filtered by time range, event type, and organization.
The response contains a page of roughly `limit` entries together with a `nextCursor` value.
Entries sharing the same timestamp are never split across pages, so a page may occasionally
contain more than `limit` entries. To fetch the next page, repeat the request with the `cursor`
parameter set to the `nextCursor` value from the previous response, keeping the other filter
parameters unchanged. `nextCursor` is absent on the last page. If both `before` and `cursor`
are provided, `cursor` takes precedence.
This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`),
not on the regional API clusters.
# Fail Authorization Request
Source: https://developers.authlete.com/api-reference/authorization-endpoint/fail-authorization-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/authorization/fail
This API generates a content of an error authorization response that the authorization server implementation returns to the client application.
This API is supposed to be called from within the implementation of the authorization endpoint of the service
in order to generate an error response to the client application.
The description of the `/auth/authorization` API describes the timing when this API should be called.
The response from `/auth/authorization/fail` API has some parameters.
Among them, it is `action` parameter that the authorization server implementation should check first because
it denotes the next action that the authorization server implementation should take.
According to the value of `action`, the authorization server implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error". Authlete recommends `application/json` as the content type.
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application since
"500 Internal Server Error" is not required by OAuth 2.0.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the ticket is no longer valid (deleted
or expired) and that the reason of the invalidity was probably due to the end-user's too-delayed
response to the authorization UI.
A response with HTTP status of "400 Bad Request" should be returned to the client application and
Authlete recommends `application/json` as the content type.
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application since
"400 Bad Request" is not required by OAuth 2.0.
## LOCATION
When the value of `action` is `LOCATION`, it means that the response to the client application must
be "302 Found" with Location header.
The parameter responseContent contains a redirect URI with (1) an authorization code, an ID token
and/or an access token (on success) or (2) an error code (on failure), so it can be used as the
value of `Location` header.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 302 Found
Location: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## FORM
When the value of `action` is `FORM`, it means that the response to the client application must be 200 OK
with an HTML which triggers redirection by JavaScript.
This happens when the authorization request from the client application contained `response_mode=form_post`.
The value of `responseContent` is an HTML which can be used as the entity body of the response.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Get Ticket Information
Source: https://developers.authlete.com/api-reference/authorization-endpoint/get-ticket-information
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/authorization/ticket/info
# Issue Authorization Response
Source: https://developers.authlete.com/api-reference/authorization-endpoint/issue-authorization-response
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/authorization/issue
This API parses request parameters of an authorization request and returns necessary data for the authorization server implementation to process the authorization request further.
This API is supposed to be called from within the implementation of the authorization endpoint of
the service in order to generate a successful response to the client application.
The description of the `/auth/authorization` API describes the timing when this API should be called
and the meaning of request parameters. See \[ISSUE] in `NO_INTERACTION`.
The response from `/auth/authorization/issue` API has some parameters.
Among them, it is `action` parameter that the authorization server implementation should check first
because it denotes the next action that the authorization server implementation should take.
According to the value of `action`, the authorization server implementation must take the steps
described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error".
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application since
"500 Internal Server Error" is not required by OAuth 2.0.
## BAD\_REQUEST
When the value of "action" is `BAD_REQUEST`, it means that the ticket is no longer valid (deleted
or expired) and that the reason of the invalidity was probably due to the end-user's too-delayed
response to the authorization UI.
The HTTP status of the response returned to the client application should be "400 Bad Request"
and the content type should be `application/json` although OAuth 2.0 specification does not mention
the format of the error response.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application since
"400 Bad Request" is not required by OAuth 2.0.
## LOCATION
When the value of `action` is `LOCATION`, it means that the response to the client application
should be "302 Found" with `Location` header.
The value of `responseContent` is a redirect URI which contains (1) an authorization code, an ID
token and/or an access token (on success) or (2) an error code (on failure), so it can be used as
the value of `Location` header.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 302 Found
Location: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## FORM
When the value of `action` is `FORM`, it means that the response to the client application should
be "200 OK" with an HTML which triggers redirection by JavaScript. This happens when the authorization
request from the client contains `response_mode=form_post` request parameter.
The value of `responseContent` is an HTML which satisfies the requirements of `response_mode=form_post`,
so it can be used as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Process Authorization Request
Source: https://developers.authlete.com/api-reference/authorization-endpoint/process-authorization-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/authorization
This API parses request parameters of an authorization request and returns necessary data for the authorization server implementation to process the authorization request further.
This API is supposed to be called from within the implementation of the authorization endpoint of
the service. The endpoint implementation must extract the request parameters from the authorization
request from the client application and pass them as the value of parameters request parameter for
Authlete's `/auth/authorization` API.
The value of `parameters` is either (1) the entire query string when the HTTP method of the request
from the client application is `GET` or (2) the entire entity body (which is formatted in
`application/x-www-form-urlencoded`) when the HTTP method of the request from the client application
is `POST`.
The following code snippet is an example in JAX-RS showing how to extract request parameters from
the authorization request.
```java
@GET
public Response get(@Context UriInfo uriInfo)
{
// The query parameters of the authorization request.
String parameters = uriInfo.getRequestUri().getQuery();
......
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response post(String parameters)
{
// 'parameters' is the entity body of the authorization request.
......
}
```
The endpoint implementation does not have to parse the request parameters from the client application
because Authlete's `/auth/authorization` API does it.
The response from `/auth/authorization` API has various parameters. Among them, it is `action`
parameter that the authorization server implementation should check first because it denotes the
next action that the authorization server implementation should take. According to the value of
`action`, the service implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error". Authlete recommends `application/json` as the content
type although OAuth 2.0 specification does not mention the format of the error response when the
redirect URI is not usable.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application
since "500 Internal Server Error" is not required by OAuth 2.0.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
is invalid.
A response with HTTP status of "400 Bad Request" should be returned to the client application and
Authlete recommends `application/json` as the content type although OAuth 2.0 specification does
not mention the format of the error response when the redirect URI is not usable.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application since
"400 Bad Request" is not required by OAuth 2.0.
## LOCATION
When the value of `action` is `LOCATION`, it means that the request from the client application
is invalid but the redirect URI
to which the error should be reported has been determined.
A response with HTTP status of "302 Found" must be returned to the client application with `Location`
header which has a redirect URI with error parameter.
The value of `responseContent` is a redirect URI with `error` parameter, so it can be used as the
value of `Location` header.
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 302 Found
Location: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## FORM
When the value of `action` is `FORM`, it means that the request from the client application is
invalid but the redirect URI to which the error should be reported has been determined, and that
the authorization request contains `response_mode=form_post` as is defined in [OAuth 2.0 Form Post
Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html).
The HTTP status of the response returned to the client application should be "200 OK" and the
content type should be `text/html;charset=UTF-8`.
The value of `responseContent` is an HTML which can be used as the entity body of the response.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## NO\_INTERACTION
When the value of `action` is `NO_INTERACTION`, it means that the request from the client application
has no problem and requires the service to process the request without displaying any user interface
pages for authentication or consent. This case happens when the authorization request contains
`prompt=none`.
The service must follow the steps described below.
**\[1] END-USER AUTHENTICATION**
Check whether an end-user has already logged in. If an end-user has logged in, go to the next step (\[MAX\_AGE]).
Otherwise, call Authlete's `/auth/authorization/fail` API with `reason=NOT_LOGGED_IN` and use the response from
the API to generate a response to the client application.
**\[2] MAX AGE**
Get the value of `maxAge` parameter from the `/auth/authorization` API response. The value represents
the maximum authentication age which has come from `max_age` request parameter or `defaultMaxAge`
configuration parameter of the client application. If the value is `0`, go to the next step (\[SUBJECT]).
Otherwise, follow the sub steps described below.
* (i) Get the time at which the end-user was authenticated. Note that this value is not managed by Authlete,
meaning that it is expected that the service implementation manages the value. If the service implementation
does not manage authentication time of end-users, call Authlete's `/auth/authorization/fail` API
with `reason=MAX_AGE_NOT_SUPPORTED` and use the API response to generate a response to the client
application.
* (ii) Add the value of the maximum authentication age (which is represented in seconds) to the authentication
time. The calculated value is the expiration time.
* (iii) Check whether the calculated value is equal to or greater than the current time. If this condition
is satisfied, go to the next step (\[SUBJECT]). Otherwise, call Authlete's `/auth/authorization/fail`
API with `reason=EXCEEDS_MAX_AGE` and use the API response to generate a response to the client
application.
**\[3] SUBJECT**
Get the value of `subject` from the `/auth/authorization` API response. The value represents an
end-user who the client application expects to grant authorization. If the value is `null`, go to
the next step (\[ACRs]). Otherwise, follow the sub steps described below.
* (i) Compare the value of the requested subject to the current end-user.
* (ii) If they are equal, go to the next step (\[ACRs]). If they are not equal, call Authlete's
`/auth/authorization/fail` API with `reason=DIFFERENT_SUBJECT` and use the response from the API
to generate a response to the client application.
**\[4] ACRs**
Get the value of `acrs` from the `/auth/authorization` API response. The value represents a list
of ACRs (Authentication Context Class References) and comes from (1) acr claim in `claims` request
parameter, (2) `acr_values` request parameter, or (3) `default_acr_values` configuration parameter
of the client application.
It is ensured that all the ACRs in acrs are supported by the authorization server implementation.
In other words, it is ensured that all the ACRs are listed in `acr_values_supported` configuration
parameter of the authorization server.
If the value of ACRs is `null`, go to the next step (\[ISSUE]). Otherwise, follow the sub steps
described below.
* (i) Get the ACR performed for the authentication of the current end-user. Note that this value is
managed not by Authlete but by the authorization server implementation. (If the authorization server
implementation cannot handle ACRs, it should not have listed ACRs as `acr_values_supported`.)
* (ii) Compare the ACR value obtained in the above step to each element in the ACR array (`acrs`)
in the listed order.
* (iii) If the ACR value was found in the array, (= the ACR performed for the authentication of the
current end-user did not match any one of the ACRs requested by the client application), check
whether one of the requested ACRs must be satisfied or not using `acrEssential` parameter in the
`/auth/authorization` API response. If the value of `acrEssential` parameter is `true`, call Authlete's
`/auth/authorization/fail` API with `reason=ACR_NOT_SATISFIED` and use the response from the API
to generate a response to the client application. Otherwise, go to the next step (\[SCOPES]).
**\[5] SCOPES**
Get the value of `scopes` from the `/auth/authorization` API response. If the array contains a
scope which has not been granted to the client application by the end-user in the past, call
Authlete's `/auth/authorization/fail` API with `reason=CONSENT_REQUIRED` and use the response from
the API to generate a response to the client application. Otherwise, go to the next step (\[RESOURCES]).
Note that Authlete provides APIs to manage records of granted scopes (`/api/client/granted_scopes/*`
APIs), which is only available in a dedicated/onpremise Authlete server (contact [sales@authlete.com](mailto:sales@authlete.com)
for details).
**\[6] DYNAMIC SCOPES**
Get the value of `dynamicScopes` from the `/auth/authorization` API response. If the array contains
a scope which has not been granted to the client application by the end-user in the past, call
Authlete's `/auth/authorization/fail` API with `reason=CONSENT_REQUIRED` and use the response from
the API to generate a response to the client application. Otherwise, go to the next step (\[RESOURCES]).
Note that Authlete provides APIs to manage records of granted scopes (`/api/client/granted_scopes/*`
APIs) but dynamic scopes are not remembered as granted scopes.
**\[7] RESOURCES**
Get the value of `resources` from the `/auth/authorization` API response. The array represents
the values of the `resource` request parameters. If you want to reject the request, call Authlete's
`/auth/authorization/fail` API with `reason=INVALID_TARGET` and use the response from the API to
generate a response to the client application. Otherwise, go to the next step (\[ISSUE]).
See "Resource Indicators for OAuth 2.0" for details.
**\[8] ISSUE**
If all the above steps succeeded, the last step is to issue an authorization code, an ID token
and/or an access token. (There is a special case, though. In the case of `response_type=none`,
nothing is issued.) It can be performed by calling Authlete's `/auth/authorization/issue` API.
The API requires the following parameters. Prepare these parameters and call `/auth/authorization/issue`
API and use the response to generate a response to the client application.
* `ticket` (required)
This parameter represents a ticket which is exchanged with tokens at `/auth/authorization/issue`.
Use the value of `ticket` contained in the `/auth/authorization` API response.
* `subject` (required)
This parameter represents the unique identifier of the current end-user. It is often called "user ID"
and it may or may not be visible to the user. In any case, it is a number or a string assigned
to an end-user by the authorization server implementation. Authlete does not care about the format
of the value of subject, but it must consist of only ASCII letters and its length must not exceed 100.
When the value of `subject` parameter in the /auth/authorization API response is not `null`,
it is necessarily identical to the value of `subject` parameter in the `/auth/authorization/issue`
API request.
The value of this parameter will be embedded in an ID token as the value of `sub` claim. When
the value of `subject_type` configuration parameter of the client application is `PAIRWISE`,
the value of sub claim is different from the value specified by this parameter, See [8. Subject
Identifier Types](https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes) of OpenID
Connect Core 1.0 for details about subject types.
You can use the `sub` request parameter to adjust the value of the `sub` claim in an ID token.
See the description of the `sub` request parameter for details.
* `authTime` (optional)
This parameter represents the time when the end-user authentication occurred. Its value is the
number of seconds from `1970-01-01`. The value of this parameter will be embedded in an ID token
as the value of `auth_time` claim.
* `acr` (optional)
This parameter represents the ACR (Authentication Context Class Reference) which the authentication
of the end-user satisfies. When `acrs` in the `/auth/authorization` API response is a non-empty
array and the value of `acrEssential` is `true`, the value of this parameter must be one of the
array elements. Otherwise, even `null` is allowed. The value of this parameter will be embedded
in an ID token as the value of `acr` claim.
* `claims` (optional)
This parameter represents claims of the end-user. "Claims" here are pieces of information about
the end-user such as `"name"`, `"email"` and `"birthdate"`. The authorization server implementation
is required to gather claims of the end-user, format the claim values into JSON and set the JSON
string as the value of this parameter.
The claims which the authorization server implementation is required to gather are listed in
`claims` parameter in the `/auth/authorization` API response.
For example, if claims parameter lists `"name"`, `"email"` and `"birthdate"`, the value of this
parameter should look like the following.
```json
{
"name": "John Smith",
"email": "john@example.com",
"birthdate": "1974-05-06"
}
```
`claimsLocales` parameter in the `/auth/authorization` API response lists the end-user's preferred
languages and scripts, ordered by preference. When `claimsLocales` parameter is a non-empty array,
its elements should be taken into account when the authorization server implementation gathers
claim values. Especially, note the excerpt below from [5.2. Claims Languages and Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts)
of OpenID Connect Core 1.0.
> When the OP determines, either through the `claims_locales` parameter, or by other means, that
> the End-User and Client are requesting Claims in only one set of languages and scripts, it is
> RECOMMENDED that OPs return Claims without language tags when they employ this language and script.
> It is also RECOMMENDED that Clients be written in a manner that they can handle and utilize Claims
> using language tags.
> If `claims` parameter in the `/auth/authorization` API response is `null` or an empty array,
> the value of this parameter should be `null`.
> See [5.1. Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)
> of OpenID Connect core 1.0 for claim names and their value formats. Note (1) that the authorization
> server implementation support its special claims ([5.1.2. Additional Claims](https://openid.net/specs/openid-connect-core-1_0.html#AdditionalClaims))
> and (2) that claim names may be followed by a language tag ([5.2. Claims Languages and Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts)).
> Read the specification of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
> for details.
> The claim values in this parameter will be embedded in an ID token.
> Note that `idTokenClaims` parameter is available in the `/auth/authorization` API response.
> The parameter has the value of the `"id_token"` property in the `claims` request parameter or
> in the `"claims"` property in a request object. The value of this parameter should be considered
> when you prepare claim values.
* `properties` (optional)
Extra properties to associate with an access token and/or an authorization code that may be issued
by this request. Note that `properties` parameter is accepted only when `Content-Type` of the
request is `application/json`, so don't use `application/x-www-form-urlencoded` for details.
* `scopes` (optional)
Scopes to associate with an access token and/or an authorization code. If this parameter is `null`,
the scopes specified in the original authorization request from the client application are used.
In other cases, including the case of an empty array, the specified scopes will replace the original
scopes contained in the original authorization request.
Even scopes that are not included in the original authorization request can be specified. However,
as an exception, `openid` scope is ignored on the server side if it is not included in the original
request. It is because the existence of `openid` scope considerably changes the validation steps
and because adding `openid` triggers generation of an ID token (although the client application
has not requested it) and the behavior is a major violation against the specification.
If you add `offline_access` scope although it is not included in the original request, keep in
mind that the specification requires explicit consent from the user for the scope ([OpenID Connect
Core 1.0, 11. Offline Access](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess)).
When `offline_access` is included in the original request, the current implementation of Authlete's
`/auth/authorization` API checks whether the request has come along with `prompt` request parameter
and the value includes consent. However, note that the implementation of Authlete's `/auth/authorization/issue`
API does not perform such checking if `offline_access` scope is added via this `scopes` parameter.
* `sub` (optional)
The value of the `sub` claim in an ID token. If the value of this request parameter is not empty,
it is used as the value of the `sub` claim. Otherwise, the value of the `subject` request parameter
is used as the value of the `sub` claim. The main purpose of this parameter is to hide the actual
value of the subject from client applications.
Note that even if this `sub` parameter is not empty, the value of the subject request parameter
is used as the value of the subject which is associated with the access token.
## INTERACTION
When the value of `action` is `INTERACTION`, it means that the request from the client application
has no problem and requires the service to process the request with user interaction by an HTML form.
The purpose of the UI displayed to the end-user is to ask the end-user to grant authorization to
the client application. The items described below are some points which the service implementation
should take into account when it builds the UI.
**\[1] DISPLAY MODE**
The response from `/auth/authorization` API has `display` parameter. It is one of `PAGE` (default),
`POPUP`, `TOUCH` and `WAP` The meanings of the values are described in [3.1.2.1. Authentication
Request of OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
Basically, the authorization server implementation should display the UI which is suitable for the
display mode, but it is okay for the authorization server implementation to "attempt to detect the
capabilities of the User Agent and present an appropriate display".
It is ensured that the value of `display` is one of the supported display modes which are specified
by `supportedDisplays` configuration parameter of the service.
**\[2] UI LOCALE**
The response from `/auth/authorization` API has `uiLocales` parameter. It it is not `null`, it lists
language tag values (such as `fr-CA`, `ja-JP` and `en`) ordered by preference. The service implementation
should display the UI in one of the language listed in the parameter when possible. It is ensured
that language tags listed in `uiLocales` are contained in the list of supported UI locales which
are specified by `supportedUiLocales` configuration parameter of the service.
**\[3] CLIENT INFORMATION**
The authorization server implementation should show information about the client application to
the end-user. The information is embedded in `client` parameter in the response from `/auth/authorization`
API.
**\[4] SCOPES**
A client application requires authorization for specific permissions. In OAuth 2.0 specification,
"scope" is a technical term which represents a permission. `scopes` parameter in the response
from `/auth/authorization` API is a list of scopes requested by the client application. The service
implementation should show the end-user the scopes.
The authorization server implementation may choose not to show scopes to which the end-user has
given consent in the past. To put it the other way around, the authorization server implementation
may show only the scopes to which the end-user has not given consent yet. However, if the value
of `prompts` response parameter contains `CONSENT`, the authorization server implementation has
to obtain explicit consent from the end-user even if the end-user has given consent to all the
requested scopes in the past.
Note that Authlete provides APIs to manage records of granted scopes (`/api/client/granted_scopes/*`
APIs), but the APIs work only in the case the Authlete server you use is a dedicated Authlete server
(contact [sales@authlete.com](mailto:sales@authlete.com) for details). In other words, the APIs of the shared Authlete server
are disabled intentionally (in order to prevent garbage data from being accumulated) and they
return 403 Forbidden.
It is ensured that the values in `scopes` parameter are contained in the list of supported scopes
which are specified by `supportedScopes` configuration parameter of the service.
**\[5] DYNAMIC SCOPES**
The authorization request may include dynamic scopes. The list of recognized dynamic scopes are
accessible by getDynamicScopes() method. See the description of the [DynamicScope](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/DynamicScope.html)
class for details about dynamic scopes.
**\[6] AUTHORIZATION DETAILS**
The authorization server implementation should show the end-user "authorization details" if the
request includes it. The value of `authorization_details` parameter in the response is the content
of the `authorization_details` request parameter.
See "OAuth 2.0 Rich Authorization Requests" for details.
**\[7] PURPOSE**
The authorization server implementation must show the value of the `purpose` request parameter if
it supports [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html).
See [8. Transaction-specific Purpose](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.8)
in the specification for details.
Note that the value of `purpose` response parameter is the value of the purpose request parameter.
**\[7] END-USER AUTHENTICATION**
Necessarily, the end-user must be authenticated (= must login the service) before granting authorization
to the client application. Simply put, a login form is expected to be displayed for end-user authentication.
The service implementation must follow the steps described below to comply with OpenID Connect.
(Or just always show a login form if it's too much of a bother.)
* (i) Get the value of `prompts` response parameter. It corresponds to the value of the `prompt`
request parameter. Details of the request parameter are described in [3.1.2.1. Authentication
Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) of OpenID Connect Core 1.0.
* (ii) If the value of `prompts` parameter is `SELECT_ACCOUNT` display a form to let the end-user
select on of his/her accounts for login. If `subject` response parameter is not `null`, it is the
end-user ID that the client application expects, so the value should be used to determine the value
of the login ID. Note that a subject and a login ID are not necessarily equal. If the value of
`subject` response parameter is `null`, the value of `loginHint` response parameter should be referred
to as a hint to determine the value of the login ID. The value of `loginHint` response parameter
is simply the value of the `login_hint` request parameter.
* (iii) If the value of `prompts` response parameter contains `LOGIN`, display a form to urge the
end-user to login even if the end-user has already logged in. If the value of `subject` response
parameter is not `null`, it is the end-user ID that the client application expects, so the value
should be used to determine the value of the login ID. Note that a subject and a login ID are not
necessarily equal. If the value of `subject` response parameter is `null`, the value of `loginHint`
response parameter should be referred to as a hint to determine the value of the login ID. The value
of `loginHint` response parameter is simply the value of the `login_hint` request parameter.
* (iv) If the value of `prompts` response parameter does not contain `LOGIN`, the authorization server
implementation does not have to authenticate the end-user if all the conditions described below
are satisfied. If any one of the conditions is not satisfied, show a login form to authenticate
the end-user.
* An end-user has already logged in the service.
* The login ID of the current end-user matches the value of `subject` response parameter.
This check is required only when the value of `subject` response parameter is a non-null value.
* The max age, which is the number of seconds contained in `maxAge` response parameter,
has not passed since the current end-user logged in your service. This check is required only when
the value of `maxAge` response parameter is a non-zero value.
* If the authorization server implementation does not manage authentication time of end-users
(= if the authorization server implementation cannot know when end-users logged in) and if the
value of `maxAge` response parameter is a non-zero value, a login form should be displayed.
* The ACR (Authentication Context Class Reference) of the authentication performed for
the current end-user satisfies one of the ACRs listed in `acrs` response parameter. This check is
required only when the value of `acrs` response parameter is a non-empty array.
In every case, the end-user authentication must satisfy one of the ACRs listed in `acrs` response
parameter when the value of `acrs` response parameter is a non-empty array and `acrEssential`
response parameter is `true`.
**\[9] GRANT/DENY BUTTONS**
The end-user is supposed to choose either (1) to grant authorization to the client application or
(2) to deny the authorization request. The UI must have UI components to accept the judgment by
the user. Usually, a button to grant authorization and a button to deny the request are provided.
When the value of `subject` response parameter is not `null`, the end-user authentication must be
performed for the subject, meaning that the authorization server implementation should repeatedly
show a login form until the subject is successfully authenticated.
The end-user will choose either (1) to grant authorization to the client application or (2) to
deny the authorization request. When the end-user chose to deny the authorization request, call
Authlete's `/auth/authorization/fail` API with `reason=DENIED` and use the response from the API
to generate a response to the client application.
When the end-user chose to grant authorization to the client application, the authorization server
implementation has to issue an authorization code, an ID token, and/or an access token to the client
application. (There is a special case. When `response_type=none`, nothing is issued.) Issuing the
tokens can be performed by calling Authlete's `/auth/authorization/issue` API. Read \[ISSUE] written
above in the description for the case of `action=NO_INTERACTION`.
# Update Ticket Information
Source: https://developers.authlete.com/api-reference/authorization-endpoint/update-ticket-information
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/authorization/ticket/update
# Backchannel Logout Token Issuing
Source: https://developers.authlete.com/api-reference/back-channel-logout/backchannel-logout-token-issuing
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/backchannel/logout/token
The `/backchannel/logout/token` API issues a logout token for a client application in the context of [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html).
The caller provides a client identifier and either a subject, a session ID, or both.
Authlete generates a logout token that the caller should then POST to the client's
registered `backchannelLogoutUri`.
A response from the `/backchannel/logout/token` API contains an `action` response
parameter. The possible values are:
## OK
When the action is `OK`, it indicates that the API call completed successfully and
a logout token has been issued. The caller should deliver `logoutToken` to
`backchannelLogoutUri`.
## SERVER\_ERROR
When the action is `SERVER_ERROR`, it indicates that something has gone wrong on
the Authlete side.
## CALLER\_ERROR
When the action is `CALLER_ERROR`, it indicates that the API call contained a
problem. For example, the call may have been missing required request parameters.
# Complete Backchannel Authentication
Source: https://developers.authlete.com/api-reference/ciba/complete-backchannel-authentication
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/backchannel/authentication/complete
This API returns information about what action the authorization server should take after it receives the result of end-user's decision about whether the end-user has approved or rejected a client application's request on the authentication device.
After the implementation of the backchannel authentication endpoint returns JSON containing an
`auth_req_id` to the client, the authorization server starts a background process that communicates
with the authentication device of the end-user. On the authentication device, end-user authentication
is performed and the end-user is asked whether they give authorization to the client or not. The
authorization server will receive the result of end-user authentication and authorization from
the authentication device.
After the authorization server receives the result from the authentication device, or even in the
case where the server gave up receiving a response from the authentication device for some reasons,
the server should call the `/backchannel/authentication/complete` API to tell Authlete the result.
When the end-user was authenticated and authorization was granted to the client by the end-user,
the authorization server should call the API with `result=AUTHORIZED`. In this successful case,
the `subject` request parameter is mandatory. If the token delivery mode is `push`, the API will generate
an access token, an ID token and optionally a refresh token. On the other hand, if the token delivery
mode is `poll` or `ping`, the API will just update the database record so that `/auth/token` API
can generate tokens later.
When the authorization server received the decision of the end-user from the authentication device
and it indicates that the end-user has rejected to give authorization to the client, the authorization
server should call the API with `result=ACCESS_DENIED`. In this case, if the token delivery mode
is `push`, the API will generate an error response that contains the error response parameter and
optionally the `error_description` and error\_uri response parameters (if the `errorDescription`
and `errorUri` request parameters have been given). On the other hand, if the token delivery mode
is `poll` or `ping`, the API will just update the database record so that `/auth/token` API can
generate an error response later. In any token delivery mode, the value of the error parameter will
become `access_denied`.
When the authorization server could not get the result of end-user authentication and authorization
from the authentication device for some reasons, the authorization server should call the API with
`result=TRANSACTION_FAILED`. In this error case, the API will behave in the same way as in the case
of `ACCESS_DENIED`. The only difference is that `expired_token` is used as the value of the `error`
parameter.
The response from `/backchannel/authentication/complete` API has various parameters. Among them,
it is `action` parameter that the authorization server implementation should check first because
it denotes the next action that the authorization server implementation should take. According to
the value of `action`, the service implementation must take the steps described below.
## SERVER\_ERROR
When the value of `action` is `SERVER_ERROR`, it means either (1) that the request from the authorization
server to Authlete was wrong, or (2) that an error occurred on Authlete side.
When the backchannel token delivery mode is `ping` or `push`, `SERVER_ERROR` is used only when
an error is detected before the record of the ticket (which is included in the API call to `/backchannel/authentication/complete`)
is retrieved from the database successfully. If an error is detected after the record of the ticket
is retrieved from the database, `NOTIFICATION` is used instead of `SERVER_ERROR`.
When the backchannel token delivery mode is `poll`, `SERVER_ERROR` is used regardless of whether
it is before or after the record of the ticket is retrieved from the database.
## NO\_ACTION
When the value of `action` is `NO_ACTION`, it means that the authorization server does not have
to take any immediate action.
`NO_ACTION` is returned when the backchannel token delivery mode is `poll`. In this case, the client
will receive the final result at the token endpoint.
## NOTIFICATION
When the value of `action` is `NOTIFICATION`, it means that the authorization server must send a
notification to the client notification endpoint.
According to the CIBA Core specification, the notification is an HTTP POST request whose request
body is JSON and whose `Authorization` header contains the client notification token, which was
included in the backchannel authentication request as the value of the `client_notification_token`
request parameter, as a bearer token.
When the backchannel token delivery mode is `ping`, the request body of the notification is JSON
which contains the `auth_req_id` property only. When the backchannel token delivery mode is `push`,
the request body will additionally contain an access token, an ID token and other properties. Note
that when the backchannel token delivery mode is `poll`, a notification does not have to be sent
to the client notification endpoint.
In error cases, in the ping mode, however, the content of a notification is not different from the
content in successful cases. That is, the notification contains the `auth_req_id` property only.
The client will know the error when it accesses the token endpoint. On the other hand, in the
`push` mode, in error cases, the content of a notification will include the `error` property instead
of an access token and an ID token. The client will know the error by detecting that error is included
in the notification.
In any case, the value of `responseContent` is JSON which can be used as the request body of the
notification.
The client notification endpoint that the notification should be sent to the value of the `clientNotificationEndpoint`
parameter. Likewise, the client notification token that the notification should include as a bearer
token is the `clientNotificationToken` parameter. With these methods, the notification can be built
like the following.
```
POST {clientNotificationEndpoint} HTTP/1.1
HOST: {The host of clientNotificationEndpoint}
Authorization: Bearer {notificationToken}
Content-Type: application/json
{responseContent}
```
# Fail Backchannel Authentication Request
Source: https://developers.authlete.com/api-reference/ciba/fail-backchannel-authentication-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/backchannel/authentication/fail
The API prepares JSON that contains an error. The JSON should be used as the response body of the response which is returned to the client from the [backchannel authentication endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint).
This API is supposed to be called from within the implementation of the [backchannel authentication
endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint)
of the service in order to generate an error response to the client application.
The response from `/backchannel/authentication/fails` API has some parameters. Among them, it is
`action` parameter that the authorization server implementation should check first because it denotes
the next action that the authorization server implementation should take. According to the value
of `action`, the authorization server implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that (1) the `reason` request parameter
of the API call was `SERVER_ERROR`, (2) an error occurred on Authlete side, or (3) the request parameters
of the API call were wrong. In this case, the authorization server implementation should return
a "500 Internal Server Error" response to the client application. However, in most cases, commercial
implementations prefer to use other HTTP status code than 5xx.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, the authorization server implementation should return a
"400 Bad Request" response to the client application.
## FORBIDDEN
When the value of `action` is `FORBIDDEN`, it means that the `reason` request parameter of the API call
was `ACCESS_DENIED`. In this case, the backchannel authentication endpoint of the authorization
server implementation should return a "403 Forbidden" response to the client application.
# Issue Backchannel Authentication Response
Source: https://developers.authlete.com/api-reference/ciba/issue-backchannel-authentication-response
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/backchannel/authentication/issue
This API prepares JSON that contains an `auth_req_id`. The JSON should be used as the response body of the response which is returned to the client from the [backchannel authentication endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint)
This API is supposed to be called from within the implementation of the backchannel authentication
endpoint of the service in order to generate a successful response to the client application.
The description of the `/backchannel/authentication` API describes the timing when this API should
be called and the meaning of request parameters. See \[AUTH\_REQ\_ID ISSUE] in `USER_IDENTIFICATION`.
The response from `/backchannel/authentication/issue` API has some parameters. Among them, it is
`action` parameter that the authorization server implementation should check first because it denotes
the next `action` that the authorization server implementation should take. According to the value
of `action`, the authorization server implementation must take the steps described below.
```java
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response post(String parameters)
{
// 'parameters' is the entity body of the backchannel authentication request.
......
}
```
The endpoint implementation does not have to parse the request parameters from the client application
because Authlete's `/backchannel/authentication` API does it.
The response from `/backchannel/authentication` API has various parameters. Among them, it is `action`
parameter that the authorization server implementation should check first because it denotes the
next action that the authorization server implementation should take. According to the value of
`action`, the service implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## INVALID\_TICKET
When the value of `action` is `INVALID_TICKET`, it means that the ticket included in the API call
was invalid. For example, it does not exist or has expired.
From a viewpoint of the client application, this is an error on the server side. Therefore, the
authorization server implementation should generate a response to the client application with
"500 Internal Server Error" and `application/json`.
You can build an error response in the same way as shown in the description for the case of `INTERNAL_SERVER_ERROR`.
## OK
When the value of `action` is `OK`, it means that Authlete has succeeded in preparing JSON that
contains an `auth_req_id`. The JSON should be used as the response body of the response that is
returned to the client from the backchannel authentication endpoint. `responseContent` contains
the JSON.
The following illustrates the response which the authorization server implementation should generate
and return to the client application.
```
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Process Backchannel Authentication Request
Source: https://developers.authlete.com/api-reference/ciba/process-backchannel-authentication-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/backchannel/authentication
This API parses request parameters of a [backchannel authentication request](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_request) and returns necessary data for the authorization server implementation to process the backchannel authentication request further.
This API is supposed to be called from within the implementation of the [backchannel authentication
endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint)
of the service. The endpoint implementation must extract the request parameters from the
backchannel authentication request from the client application and pass them as the value of parameters
request parameter for Authlete's `/backchannel/authentication` API.
The value of parameters is the entire entity body (which is formatted in `application/x-www-form-urlencoded`)
of the request from the client application.
The following code snippet is an example in JAX-RS showing how to extract request parameters from
the backchannel authentication request.
```java
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response post(String parameters)
{
// 'parameters' is the entity body of the backchannel authentication request.
......
}
```
The endpoint implementation does not have to parse the request parameters from the client application
because Authlete's `/backchannel/authentication` API does it.
The response from `/backchannel/authentication` API has various parameters. Among them, it is `action`
parameter that the authorization server implementation should check first because it denotes the
next action that the authorization server implementation should take. According to the value of
`action`, the service implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
is invalid.
The authorization server implementation should generate a response to the client application with
"400 Bad Request" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that client authentication of the backchannel
authentication request failed. Note that client authentication is always required at the backchannel
authentication endpoint. This implies that public clients are not allowed to use the backchannel
authentication endpoint.
The authorization server implementation should generate a response to the client application with
"401 Unauthorized" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: (challenge)
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## USER\_IDENTIFICATION
When the value of `action` is `USER_IDENTIFICATION`, it means that the backchannel authentication
request from the client application is valid. The authorization server implementation has to follow
the steps below.
**\[1] END-USER IDENTIFICATION**
The first step is to determine the subject (= unique identifier) of the end-user from whom the
client application wants to get authorization.
According to the CIBA specification, a backchannel authentication request contains one (and only
one) of the `login_hint_token`, `id_token_hint` and `login_hint` request parameters as a hint
by which the authorization server identifies the subject of an end-user.
The authorization server implementation can know which hint is included in the backchannel authentication
request by the `hintType` parameter. For example, when the value of the parameter `LOGIN_HINT`,
it means that the backchannel authentication request contains the `login_hint` request parameter
as a hint.
The value of the `hint` parameter is the value of the hint. For example, when the value of the
`hintType` parameter is `LOGIN_HINT`, The value of the `hint` parameter is the value of the `login_hint`
request parameter.
It is up to the authorization server implementation how to determine the subject of the end-user
from the hint. Only when the `id_token_hint` request parameter is used, authorization server
implementation can use the sub response parameter, which holds the value of the sub claim in the
`id_token_hint` request parameter.
**\[2] END-USER IDENTIFICATION ERROR**
There are some cases where the authorization server implementation encounters an error during
the user identification process. In any error case, the service implementation has to return an
HTTP response with the error response parameter to the client application. The following is an
example of such error responses.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{ "error":"unknown_user_id" }
```
Authlete provides `/backchannel/authentication/fail` API that builds the response body (JSON)
of an error response. However, because it is easy to build an error response manually, you may
choose not to call the API. One good thing in using the API is that the API call can trigger
deletion of the ticket which has been issued from Authlete's `/backchannel/authentication` API.
If you don't call `/backchannel/authentication/fail` API, the ticket will continue to exist in
the database until it is cleaned up by the batch program after the ticket expires.
Possible error cases that the authorization server implementation itself has to handle are as
follows. Other error cases have already been covered by `/backchannel/authentication` API.
* `expired_login_hint_token`
The authorization server implementation detected that the hint presented by the `login_hint_token`
request parameter has expired.
Note that the format of `login_hint_token` is not described in the CIBA Core spec at all and
so there is no consensus on how to detect expiration of `login_hint_token`. Interpretation
of `login_hint_token` is left to each authorization server implementation.
* `unknown_user_id`
The authorization server implementation could not determine the subject of the end-user by
the presented hint.
* `unauthorized_client`
The authorization server implementation has custom rules to reject backchannel authentication
requests from some particular clients and found that the client which has made the backchannel
authentication request is one of the particular clients.
Note that `/backchannel/authentication` API does not return `action=USER_IDENTIFICATION` in
cases where the client does not exist or client authentication has failed. Therefore, the
authorization server implementation will never have to use the error code `unauthorized_client`
unless the server has intentionally implemented custom rules to reject backchannel authentication
requests based on clients.
* `missing_user_code`
The authorization server implementation has custom rules to require that a backchannel authentication
request include a user code for some particular users and found that the user identified by
the hint is one of the particular users.
Note that `/backchannel/authentication` API does not return `action=USER_IDENTIFICATION` when
both the `backchannel_user_code_parameter_supported` metadata of the server and the
`backchannel_user_code_parameter` metadata of the client are true and the backchannel authentication
request does not include the user\_code request parameter. In this case, `/backchannel/authentication`
API returns action=BAD\_REQUEST with JSON containing `"error":"missing_user_code"`. Therefore,
the authorization server implementation will never have to use the error code `missing_user_code`
unless the server has intentionally implemented custom rules to require a user code based
on users even in the case where the `backchannel_user_code_parameter` metadata of the client
which has made the backchannel authentication request is `false`.
* `invalid_user_code`
The authorization server implementation detected that the presented user code is invalid.
Note that the format of user\_code is not described in the CIBA Core spec at all and so there
is no consensus on how to judge whether a user code is valid or not. It is up to each authorization
server implementation how to handle user codes.
* `invalid_binding_message`
The authorization server implementation detected that the presented binding message is invalid.
Note that the format of binding\_message is not described in the CIBA Core spec at all and
so there is no consensus on how to judge whether a binding message is valid or not. It is
up to each authorization server implementation how to handle binding messages.
* `invalid_target`
The authorization server implementation rejects the requested target resources.
The error code invalid\_target is from "Resource Indicators for OAuth 2.0". The specification
defines the resource request parameter. By using the parameter, client applications can request
target resources that should be bound to the access token being issued. If the authorization
server wants to reject the request, call `/backchannel/authentication/fail` API with `INVALID_TARGET`.
* `access_denined`
The authorization server implementation has custom rules to reject backchannel authentication
requests without asking the end-user and respond to the client as if the end-user had rejected
the request in some particular cases and found that the backchannel authentication request
is one of the particular cases.
The authorization server implementation will never have to use the error code `access_denied`
at this timing unless the server has intentionally implemented custom rules to reject backchannel
authentication requests without asking the end-user and respond to the client as if the end-user
had rejected the request.
**\[3] AUTH\_REQ\_ID ISSUE**
If the authorization server implementation has successfully determined the subject of the end-user,
the next action is to return an HTTP response to the client application which contains `auth_req_id`.
Authlete provides `/backchannel/authentication/issue` API which generates a JSON containing `auth_req_id`,
so, your next action is (1) call the API, (2) receive the response from the API, (3) build a response
to the client application using the content of the API response, and (4) return the response to
the client application. See the description of `/backchannel/authentication/issue` API for details.
**\[4] END-USER AUTHENTICATION AND AUTHORIZATION**
After sending a JSON containing `auth_req_id` back to the client application, the service implementation
starts to communicate with an authentication device of the end-user. It is assumed that end-user
authentication is performed on the authentication device and the end-user confirms the content of
the backchannel authentication request and grants authorization to the client application if everything
is okay. The authorization server implementation must be able to receive the result of the end-user
authentication and authorization from the authentication device.
How to communicate with an authentication device and achieve end-user authentication and authorization
is up to each authorization server implementation, but the following request parameters of the backchannel
authentication request should be taken into consideration in any implementation.
* `acr_values`
A backchannel authentication request may contain an array of ACRs (Authentication Context Class
References) in preference order. If multiple authentication devices are registered for the end-user,
the authorization server implementation should take the ACRs into consideration when selecting
the best authentication device.
* `scope`
A backchannel authentication request always contains a list of scopes. At least, `openid` is
included in the list (otherwise `/backchannel/authentication` API returns `action=BAD_REQUEST`).
It would be better to show the requested scopes to the end-user on the authentication device
or somewhere appropriate.
If the scope request parameter contains `address`, `email`, `phone` and/or `profile`, they are
interpreted as defined in "5.4. Requesting Claims using Scope Values of OpenID Connect Core 1.0".
That is, they are expanded into a list of claim names. The claimNames parameter returns the expanded
result.
* `binding_message`
A backchannel authentication request may contain a binding message. It is a human readable identifier
or message intended to be displayed on both the consumption device (client application) and the
authentication device.
* `user_code`
A backchannel authentication request may contain a user code. It is a secret code, such as password
or pin, known only to the end-user but verifiable by the authorization server. The user code should
be used to authorize sending a request to the authentication device.
**\[5] END-USER AUTHENTICATION AND AUTHORIZATION COMPLETION**
After receiving the result of end-user authentication and authorization, the authorization server
implementation must call Authlete's `/backchannel/authentication/complete` API to tell Authlete
the result and pass necessary data so that Authlete can generate an ID token, an access token and
optionally a refresh token. See the description of the API for details.
**\[6] CLIENT NOTIFICATION**
When the backchannel token delivery mode is either `ping` or `push`, the authorization server implementation
must send a notification to the pre-registered notification endpoint of the client after the end-user
authentication and authorization. In this case, the `action` parameter in a response from `/backchannel/authentication/complete`
API is `NOTIFICATION`. See the description of `/backchannel/authentication/complete` API for details.
**\[7] TOKEN REQUEST**
When the backchannel token delivery mode is either `ping` or `poll`, the client application will make
a token request to the token endpoint to get an ID token, an access token and optionally a refresh
token.
A token request that corresponds to a backchannel authentication request uses `urn:openid:params:grant-type:ciba`
as the value of the `grant_type` request parameter. Authlete's `/auth/token` API recognizes the
grant type automatically and behaves properly, so the existing token endpoint implementation does
not have to be changed to support CIBA.
# Create Client
Source: https://developers.authlete.com/api-reference/client-management/create-client
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/create
Create a new client.
# Delete Client Tokens
Source: https://developers.authlete.com/api-reference/client-management/delete-client-tokens
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/authorization/delete/{clientId}
Delete all existing access tokens issued to a client application by an end-user.
The subject parameter is required.
# Delete Client Tokens
Source: https://developers.authlete.com/api-reference/client-management/delete-client-tokens-1
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/client/authorization/delete/{clientId}
Delete all existing access tokens issued to a client application by an end-user.
The subject parameter is required and must be provided as a query parameter.
# Delete Client Tokens (by Subject)
Source: https://developers.authlete.com/api-reference/client-management/delete-client-tokens-by-subject
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/client/authorization/delete/{clientId}/{subject}
Delete all existing access tokens issued to a client application by an end-user.
In this variant, the subject is provided in the path.
# Delete Client ⚡
Source: https://developers.authlete.com/api-reference/client-management/delete-client-⚡
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/client/delete/{clientId}
Delete a client.
# Delete Granted Scopes
Source: https://developers.authlete.com/api-reference/client-management/delete-granted-scopes
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/client/granted_scopes/delete/{clientId}
Delete the set of scopes that an end-user has granted to a client application.
Even if records about granted scopes are deleted by calling this API, existing access tokens are
not deleted and scopes of existing access tokens are not changed.
The subject parameter is required and must be provided as a query parameter.
# Delete Granted Scopes (by Subject)
Source: https://developers.authlete.com/api-reference/client-management/delete-granted-scopes-by-subject
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/client/granted_scopes/delete/{clientId}/{subject}
Delete the set of scopes that an end-user has granted to a client application.
In this variant, the subject is provided in the path.
# Delete Requestable Scopes
Source: https://developers.authlete.com/api-reference/client-management/delete-requestable-scopes
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/client/extension/requestable_scopes/delete/{clientId}
Delete requestable scopes of a client
# Get Authorized Applications
Source: https://developers.authlete.com/api-reference/client-management/get-authorized-applications
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/authorization/get/list
Get a list of client applications that an end-user has authorized.
The subject parameter is required and can be provided as a query parameter.
# Get Authorized Applications
Source: https://developers.authlete.com/api-reference/client-management/get-authorized-applications-1
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/authorization/get/list
Get a list of client applications that an end-user has authorized.
The subject parameter is required.
# Get Authorized Applications (by Subject)
Source: https://developers.authlete.com/api-reference/client-management/get-authorized-applications-by-subject
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/authorization/get/list/{subject}
Get a list of client applications that an end-user has authorized.
In this variant, the subject is provided in the path.
# Get Client
Source: https://developers.authlete.com/api-reference/client-management/get-client
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/get/{clientId}
Get a client.
# Get Granted Scopes
Source: https://developers.authlete.com/api-reference/client-management/get-granted-scopes
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/granted_scopes/get/{clientId}
Get the set of scopes that a user has granted to a client application.
Possible values for `requestableScopes` parameter in the response from this API are as follows.
## null
The user has not granted authorization to the client application in the past, or records about the
combination of the user and the client application have been deleted from Authlete's DB.
## An empty set
The user has granted authorization to the client application in the past, but no scopes are associated
with the authorization.
## A set with at least one element
The user has granted authorization to the client application in the past and some scopes are associated
with the authorization. These scopes are returned.
Example: `[ "profile", "email" ]`
The subject parameter is required and must be provided as a query parameter.
# Get Granted Scopes
Source: https://developers.authlete.com/api-reference/client-management/get-granted-scopes-1
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/granted_scopes/get/{clientId}
Get the set of scopes that a user has granted to a client application.
The subject parameter is required.
# Get Granted Scopes (by Subject)
Source: https://developers.authlete.com/api-reference/client-management/get-granted-scopes-by-subject
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/granted_scopes/get/{clientId}/{subject}
Get the set of scopes that a user has granted to a client application.
In this variant, the subject is provided in the path.
# Get Requestable Scopes
Source: https://developers.authlete.com/api-reference/client-management/get-requestable-scopes
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/extension/requestable_scopes/get/{clientId}
Get the requestable scopes per client
# List Clients
Source: https://developers.authlete.com/api-reference/client-management/list-clients
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/get/list
Get a list of clients on a service.
If the access token can view a full service (including an admin), all clients within the
service are returned. Otherwise, only clients that the access token can view within the
service are returned.
- ViewClient: []
# Rotate Client Secret
Source: https://developers.authlete.com/api-reference/client-management/rotate-client-secret
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/client/secret/refresh/{clientIdentifier}
Refresh the client secret of a client. A new value of the client secret will be generated by the
Authlete server.
If you want to specify a new value, use `/api/client/secret/update` API.
# Update Client
Source: https://developers.authlete.com/api-reference/client-management/update-client
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/update/{clientId}
Update a client.
# Update Client Lock
Source: https://developers.authlete.com/api-reference/client-management/update-client-lock
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/lock_flag/update/{clientIdentifier}
Lock and unlock a client
# Update Client Secret
Source: https://developers.authlete.com/api-reference/client-management/update-client-secret
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/secret/update/{clientIdentifier}
Update the client secret of a client.
If you want to have the Authlete server generate a new value of the client secret, use `/api/client/secret/refresh`
API.
# Update Client Tokens
Source: https://developers.authlete.com/api-reference/client-management/update-client-tokens
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/authorization/update/{clientId}
Update attributes of all existing access tokens given to a client application.
# Update Requestable Scopes
Source: https://developers.authlete.com/api-reference/client-management/update-requestable-scopes
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples put /api/{serviceId}/client/extension/requestable_scopes/update/{clientId}
Update requestable scopes of a client
# Update Requestable Scopes
Source: https://developers.authlete.com/api-reference/client-management/update-requestable-scopes-1
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/extension/requestable_scopes/update/{clientId}
Update requestable scopes of a client
# Complete Device Authorization
Source: https://developers.authlete.com/api-reference/device-flow/complete-device-authorization
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/device/complete
This API returns information about what action the authorization server should take after it receives the result of end-user's decision about whether the end-user has approved or rejected a client application's request.
In the device flow, an end-user accesses the verification endpoint of the authorization server where
she interacts with the verification endpoint and inputs a user code. The verification endpoint checks
if the user code is valid and then asks the end-user whether she approves or rejects the authorization
request which the user code represents.
After the authorization server receives the decision of the end-user, it should call Authlete's
`/device/complete` API to tell Authlete the decision.
When the end-user was authenticated and authorization was granted to the client by the end-user,
the authorization server should call the API with `result=AUTHORIZED`. In this successful case,
the subject request parameter is mandatory. The API will update the database record so that `/auth/token`
API can generate an access token later.
If the `scope` parameter of the device authorization request included the openid scope, an ID token
is generated. In this case, `sub`, `authTime`, `acr` and `claims` request parameters in the API
call to `/device/complete` affect the ID token.
When the authorization server receives the decision of the end-user and it indicates that she has
rejected to give authorization to the client, the authorization server should call the API with
`result=ACCESS_DENIED`. In this case, the API will update the database record so that the `/auth/token`
API can generate an error response later. If `errorDescription` and `errorUri` request parameters
are given to the `/device/complete` API, they will be used as the values of `error_description`
and `error_uri` response parameters in the error response from the token endpoint.
When the authorization server could not get decision from the end-user for some reasons, the authorization
server should call the API with `result=TRANSACTION_FAILED`. In this error case, the API will behave
in the same way as in the case of `ACCESS_DENIED`. The only difference is that `expired_token` is
used as the value of the `error` response parameter instead of `access_denied`.
After receiving a response from the `/device/complete` API, the implementation of the authorization
server should retrieve the value of `action` from the response and take the following steps according
to the value.
## SERVER\_ERROR
When the value of `action` is `SERVER_ERROR`, it means that an error occurred on Authlete side. The
authorization server implementation should tell the end-user that something wrong happened and
urge her to re-initiate a device flow.
## USER\_CODE\_NOT\_EXIST
When the value of `action` is `USER_CODE_NOT_EXIST`, it means that the user code included in the API
call does not exist. The authorization server implementation should tell the end-user that the user
code has been invalidated and urge her to re-initiate a device flow.
## USER\_CODE\_EXPIRED
When the value of `action` is `USER_CODE_EXPIRED`, it means that the user code included in the API
call has expired. The authorization server implementation should tell the end-user that the user
code has expired and urge her to re-initiate a device flow.
## INVALID\_REQUEST
When the value of `action` is `INVALID_REQUEST`, it means that the API call is invalid. Probably,
the authorization server implementation has some bugs.
## SUCCESS
When the value of `action` is `SUCCESS`, it means that the API call has been processed successfully.
The authorization server should return a successful response to the web browser the end-user is
using.
# Process Device Authorization Request
Source: https://developers.authlete.com/api-reference/device-flow/process-device-authorization-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/device/authorization
This API parses request parameters of a [device authorization request](https://datatracker.ietf.org/doc/html/rfc8628#section-3.1) and returns necessary data for the authorization server implementation to process the device authorization request further.
This API is supposed to be called from the within the implementation of the device authorization
endpoint of the service. The service implementation should retrieve the value of `action` from the
response and take the following steps according to the value.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the API call from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from a viewpoint of the client application, it is an error on the server side.
Therefore, the authorization server implementation should generate a response to the client application
with "500 Internal Server Error"s and `application/json`.
The value of `responseContent` is a JSON string which describes t he error, so it can be
used as the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
is wrong.
The authorization server implementation should generate a response to the client application with
"400 Bad Request" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that client authentication of the device authorization
request failed.
The authorization server implementation should generate a response to the client application with
"401 Unauthorized" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: (challenge)
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## OK
When the value of `action` is `OK`, it means that the device authorization request from the client
application is valid.
The authorization server implementation should generate a response to the client application with
"200 OK" and `application/json`.
The `responseContent` is a JSON string which can be used as the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client application.
# Process Device Verification Request
Source: https://developers.authlete.com/api-reference/device-flow/process-device-verification-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/device/verification
The API returns information associated with a user code.
After receiving a response from the device authorization endpoint of the authorization server,
the client application shows the end-user the user code and the verification URI which are included
in the device authorization response. Then, the end-user will access the verification URI using
a web browser on another device (typically, a smart phone). In normal implementations, the verification
endpoint will return an HTML page with an input form where the end-user inputs a user code. The
authorization server will receive a user code from the form.
After receiving a user code, the authorization server should call Authlete's `/device/verification`
API with the user code. And then, the authorization server implementation should retrieve the value
of `action` parameter from the API response and take the following steps according to the value.
## SERVER\_ERROR
When the value of `action` is `SERVER_ERROR`, it means that an error occurred on Authlete side. The
authorization server implementation should tell the end-user that something wrong happened and
urge her to re-initiate a device flow.
## NOT\_EXIST
When the value of `action` is `NOT_EXIST`, it means that the user code does not exist. The authorization
server implementation should tell the end-user that the user code is invalid and urge her to retry
to input a valid user code.
## EXPIRED
When the value of `action` is `EXPIRED`, it means that the user code has expired. The authorization
server implementation should tell the end-user that the user code has expired and urge her to
re-initiate a device flow.
## VALID
When the value of `action` is `VALID`, it means that the user code exists, has not expired, and
belongs to the service. The authorization server implementation should interact with the end-user
to ask whether she approves or rejects the authorization request from the device.
# Delete Client
Source: https://developers.authlete.com/api-reference/dynamic-client-registration/delete-client
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/registration/delete
Delete a dynamically registered client. This API is supposed to be used to implement a client registration management endpoint that complies with [RFC 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration Management).
This API is supposed to be called from the within the implementation of the client registration
management endpoint of the authorization server. The authorization server implementation should
retrieve the value of `action` from the response and take the following steps according to the value.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the API call from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from a viewpoint of the client or developer, it is an error on the server side.
Therefore, the authorization server implementation should generate a response with "500 Internal
Server Error"s and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client or developer since
"500 Internal Server Error" is not required by the specification.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client or developer
was wrong.
The authorization server implementation should generate a response with "400 Bad Request" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that the registration access token used by
the client configuration request (RFC 7592) is invalid, or the client application which the token
is tied to does not exist any longer or is invalid.
The HTTP status of the response returned to the client application must be "401 Unauthorized" and
the content type must be `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the endpoint implementation should generate and return
to the client application.
```
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
NOTE: The `UNAUTHORIZED` value was added in October, 2021. See the description of
`Service.unauthorizedOnClientConfigSupported` for details.
## DELETED
When the value of `action` is `DELETED`, it means that the request from the client or developer is
valid.
The authorization server implementation should generate a response to the client or developer with
"204 No Content".
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 204 No Content
Cache-Control: no-store
Pragma: no-cache
```
# Get Client
Source: https://developers.authlete.com/api-reference/dynamic-client-registration/get-client
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/registration/get
Get a dynamically registered client. This API is supposed to be used to implement a client registration management endpoint that complies with [RFC 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration Management).
This API is supposed to be called from the within the implementation of the client registration
management endpoint of the authorization server. The authorization server implementation should
retrieve the value of `action` from the response and take the following steps according to the value.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the API call from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from a viewpoint of the client or developer, it is an error on the server side.
Therefore, the authorization server implementation should generate a response to the client or developer
with "500 Internal Server Error"s and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client or developer since
"500 Internal Server Error" is not required by the specification.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client or developer
was wrong.
The authorization server implementation should generate a response to the client or developer with
"400 Bad Request" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that the registration access token used by
the client configuration request (RFC 7592) is invalid, or the client application which the token
is tied to does not exist any longer or is invalid.
The HTTP status of the response returned to the client application must be "401 Unauthorized" and
the content type must be `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the endpoint implementation should generate and return
to the client application.
```
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
NOTE: The `UNAUTHORIZED` value was added in October, 2021. See the description of
`Service.unauthorizedOnClientConfigSupported` for details.
## OK
When the value of `action` is `OK`, it means that the request from the client or developer is valid.
The authorization server implementation should generate a response to the client or developer with
"200 OK" and `application/json`.
The `responseContent` a JSON string which can be used as the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Register Client
Source: https://developers.authlete.com/api-reference/dynamic-client-registration/register-client
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/registration
Register a client. This API is supposed to be used to implement a client registration endpoint that complies with [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic Client Registration Protocol).
This API is supposed to be called from the within the implementation of the client registration
endpoint of the authorization server. The authorization server implementation should retrieve
the value of `action` from the response and take the following steps according to the value.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the API call from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from a viewpoint of the client or developer, it is an error on the server side.
Therefore, the authorization server implementation should generate a response with "500 Internal
Server Error"s and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client or developer since
"500 Internal Server Error" is not required by the specification.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client or developer
was wrong.
The authorization server implementation should generate a response with "400 Bad Request" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## CREATED
When the value of `action` is `CREATED`, it means that the request from the client or developer is
valid.
The authorization server implementation should generate a response to the client or developer with
"201 CREATED" and `application/json`.
The `responseContent` a JSON string which can be used as the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 201 CREATED
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Update Client
Source: https://developers.authlete.com/api-reference/dynamic-client-registration/update-client
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/client/registration/update
Update a dynamically registered client. This API is supposed to be used to implement a client registration management endpoint that complies with [RFC 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration Management).
This API is supposed to be called from the within the implementation of the client registration
management endpoint of the authorization server. The authorization server implementation should
retrieve the value of `action` from the response and take the following steps according to the value.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the API call from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from a viewpoint of the client or developer, it is an error on the server side.
Therefore, the authorization server implementation should generate a response with "500 Internal
Server Error"s and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client or developer since
"500 Internal Server Error" is not required by the specification.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client or developer
was wrong.
The authorization server implementation should generate a response with "400 Bad Request" and `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that the registration access token used by
the client configuration request (RFC 7592) is invalid, or the client application which the token
is tied to does not exist any longer or is invalid.
The HTTP status of the response returned to the client application must be "401 Unauthorized" and
the content type must be `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
***
The following illustrates the response which the endpoint implementation should generate and return
to the client application.
```
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
NOTE: The `UNAUTHORIZED` value was added in October, 2021. See the description of
`Service.unauthorizedOnClientConfigSupported` for details.
## UPDATED
When the value of `action` is `UPDATED`, it means that the request from the client or developer is
valid.
The authorization server implementation should generate a response to the client or developer with
"200 OK" and `application/json`.
The `responseContent` a JSON string which can be used as the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client or developer.
```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Process Entity Configuration Request
Source: https://developers.authlete.com/api-reference/federation-endpoint/process-entity-configuration-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/federation/configuration
This API gathers the federation configuration about a service. The authorization server implementation should retrieve the value of the `action` response parameter from the API response and take the following steps according to the value.
### `OK`
When the value of the `action` response
parameter is `OK`, it means that Authlete
could prepare an entity configuration successfully.
In this case, the implementation of the entity configuration endpoint of the
authorization server should return an HTTP response to the client application
with the HTTP status code "`200 OK`" and the content type
"`application/entity-statement+jwt`". The message body (= an entity
configuration in the JWT format) of the response has been prepared by
Authlete's `/federation/configuration` API and it is available as the
`responseContent` response parameter.
The implementation of the entity configuration endpoint can construct an
HTTP response by doing like below.
```
200 OK
Content-Type: application/entity-statement+jwt
(Other HTTP headers)
(the value of the responseContent response parameter)
```
### `NOT_FOUND`
When the value of the `action` response
parameter is `NOT_FOUND`, it means that
the service configuration has not enabled the feature of [OpenID Connect
Federation 1.0](https://openid.net/specs/openid-connect-federation-1_0.html) and so the client application should have not access the
entity configuration endpoint.
In this case, the implementation of the entity configuration endpoint of the
authorization server should return an HTTP response to the client application
with the HTTP status code "`404 Not Found`" and the content type
"`application/json`". The message body (= error information in the JSON
format) of the response has been prepared by Authlete's
`/federation/configuration` API and it is available as the
`responseContent` response parameter.
The implementation of the entity configuration endpoint can construct an
HTTP response by doing like below.
```
404 Not Found
Content-Type: application/json
(Other HTTP headers)
(the value of the responseContent response parameter)
```
### `INTERNAL_SERVER_ERROR`
could prepare an entity configuration successfully.
In this case, the implementation of the entity configuration endpoint of the
authorization server should return an HTTP response to the client application
with the HTTP status code "`200 OK`" and the content type
"`application/entity-statement+jwt`". The message body (= an entity
configuration in the JWT format) of the response has been prepared by
Authlete's `/federation/configuration` API and it is available as the
`responseContent` response parameter.
The implementation of the entity configuration endpoint can construct an
HTTP response by doing like below.
```
200 OK
Content-Type: application/entity-statement+jwt
(Other HTTP headers)
(the value of the responseContent response parameter)
```
# Process Federation Registration Request
Source: https://developers.authlete.com/api-reference/federation-endpoint/process-federation-registration-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/federation/registration
The Authlete API is for implementations of the **federation registration
endpoint** that accepts "explicit client registration". Its details are
defined in [OpenID Connect Federation 1.0](https://openid.net/specs/openid-connect-federation-1_0.html).
The endpoint accepts `POST` requests whose `Content-Type`
is either of the following.
1. `application/entity-statement+jwt`- `application/trust-chain+json`
When the `Content-Type` of a request is
`application/entity-statement+jwt`, the content of the request is
the entity configuration of a relying party that is to be registered.
In this case, the implementation of the federation registration endpoint
should call Authlete's `/federation/registration` API with the
entity configuration set to the `entityConfiguration` request
parameter.
On the other hand, when the `Content-Type` of a request is
`application/trust-chain+json`, the content of the request is a
JSON array that contains entity statements in JWT format. The sequence
of the entity statements composes the trust chain of a relying party
that is to be registered. In this case, the implementation of the
federation registration endpoint should call Authlete's
`/federation/registration` API with the trust chain set to the
`trustChain` request parameter.
# Process Grant Management Request
Source: https://developers.authlete.com/api-reference/grant-management-endpoint/process-grant-management-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/gm
The API is for the implementation of the grant management endpoint which is
defined in "[Grant Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html)".
# Create Security Key
Source: https://developers.authlete.com/api-reference/hardware-security-key/create-security-key
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/hsk/create
# Delete Security Key
Source: https://developers.authlete.com/api-reference/hardware-security-key/delete-security-key
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/hsk/delete/{handle}
# Get Security Key
Source: https://developers.authlete.com/api-reference/hardware-security-key/get-security-key
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/hsk/get/{handle}
# List Security Keys
Source: https://developers.authlete.com/api-reference/hardware-security-key/list-security-keys
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/hsk/get/list
# Process Introspection Request
Source: https://developers.authlete.com/api-reference/introspection-endpoint/process-introspection-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/introspection
This API gathers information about an access token.
This API is supposed to be called from within the implementations of protected resource endpoints
of the authorization server implementation in order to get information about the access token which
was presented by the client application.
In general, a client application accesses a protected resource endpoint of a service with an access
token, and the implementation of the endpoint checks whether the presented access token has enough
privileges (= scopes) to access the protected resource before returning the protected resource to
the client application. To achieve this flow, the endpoint implementation has to know detailed
information about the access token. Authlete `/auth/introspection` API can be used to get such information.
The response from `/auth/introspection` API has some parameters. Among them, it is `action` parameter
that the authorization server implementation should check first because it denotes the next action
that the authorization server implementation should take. According to the value of `action`, the
authorization server implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error". Authlete recommends `application/json` as the content
type although OAuth 2.0 specification does not mention the format of the error response when the
redirect URI is not usable.
The value of `responseContent` is a string which describes the error in the format of
[RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage), so if
the protected resource of the service implementation wants to return an error response to the client
application in the way that complies with RFC 6750 (in other words, if `accessTokenType` configuration
parameter of the service is Bearer), the value of `responseContent` can be used as the value of
`WWW-Authenticate` header.
The following is an example response which complies with RFC 6750.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
does not contain an access token (= the request from the authorization server implementation to
Authlete does not contain `token` request parameter).
A response with HTTP status of "400 Bad Request" must be returned to the client application and
the content type must be `application/json`.
The value of `responseContent` is a string which describes the error in the format of [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage), so if the
protected resource of the service implementation wants to return an error response to the client
application in the way that complies with RFC 6750 (in other words, if `accessTokenType` configuration
parameter of the service is `Bearer`), the value of `responseContent` can be used as the value of
`WWW-Authenticate` header.
The following is an example response which complies with RFC 6750.
```
HTTP/1.1 400 Bad Request
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that the access token does not exist or has
expired.
The value of `responseContent` is a string which describes the error in the format of RFC
6750 (OAuth 2.0 Bearer Token Usage), so if the protected resource of the service implementation
wants to return an error response to the client application in the way that complies with [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (in other words, if `accessTokenType` configuration
parameter of the service is `Bearer`), the value of `responseContent` can be used as the value of
`WWW-Authenticate` header.
The following is an example response which complies with RFC 6750.
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## FORBIDDEN
When the value of `action` is `FORBIDDEN`, it means that the access token does not cover the required
scopes or that the subject associated with the access token is different from the subject contained
in the request.
A response with HTTP status of "400 Bad Request" must be returned to the client application and
the content type must be `application/json`.
The value of `responseContent` is a string which describes the error in the format of [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage), so if the
protected resource of the service implementation wants to return an error response to the client
application in the way that complies with RFC 6750 (in other words, if `accessTokenType` configuration
parameter of the service is Bearer), the value of `responseContent` can be used as the value of
`WWW-Authenticate` header.
The following is an example response which complies with RFC 6750.
```
HTTP/1.1 403 Forbidden
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## OK
When the value of `action` is `OK`, it means that the access token which the client application
presented is valid (= exists and has not expired).
The implementation of the protected resource endpoint is supposed to return the protected resource
to the client application.
When action is `OK`, the value of `responseContent` is `"Bearer error=\"invalid_request\""`. This
is the simplest string which can be used as the value of `WWW-Authenticate` header to indicate
"400 Bad Request". The implementation of the protected resource endpoint may use this string to
tell the client application that the request was bad (e.g. in case necessary request parameters
for the protected resource endpoint are missing). However, in such a case, the implementation
should generate a more informative error message to help developers of client applications.
The following is an example error response which complies with RFC 6750.
```
HTTP/1.1 400 Bad Request
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
Basically, The value of `responseContent` is a string which describes the error in the format of
[RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage). So, if
the service has selected `Bearer` as the value of `accessTokenType` configuration parameter, the
value of `responseContent` can be used directly as the value of `WWW-Authenticate` header. However,
if the service has selected another different token type, the service has to generate error messages
for itself.
***JWT-based access token***
Since version 2.1, Authlete provides a feature to issue access tokens in JWT format. This feature
can be enabled by setting a non-null value to the `accessTokenSignAlg` property of the service
(see the description of the Service class for details). `/api/auth/introspection` API can accept
access tokens in JWT format. However, note that the API does not return information contained in
a given JWT-based access token but returns information stored in the database record which corresponds
to the given JWT-based access token. Because attributes of the database record can be modified
after the access token is issued (for example, by using `/api/auth/token/update` API), information
returned by `/api/auth/introspection` API and information the given JWT-based access token holds
may be different.
# Process OAuth 2.0 Introspection Request
Source: https://developers.authlete.com/api-reference/introspection-endpoint/process-oauth-20-introspection-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/introspection/standard
This API exists to help your authorization server provide its own introspection API which complies with [RFC 7662](https://tools.ietf.org/html/rfc7662) (OAuth 2.0 Token Introspection).
This API is supposed to be called from within the implementations of the introspection endpoint
of your service. The authorization server implementation should retrieve the value of `action` from
the response and take the following steps according to the value.
In general, a client application accesses a protected resource endpoint of a service with an access
token, and the implementation of the endpoint checks whether the presented access token has enough
privileges (= scopes) to access the protected resource before returning the protected resource to
the client application. To achieve this flow, the endpoint implementation has to know detailed
information about the access token. Authlete `/auth/introspection` API can be used to get such information.
The response from `/auth/introspection` API has some parameters. Among them, it is `action` parameter
that the authorization server implementation should check first because it denotes the next action
that the authorization server implementation should take. According to the value of `action`, the
authorization server implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error".
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response if you want. Note that, however, [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662) does not mention anything about the response
body of error responses.
***
The following illustrates an example response which the introspection endpoint of the authorization
server implementation generates and returns to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{responseContent}
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
is invalid. This happens when the request from the client did not include the token request parameter.
See "[2.1. Introspection Request](https://datatracker.ietf.org/doc/html/rfc7662#section-2.1)" in
RFC 7662 for details about requirements for introspection requests.
The HTTP status of the response returned to the client application should be "400 Bad Request".
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response if you want. Note that, however, [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)
does not mention anything about the response body of error responses.
The following illustrates an example response which the introspection endpoint of the authorization
server implementation generates and returns to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
{responseContent}
```
## OK
When the value of `action` is `OK`, the request from the client application is valid.
The HTTP status of the response returned to the client application must be "200 OK" and its content
type must be `application/json`.
The value of `responseContent` is a JSON string which complies with the introspection response
defined in "2.2. Introspection Response" in RFC7662.
***
The following illustrates the response which the introspection endpoint of your authorization server
implementation should generate and return to the client application.
```
HTTP/1.1 200 OK
Content-Type: application/json
{responseContent}
```
Note that RFC 7662 says *"To prevent token scanning attacks, **the endpoint MUST also require some
form of authorization to access this endpoint**"*. This means that you have to protect your introspection
endpoint in some way or other. Authlete does not care about how your introspection endpoint is protected.
In most cases, as mentioned in RFC 7662, "401 Unauthorized" is a proper response when an introspection
request does not satisfy authorization requirements imposed by your introspection endpoint.
# Process Pushed Authorization Request
Source: https://developers.authlete.com/api-reference/pushed-authorization-endpoint/process-pushed-authorization-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/pushed_auth_req
This API creates a pushed request authorization. It authenticates the client and creates a authorization_uri to be returned by the authorization server.
# Process Revocation Request
Source: https://developers.authlete.com/api-reference/revocation-endpoint/process-revocation-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/revocation
This API revokes access tokens and refresh tokens.
This API is supposed to be called from within the implementation of the revocation endpoint ([RFC
7009](tools.ietf.org/html/rfc7009)) of the authorization server implementation in order to revoke
access tokens and refresh tokens.
The response from `/auth/revocation` API has some parameters. Among them, it is `action` parameter
that the authorization server implementation should check first because it denotes the next action
that the authorization server implementation should take. According to the value of `action`, the
authorization server implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error".
The value of `responseContent` is a JSON string which describes the error, so it can be
used as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## INVALID\_CLIENT
When the value of `action` is `INVALID_CLIENT`, it means that authentication of the client failed.
In this case, the HTTP status of the response to the client application is either "400 Bad Request"
or "401 Unauthorized". The description about `invalid_client` shown below is an excerpt from [RFC
6749](https://datatracker.ietf.org/doc/html/rfc6749).
***
Client authentication failed (e.g., unknown client, no client authentication included, or unsupported
authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code
to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate
via the `Authorization` request header field, the authorization server MUST respond with an HTTP
401 (Unauthorized) status code and include the `WWW-Authenticate` response header field matching
the authentication scheme used by the client.
***
In either case, the value of `responseContent` is a JSON string which can be used as the entity
body of the response to the client application.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: {challenge}
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
is invalid.
The HTTP status of the response returned to the client application must be "400 Bad Request" and
the content type must be `application/json`. [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009),
[2.2.1. Error Respons](https://datatracker.ietf.org/doc/html/rfc7009#section-2.2.1) states "The
error presentation conforms to the definition in [Section 5.2](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2)
of \[[RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749)]."
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the authorization server implementation should generate
and return to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## OK
When the value of `action` is `OK`, it means that the request from the client application is valid
and the presented token has been revoked successfully or if the client submitted an invalid token.
Note that invalid tokens do not cause an error. See [2.2. Revocation Response](https://datatracker.ietf.org/doc/html/rfc7009#section-2.2) for details.
The HTTP status of the response returned to the client application must be 200 OK.
If the original request from the client application contains callback request parameter and its
value is not empty, the content type should be `application/javascript` and the content should be
a JavaScript snippet for JSONP.
The value of `responseContent` is JavaScript snippet if the original request from the client application
contains callback request parameter and its value is not empty. Otherwise, the value of `responseContent`
is `null`.
```
HTTP/1.1 200 OK
Content-Type: application/javascript
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Create Service (IDP)
Source: https://developers.authlete.com/api-reference/service-management/create-service-idp
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/service
Create a new service on the API server, belonging to the specified organization.
A service can be created with an organization token or by a user with the CREATE_SERVICE role.
This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`),
not on the regional API clusters.
# Get Service
Source: https://developers.authlete.com/api-reference/service-management/get-service
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/service/get
Get a service.
If the access token can only view or modify clients underneath this service, but does not
have access to view this service directly, a limited view of the service will be returned.
# Get Service Configuration
Source: https://developers.authlete.com/api-reference/service-management/get-service-configuration
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/service/configuration
This API gathers configuration information about a service.
This API is supposed to be called from within the implementation of the configuration endpoint of
the service where the service that supports OpenID Connect and [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html)
must expose its configuration information in a JSON format. Details about the format are described
in "[3. OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata)"
in OpenID Connect Discovery 1.0.
# List Services
Source: https://developers.authlete.com/api-reference/service-management/list-services
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/service/get/list
Get a list of services.
If the access token can only view or modify clients underneath a service, but does not
have access to view that service directly, a limited view of the service will be returned.
Otherwise, all properties of the service are returned.
If the access token is an administrative token, this returns a list of all services on the Authlete instance.
Otherwise, all services that the access token can view, even in a limited fashion, are returned.
# Remove Service (IDP) ⚡
Source: https://developers.authlete.com/api-reference/service-management/remove-service-idp-⚡
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/service/remove
Delete a service from the API server.
A service can be deleted with an organization token or by a user with the MODIFY_SERVICE role.
This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`),
not on the regional API clusters.
# Update Service
Source: https://developers.authlete.com/api-reference/service-management/update-service
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/service/update
Update a service.
# Fail Token Request
Source: https://developers.authlete.com/api-reference/token-endpoint/fail-token-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/token/fail
This API generates a content of an error token response that the authorization server implementation returns to the client application.
This API is supposed to be called from within the implementation of the token endpoint of the service
in order to generate an error response to the client application.
The description of the `/auth/token` API describes the timing when this API should be called. See
the description for the case of `action=PASSWORD`.
The response from `/auth/token/fail` API has some parameters. Among them, it is `action` parameter
that the authorization server implementation should check first because it denotes the next action
that the authorization server implementation should take. According to the value of `action`, the
authorization server implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error".
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application
since "500 Internal Server Error" is not required by OAuth 2.0.
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that Authlete's `/auth/token/fail` API successfully
generated an error response for the client application.
The HTTP status of the response returned to the client application must be "400 Bad Request" and
the content type must be `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Issue Token Response
Source: https://developers.authlete.com/api-reference/token-endpoint/issue-token-response
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/token/issue
This API generates a content of a successful token response that the authorization server implementation returns to the client application.
This API is supposed to be called from within the implementation of the token endpoint of the service
in order to generate a successful response to the client application.
The description of the `/auth/token` API describes the timing when this API should be called. See
the description for the case of `action=PASSWORD`.
The response from `/auth/token/issue` API has some parameters. Among them, it is `action` parameter
that the authorization server implementation should check first because it denotes the next action
that the authorization server implementation should take. According to the value of `action`, the
authorization server implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error".
The value of `responseContent` is a JSON string which describes the error, so it can be used
as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application
since "500 Internal Server Error" is not required by OAuth 2.0.
## OK
When the value of `action` is `OK`, it means that Authlete's `/auth/token/issue` API successfully
generated an access token.
The HTTP status of the response returned to the client application must be "200 OK" and the content
type must be`application/json`.
The value of `responseContent` is a JSON string which contains an access token, so it can be used
as the entity body of the response.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
# Process Token Request
Source: https://developers.authlete.com/api-reference/token-endpoint/process-token-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/token
This API parses request parameters of an authorization request and returns necessary data for the authorization server implementation to process the authorization request further.
This API is supposed to be called from with the implementation of the token endpoint of the service.
The endpoint implementation must extract the request parameters from the token request from the
client application and pass them as the value of parameters request parameter to Authlete's `/auth/token` API.
The value of parameters is the entire entity body (which is formatted in `application/x-www-form-urlencoded`)
of the token request.
In addition, if the token endpoint of the authorization server implementation supports basic authentication
as a means of [client authentication](https://datatracker.ietf.org/doc/html/rfc6749#section-2.3),
the client credentials must be extracted from `Authorization` header and they must be passed as
`clientId` request parameter and `clientSecret` request parameter to Authlete's `/auth/token` API.
The following code snippet is an example in JAX-RS showing how to extract request parameters from
the token request and client credentials from Authorization header.
```java
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response post(
@HeaderParam(HttpHeaders.AUTHORIZATION) String auth,
String parameters)
{
// Convert the value of Authorization header (credentials of
// the client application), if any, into BasicCredentials.
BasicCredentials credentials = BasicCredentials.parse(auth);
// The credentials of the client application extracted from
// 'Authorization' header. These may be null.
String clientId = credentials == null ? null
: credentials.getUserId();
String clientSecret = credentials == null ? null
: credentials.getPassword();
// Process the given parameters.
return process(parameters, clientId, clientSecret);
}
```
**Note**: The `jwtAtClaims` request parameter is effective only when this API directly issues an access
token, i.e., in the `client_credentials` grant flow (and the Native SSO branch of Token Exchange). For
other grant flows, additional JWT claims must be set via the corresponding API instead: `/auth/authorization/issue`
(authorization\_code), `/auth/backchannel/authentication/complete` (CIBA), or `/auth/device/complete`
(device\_code). `jwtAtClaims` is silently ignored when sent with other grants.
The response from `/auth/token` API has some parameters. Among them, it is action parameter that
the service implementation should check first because it denotes the next action that the authorization
server implementation should take. According to the value of action, the authorization server
implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete.
In either case, from the viewpoint of the client application, it is an error on the server side.
Therefore, the service implementation should generate a response to the client application with
HTTP status of "500 Internal Server Error". Authlete recommends `application/json` as the content
type although OAuth 2.0 specification does not mention the format of the error response when the
redirect URI is not usable.
The value of `responseContent` is a JSON string which describes the error, so it can be
used as the entity body of the response.
***
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
The endpoint implementation may return another different response to the client application
since "500 Internal Server Error" is not required by OAuth 2.0.
## INVALID\_CLIENT
When the value of `action` is `INVALID_CLIENT`, it means that authentication of the client failed.
In this case, the HTTP status of the response to the client application is either "400 Bad Request"
or "401 Unauthorized". This requirement comes from [RFC 6749, 5.2. Error Response](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2).
The description about `invalid_client` shown below is an excerpt from RFC 6749.
***
Client authentication failed (e.g., unknown client, no client authentication included, or unsupported
authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code
to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate
via the `Authorization` request header field, the authorization server MUST respond with an HTTP
401 (Unauthorized) status code and include the `WWW-Authenticate` response header field matching
the authentication scheme used by the client.
***
In either case, the value of `responseContent` is a JSON string which can be used as the entity
body of the response to the client application.
***
The following illustrate responses which the service implementation must generate and return to
the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: {challenge}
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
is invalid.
A response with HTTP status of "400 Bad Request" must be returned to the client application and
the content type must be `application/json`.
The value of `responseContent` is a JSON string which describes the error, so it can be used as
the entity body of the response.
The following illustrates the response which the service implementation should generate and return
to the client application.
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## PASSWORD
When the value of `"action"` is `"PASSWORD"`, it means that
the request from the client application is valid and `grant_type`
is `"password"`. That is, the flow is
["Resource Owner
Password Credentials"](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.3).
In this case, \{@link #getUsername()} returns the value of `"username"`
request parameter and \{@link #getPassword()} returns the value of \{@code
"password"} request parameter which were contained in the token request
from the client application. The service implementation must validate the
credentials of the resource owner (= end-user) and take either of the
actions below according to the validation result.
1. When the credentials are valid, call Authlete's /auth/token/issue} API to generate an access token for the client
application. The API requires `"ticket"` request parameter and
`"subject"` request parameter.
Use the value returned from \{@link #getTicket()} method as the value
for `"ticket"` parameter.
2. The response from `/auth/token/issue` API (\{@link
TokenIssueResponse}) contains data (an access token and others)
which should be returned to the client application. Use the data
to generate a response to the client application.
3. When the credentials are invalid, call Authlete's \{@code
/auth/token/fail} API with `reason=`\{@link
TokenFailRequest.Reason#INVALID\_RESOURCE\_OWNER\_CREDENTIALS
INVALID\_RESOURCE\_OWNER\_CREDENTIALS} to generate an error response
for the client application. The API requires `"ticket"`
request parameter. Use the value returned from \{@link #getTicket()}
method as the value for `"ticket"` parameter.
4. The response from `/auth/token/fail` API (\{@link
TokenFailResponse}) contains error information which should be
returned to the client application. Use it to generate a response
to the client application.
## OK
When the value of `action` is `OK`, it means that the request from the client application is valid
and an access token, and optionally an ID token, is ready to be issued.
The HTTP status of the response returned to the client application must be "200 OK" and the content
type must be `application/json`.
The value of `responseContent` is a JSON string which contains an access token (and optionally
an ID token), so it can be used as the entity body of the response.
***
The following illustrates the response which the service implementation must generate and return
to the client application.
```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
## TOKEN\_EXCHANGE (Authlete 2.3 onwards)
When the value of `"action"` is `"TOKEN_EXCHANGE"`, it means
that the request from the client application is a valid token exchange
request (cf. [RFC
8693 OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html)) and that the request has already passed
the following validation steps.
1. Confirm that the value of the `requested_token_type` request parameter
is one of the registered token type identifiers if the request parameter is
given and its value is not empty.
2. Confirm that the `subject_token` request parameter is given and its
value is not empty.
3. Confirm that the `subject_token_type` request parameter is given and
its value is one of the registered token type identifiers.
4. Confirm that the `actor_token_type` request parameter is given and
its value is one of the registered token type identifiers if the
`actor_token` request parameter is given and its value is not empty.
5. Confirm that the `actor_token_type` request parameter is not given
or its value is empty when the `actor_token` request parameter is
not given or its value is empty.
Furthermore, Authlete performs additional validation on the tokens specified
by the `subject_token` request parameter and the `actor_token`
request parameter according to their respective token types as shown below.
## Token Validation Steps
\*Token Type: `urn:ietf:params:oauth:token-type:jwt`\*
1. Confirm that the format conforms to the JWT specification \[RFC 7519]\[[https://www.rfc-editor.org/rfc/rfc7519.html](https://www.rfc-editor.org/rfc/rfc7519.html)].
2. Check if the JWT is encrypted and if it is encrypted, then (a) reject
the token exchange request when the \{@link
Service#isTokenExchangeEncryptedJwtRejected()
tokenExchangeEncryptedJwtRejected} flag of the service is `true`
or (b) skip remaining validation steps when the flag is `false`.
Note that Authlete does not verify an encrypted JWT because there is
no standard way to obtain the key to decrypt the JWT with. This means
that you must verify an encrypted JWT by yourself when one is used as
an input token with the token type
\{ @code "urn:ietf:params:oauth:token-type:jwt" }.
3. Confirm that the current time has not reached the time indicated by
the `exp` claim if the JWT contains the claim.
4. Confirm that the current time is equal to or after the time indicated
by the `iat` claim if the JWT contains the claim.
5.Confirm that the current time is equal to or after the time indicated
by the `nbf` claim if the JWT contains the claim.
5. Check if the JWT is signed and if it is not signed, then (a) reject
the token exchange request when the \{@link
Service#isTokenExchangeUnsignedJwtRejected()
tokenExchangeUnsignedJwtRejected} flag of the service is `true`
or (b) finish validation on the input token. Note that Authlete does
not verify the signature of the JWT because there is no standard way
to obtain the key to verify the signature of a JWT with. This means
that you must verify the signature by yourself when a signed JWT is
used as an input token with the token type
`"urn:ietf:params:oauth:token-type:jwt"`.
\*Token Type: `urn:ietf:params:oauth:token-type:access_token`\*
6. Confirm that the token is an access token that has been issued by
the Authlete server of your service. This implies that access
tokens issued by other systems cannot be used as a subject token
or an actor token with the token type
`urn:ietf:params:oauth:token-type:access_token`.
7. Confirm that the access token has not expired.
8. Confirm that the access token belongs to the service.
\*Token Type: `urn:ietf:params:oauth:token-type:refresh_token`\*
9. Confirm that the token is a refresh token that has been issued by
the Authlete server of your service. This implies that refresh
tokens issued by other systems cannot be used as a subject token
or an actor token with the token type
`urn:ietf:params:oauth:token-type:refresh_token`.
10. Confirm that the refresh token has not expired.
11. Confirm that the refresh token belongs to the service.
\*Token Type: `urn:ietf:params:oauth:token-type:id_token`\*
12. Confirm that the format conforms to the JWT specification ([RFC 7519](https://www.rfc-editor.org/rfc/rfc7519.html)).
13. Check if the ID Token is encrypted and if it is encrypted, then (a)
reject the token exchange request when the \{@link
Service#isTokenExchangeEncryptedJwtRejected()
tokenExchangeEncryptedJwtRejected} flag of the service is `true`
or (b) skip remaining validation steps when the flag is `false`.
Note that Authlete does not verify an encrypted ID Token because
there is no standard way to obtain the key to decrypt the ID Token
with in the context of token exchange where the client ID for the
encrypted ID Token cannot be determined. This means that you must
verify an encrypted ID Token by yourself when one is used as an
input token with the token type
`"urn:ietf:params:oauth:token-type:id_token"`.
14. Confirm that the ID Token contains the `exp` claim and the
current time has not reached the time indicated by the claim.
15. Confirm that the ID Token contains the `iat` claim and the
current time is equal to or after the time indicated by the claim.
16. Confirm that the current time is equal to or after the time indicated
by the `nbf` claim if the ID Token contains the claim.
17. Confirm that the ID Token contains the `iss` claim and the
value is a valid URI. In addition, confirm that the URI has the
`https` scheme, no query component and no fragment component.
18. Confirm that the ID Token contains the `aud` claim and its
value is a JSON string or an array of JSON strings.
19. Confirm that the value of the `nonce` claim is a JSON string
if the ID Token contains the claim.
20. Check if the ID Token is signed and if it is not signed, then (a)
reject the token exchange request when the \{@link
Service#isTokenExchangeUnsignedJwtRejected()
tokenExchangeUnsignedJwtRejected} flag of the service is `true`
or (b) finish validation on the input token.
21. Confirm that the signature algorithm is asymmetric. This implies that
ID Tokens whose signature algorithm is symmetric (`HS256`,
`HS384` or `HS512`) cannot be used as a subject token or
an actor token with the token type
`urn:ietf:params:oauth:token-type:id_token`.
22. Verify the signature of the ID Token. Signature verification is
performed even in the case where the issuer of the ID Token is not
your service. But in that case, the issuer must support the discovery
endpoint defined in [OpenID
Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html). Otherwise, signature verification fails.
\*Token Type: `urn:ietf:params:oauth:token-type:saml1`\*
(Authlete does not perform any validation for this token type.)
\*Token Type: `urn:ietf:params:oauth:token-type:saml2`\*
(Authlete does not perform any validation for this token type.)
The specification of Token Exchange ([RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html)) is very
flexible. In other words, the specification has abandoned the task of
determining details. Therefore, for secure token exchange, you have
to complement the specification with your own rules. For that purpose,
Authlete provides some configuration options as listed below.
Authorization server implementers may utilize them and/or implement
their own rules.
In the case of \{@link Action#TOKEN\_EXCHANGE TOKEN\_EXCHANGE}, the \{@link
\#getResponseContent()} method returns `null`. You have to construct
the token response by yourself.
For example, you may generate an access token by calling Authlete's
`/api/auth/token/create` API and construct a token response like
below.
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: {challenge}
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{responseContent}
```
```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store
{
"access_token": "{@link TokenCreateResponse#getAccessToken()}",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": { @link TokenCreateResponse#getExpiresIn() },
"scope": "String.join(" ", {@link TokenCreateResponse#getScopes()})"
}
```
## JWT\_BEARER JWT\_BEARER (Authlete 2.3 onwards)
When the value of `"action"` is `"JWT_BEARER"`, it means that
the request from the client application is a valid token request with the
grant type `"urn:ietf:params:oauth:grant-type:jwt-bearer"` ([RFC 7523 JSON Web Token (JWT)
Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://www.rfc-editor.org/rfc/rfc7523.html))
and that the request has already passed the following validation steps.
1. Confirm that the `assertion` request parameter is given and its value
is not empty.
2. Confirm that the format of the assertion conforms to the JWT specification
([RFC 7519](https://www.rfc-editor.org/rfc/rfc7519.html)).
3. Check if the JWT is encrypted and if it is encrypted, then (a) reject the
token request when the \{@link Service#isJwtGrantEncryptedJwtRejected()
jwtGrantEncryptedJwtRejected} flag of the service is `true` or (b)
skip remaining validation steps when the flag is `false`. Note that
Authlete does not verify an encrypted JWT because there is no standard way
to obtain the key to decrypt the JWT with. This means that you must verify
an encrypted JWT by yourself.
4. Confirm that the JWT contains the `iss` claim and its value is a
JSON string.
5. Confirm that the JWT contains the `sub` claim and its value is a
JSON string.
6. Confirm that the JWT contains the `aud` claim and its value is
either a JSON string or an array of JSON strings.
7. Confirm that the issuer identifier of the service (cf. \{@link Service#getIssuer()})
or the URL of the token endpoint (cf. \{@link Service#getTokenEndpoint()})
is listed as audience in the `aud` claim.
8. Confirm that the JWT contains the `exp` claim and the current time
has not reached the time indicated by the claim.
9. Confirm that the current time is equal to or after the time indicated by
by the `iat` claim if the JWT contains the claim.
10. Confirm that the current time is equal to or after the time indicated by
by the `nbf` claim if the JWT contains the claim.
11. Check if the JWT is signed and if it is not signed, then (a) reject the
token request when the \{@link Service#isJwtGrantUnsignedJwtRejected()
jwtGrantUnsignedJwtRejected} flag of the service is `true` or (b)
finish validation on the JWT. Note that Authlete does not verify the
signature of the JWT because there is no standard way to obtain the key
to verify the signature of a JWT with. This means that you must verify
the signature by yourself.
Authlete provides some configuration options for the grant type as listed
below. Authorization server implementers may utilize them and/or implement
their own rules.
```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store
{
"access_token": "{@link TokenCreateResponse#getAccessToken()}",
"token_type": "Bearer",
"expires_in": {@link TokenCreateResponse#getExpiresIn()},
"scope": "String.join(" ", {@link TokenCreateResponse#getScopes()})"
}
```
Finally, note again that Authlete does not verify the signature of the JWT
specified by the `assertion` request parameter. You must verify the
signature by yourself.
# Reissue ID Token
Source: https://developers.authlete.com/api-reference/token-endpoint/reissue-id-token
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/idtoken/reissue
The API is expected to be called only when the value of the `action`
parameter in a response from the `/auth/token` API is [ID_TOKEN_REISSUABLE](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/TokenResponse.Action.html#ID_TOKEN_REISSUABLE). The purpose
of the `/idtoken/reissue` API is to generate a token response that
includes a new ID token together with a new access token and a refresh
token.
# Create Access Token
Source: https://developers.authlete.com/api-reference/token-operations/create-access-token
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/token/create
Create an access token.
# Delete Access Token
Source: https://developers.authlete.com/api-reference/token-operations/delete-access-token
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples delete /api/{serviceId}/auth/token/delete/{accessTokenIdentifier}
Delete an access token.
# List Issued Tokens
Source: https://developers.authlete.com/api-reference/token-operations/list-issued-tokens
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/{serviceId}/auth/token/get/list
Get the list of access tokens that are associated with the service.
# Revoke Access Token
Source: https://developers.authlete.com/api-reference/token-operations/revoke-access-token
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/token/revoke
Revoke an access token.
Bulk revocation with `clientIdentifier` only, `clientIdentifier` + `subject`, or `subject` only deletes at most **20 tokens per request** (the default of `token.revoke.count.max` in `ServerConfiguration.java`). If the target has more than 20 tokens, the response `count` will be 20 and the remainder is left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0.
### Safe bulk revocation under ongoing service: the lock-then-revoke pattern
When you call `/auth/token/revoke` repeatedly with `clientIdentifier` to wipe all tokens for a client that is still serving live traffic, the following problems arise:
* Each call deletes at most 20 tokens, so a loop is required.
* During the loop the client can keep issuing and refreshing tokens, so the loop may not converge, or it may collateral-delete legitimate tokens issued after the revocation started.
* For a public client whose refresh token has leaked (no `client_secret` to rotate), the attacker can keep refreshing while the cleanup runs.
The safe and reliable pattern is to **temporarily lock the client first, then revoke**.
1. Call `POST /api/client/lock_flag/update/{clientIdentifier}` with `clientLocked: true`.
* All subsequent token-endpoint calls for this client (`authorization_code`, `refresh_token`, `client_credentials`, etc.) are rejected with `invalid_client` (e.g. `A458101`).
* Existing tokens are also rejected by `/auth/introspection` with `invalid_token` (`A097303`: "The client application associated with the presented access token is locked.").
* This immediately stops the attacker's refresh path — including the public-client RT-leak case where there is no `client_secret` to rotate.
2. Call `POST /api/auth/token/revoke` with `clientIdentifier` in a loop until the response `count` reaches 0.
* Because issuance is stopped, the loop converges deterministically.
* All existing tokens are physically deleted, eliminating any leftover leaked tokens.
3. Call `POST /api/client/lock_flag/update/{clientIdentifier}` with `clientLocked: false` to return to normal operation.
* End users re-authenticate and receive fresh tokens.
# Update Access Token
Source: https://developers.authlete.com/api-reference/token-operations/update-access-token
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/token/update
Update an access token.
# Issue UserInfo Response
Source: https://developers.authlete.com/api-reference/userinfo-endpoint/issue-userinfo-response
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/userinfo/issue
This API generates an ID token.
This API is supposed to be called from within the implementation of the [userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo)
of the authorization server in order to generate an ID token. Before calling this API, a valid
response from `/auth/userinfo` API must be obtained. Then, call this API with the access token
contained in the response and the claims values of the user (subject) associated with the access
token. See **OK** written in the description of `/auth/userinfo` API for details.
The response from `/auth/userinfo/issue` API has various parameters. Among them, it is `action`
parameter that the authorization server implementation should check first because it denotes the
next action that the authorization server implementation should take. According to the value of
`action`, the service implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete. In either case, from the
viewpoint of the client application, it is an error on the server side. Therefore, the service
implementation should generate a response to the client application with HTTP status of "500 Internal
Server Error".
The parameter `responseContent` returns a string which describes the error in the format of [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage) so the userinfo
endpoint implementation can use the value of `responseContent` as the value of`WWW-Authenticate`
header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 500 Internal Server Error
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
does not contain an access token (= the request from the authorization server implementation to
Authlete does not contain `token` parameter).
The parameter `responseContent` returns a string which describes the error in the format of [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage) so the userinfo
endpoint implementation can use the value of `responseContent` as the value of`WWW-Authenticate`
header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 400 Bad Request
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that the access token does not exist, has
expired, or is not associated with any subject (= any user account).
The parameter `responseContent` returns a string which describes the error in the format of [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage) so the userinfo
endpoint implementation can use the value of `responseContent` as the value of`WWW-Authenticate`
header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## FORBIDDEN
When the value of `action` is `FORBIDDEN`, it means that the access token does not include the
`openid` scope.
The parameter `responseContent` returns a string which describes the error in the format of [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage) so the userinfo
endpoint implementation can use the value of `responseContent` as the value of`WWW-Authenticate`
header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 403 Forbidden
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## JSON
When the value of `action` is `JSON`, it means that the access token which the client application
presented is valid and an ID token was successfully generated in the format of JSON.
The userinfo endpoint implementation is expected to generate a response to the client application.
The content type of the response must be `application/json` and the response body must be an ID
token in JSON format.
The value of `responseContent` is the ID token in JSON format when `action` is `JSON`, so
a response to the client can be built like below.
```
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
{responseContent}
```
## JWT
When the value of `action` is `JWT`, it means that the access token which the client application
presented is valid and an ID token was successfully generated in the format of JWT (JSON Web Token)
([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)).
The userinfo endpoint implementation is expected to generate a response to the client application.
The content type of the response must be `application/jwt` and the response body must be an ID
token in JWT format.
The value of `responseContent` is the ID token in JSON format when `action` is `JWT`, so a response
to the client can be built like below.
```
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/jwt
{responseContent}
```
# Process UserInfo Request
Source: https://developers.authlete.com/api-reference/userinfo-endpoint/process-userinfo-request
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/auth/userinfo
This API gathers information about a user.
This API is supposed to be called from within the implementation of the [userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo)
of the authorization server in order to get information about the user that is associated with
an access token.
The response from `/auth/userinfo` API has various parameters. Among them, it is `action` parameter
that the authorization server implementation should check first because it denotes the next action
that the authorization server implementation should take. According to the value of `action`, the
service implementation must take the steps described below.
## INTERNAL\_SERVER\_ERROR
When the value of `action` is `INTERNAL_SERVER_ERROR`, it means that the request from the authorization
server implementation was wrong or that an error occurred in Authlete. In either case, from the
viewpoint of the client application, it is an error on the server side. Therefore, the service
implementation should generate a response to the client application with HTTP status of "500 Internal
Server Error".
The value of `responseContent` is a string which describes the error in the format of [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750)
(OAuth 2.0 Bearer Token Usage) so the userinfo endpoint implementation can use the value of `responseContent`
as the value of`WWW-Authenticate` header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 500 Internal Server Error
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## BAD\_REQUEST
When the value of `action` is `BAD_REQUEST`, it means that the request from the client application
does not contain an access token (= the request from the authorization server implementation to
Authlete does not contain `token` parameter).
The value of `responseContent` is a string which describes the error in the format
of [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage) so the
userinfo endpoint implementation can use the value of `responseContent` as the value of`WWW-Authenticate`
header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 400 Bad Request
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## UNAUTHORIZED
When the value of `action` is `UNAUTHORIZED`, it means that the access token does not exist, has
expired, or is not associated with any subject (= any user account).
The value of `responseContent` is a string which describes the error in the format of [RFC
6750](https://datatracker.ietf.org/doc/html/rfc6750) (OAuth 2.0 Bearer Token Usage) so the userinfo
endpoint implementation can use the value of `responseContent` as the value of`WWW-Authenticate`
header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## FORBIDDEN
When the value of `action` is `FORBIDDEN`, it means that the access token does not include the
`openid` scope.
The value of `responseContent` is a string which describes the error in the format of [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750)
(OAuth 2.0 Bearer Token Usage) so the userinfo endpoint implementation can use the value of `responseContent`
as the value of`WWW-Authenticate` header.
The following is an example response which complies with RFC 6750. Note that OpenID Connect Core
1.0 requires that an error response from userinfo endpoint comply with RFC 6750. See [5.3.3. UserInfo
Response](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError) for details.
```
HTTP/1.1 403 Forbidden
WWW-Authenticate: {responseContent}
Cache-Control: no-store
Pragma: no-cache
```
## OK
When the value of `action` is `OK`, it means that the access token which the client application
presented is valid. To be concrete, it means that the access token exists, has not expired, includes
the openid scope, and is associated with a subject (= a user account).
What the userinfo endpoint implementation should do next is to collect information about the subject
(user) from your database. The value of the `subject` is contained in the subject parameter in the
response from this API and the names of data, i.e., the claims names are contained in the claims
parameter in the response. For example, if the `subject` parameter is `joe123` and the claims
parameter is `[ "given_name", "email" ]`, you need to extract information about joe123's given name
and email from your database.
Then, call Authlete's `/auth/userinfo/issue` API with the collected information and the access token
in order to make Authlete generate an ID token.
If an error occurred during the above steps, generate an error response to the client. The response
should comply with [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750). For example, if the
subject associated with the access token does not exist in your database any longer, you may feel
like generating a response like below.
```
HTTP/1.1 400 Bad Request
WWW-Authenticate: Bearer error="invalid_token",
error_description="The subject associated with the access token does not exist."
Cache-Control: no-store
Pragma: no-cache
```
Also, an error might occur on database access. If you treat the error as an internal server error,
then the response would be like the following.
```
HTTP/1.1 500 Internal Server Error
WWW-Authenticate: Bearer error="server_error",
error_description="Failed to extract information about the subject from the database."
Cache-Control: no-store
Pragma: no-cache
```
# Get JSON Web Key Set
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/get-json-web-key-set
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/jwks
Get JSON Web Key Set for VCI
# Get JWT Issuer Information
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/get-jwt-issuer-information
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/jwtissuer
Get JWT issuer information for VCI
# Get Verifiable Credential Issuer Metadata
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/get-verifiable-credential-issuer-metadata
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/metadata
Get verifiable credential issuer metadata
# Action Handling
Source: https://developers.authlete.com/get-started/concepts/action-handling
How to branch on the action field in Core API responses and map each action to the HTTP response your authorization server returns.
Most Core API responses include an `action` field. The principle of implementing an authorization server with Authlete is simple: call a Core API, parse the response, check the value of `action`, and process accordingly.
You **NEVER** use the value of `resultCode` as a condition of processing.
Each Core API defines its own set of actions, but the values fall into two patterns — actions that map directly to an HTTP response to the client, and actions that require a second Authlete API call:
```mermaid theme={null}
flowchart TD
Start(["Response from a Core API"]) --> D{"action"}
D -->|"Direct response (e.g. LOCATION)"| L["HTTP response built from responseContent"]
D -->|"Two-step call (e.g. INTERACTION)"| Work["Server-side work (e.g. authenticate user)"]
Work --> Next["Call the next Authlete API"]
Next -.->|response carries another action| D
```
This page walks through the actions of the [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API to show how the model works, then introduces **representative** actions you will encounter in other Core APIs — the lists on this page are not exhaustive, so always consult the API reference of the API you are calling.
## Actions of /auth/authorization
A response from the [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API contains one of the following values in the `action` field:
| Action | Meaning | What your server does (example) |
| - | - | - |
| `INTERACTION` | The authorization request is valid and user interaction is required. | Continue with a second API call — see [Two-Step API Calls](/get-started/concepts/two-step-api-calls). |
| `NO_INTERACTION` | The request is valid and requires no user interaction (e.g. `prompt=none`). | Continue with a second API call — see [Two-Step API Calls](/get-started/concepts/two-step-api-calls). |
| `LOCATION` | A redirect response should be returned. | Return `302 Found` with `responseContent` as the `Location` header. |
| `FORM` | A form-post response should be returned. | Return `200 OK` with `responseContent` (an HTML form) as the body. |
| `BAD_REQUEST` | The authorization request is invalid. | Return `400 Bad Request` with `responseContent` as the body. |
| `INTERNAL_SERVER_ERROR` | An error occurred on the Authlete side or the request to Authlete was malformed. | Return `500 Internal Server Error` with `responseContent` as the body. |
Besides `action`, the response also carries the information your authorization server needs to build the authentication and consent page — such as the client's display information and the requested scopes and claims. See the [API Reference](/api-reference/authorization-endpoint/process-authorization-request) for the full response schema.
## Mapping Actions to HTTP Responses
Most actions correspond one-to-one to an HTTP response your server returns to the client, built from `responseContent`. For example, when the response from the Authlete Core API contains `action` of `BAD_REQUEST`, your authorization server returns a response like the following to the client:
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
(responseContent)
```
And when `action` is `LOCATION`:
```
HTTP/1.1 302 Found
Location: (responseContent)
Cache-Control: no-store
Pragma: no-cache
```
## Actions That Need More Steps
`INTERACTION` and `NO_INTERACTION` — along with grant-processing actions such as `PASSWORD`, `TOKEN_EXCHANGE`, and `JWT_BEARER` — cannot be completed by returning a single HTTP response. Your server needs to do its own work, such as authenticating the user and obtaining consent, and then call a second Authlete API to finish the processing. This pattern is explained in [Two-Step API Calls](/get-started/concepts/two-step-api-calls).
## Representative Actions in Other APIs
Other Core APIs follow the same convention with their own action sets. The following are representative actions — not an exhaustive list:
| Action | Examples of APIs | What your server does (example) |
| - | - | - |
| `OK` | [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request), [/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request), many others | The request was processed successfully. Typically return `200 OK` with `responseContent`, or proceed to the next step (for `/auth/userinfo`, gather claim values and call [/auth/userinfo/issue](/api-reference/userinfo-endpoint/issue-userinfo-response)). |
| `CREATED` | [/client/registration](/api-reference/client-management/create-client) | A resource was created (e.g. dynamic client registration). Return `201 Created` with `responseContent`. |
| `UNAUTHORIZED` | [/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request), [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) | The presented token is invalid. Return `401 Unauthorized` with `responseContent` as the `WWW-Authenticate` header. |
| `INVALID_CLIENT` | [/auth/token](/api-reference/token-endpoint/process-token-request), [/auth/revocation](/api-reference/revocation-endpoint/process-revocation-request) | Client authentication failed. Return `400` or `401` as described in the API reference. |
| `FORBIDDEN` | [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request), [/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request) | The access token is valid but lacks the scopes (or resources) required for the request. Return `403 Forbidden`. |
| `NOT_FOUND` | [/gm](/api-reference/grant-management-endpoint/process-grant-management-request) | The target resource does not exist (e.g. the `grant_id` specified in a Grant Management request). Return `404 Not Found`. |
| `PASSWORD`, `TOKEN_EXCHANGE`, `JWT_BEARER` | [/auth/token](/api-reference/token-endpoint/process-token-request) | Your server must perform its own validation (user credentials, subject token, assertion) before deciding to issue tokens — see [Two-Step API Calls](/get-started/concepts/two-step-api-calls). |
Newer APIs (e.g. `/vci/*`, `/gm`) distinguish where an error originates: `CALLER_ERROR` means the request from your implementation is wrong, while `AUTHLETE_ERROR` indicates an error inside Authlete.
# API Authentication
Source: https://developers.authlete.com/get-started/concepts/authentication
How to authenticate with Authlete APIs: bearer tokens, Service Access Tokens, and Organization Access Tokens, and how to obtain them.
This page is for Authlete 3.0. For 2.x, see [API Authentication (2.x)](/v2/get-started/quickstarts/authentication) — 2.x uses API key and API secret (HTTP Basic auth) rather than Bearer tokens.
# API Authentication
All API endpoints are secured using **Bearer token** authentication. You must include an access token in every request:
```http theme={null}
Authorization: Bearer YOUR_ACCESS_TOKEN
```
Authlete supports two types of access tokens: **Service Access Token** (scoped to a single service) and **Organization Token** (scoped to your entire organization). The sections below explain how to get each and when to use them.
***
## Getting your access token
### Service Access Token
Scoped to a **single service** (one authorization server instance). Use it for the [Core API](/get-started/concepts/request-and-response) (authorization, token, introspection) and for managing that service’s clients and settings.
**How to get a Service Access Token**
1. Log in to the [Authlete Console](https://console.authlete.com/).
2. Open the **Organization** that owns the service, then open the **Service** to view its **Service Overview**.
3. Click **Service Settings** (next to **Create Client**).
4. On the **General** tab, scroll to **Service Access Tokens**, then click **Create Token**.
5. In the dialog, set a **token name** and choose permissions — either a preset (Authorization Server for `use_service`, Resource Server for `use_introspection`, …) or individual permissions. See [Permissions](#permissions) below.
6. Click **Create Token** to generate it, then **copy the token immediately** — it is shown only once. Store it in a secure place (e.g. environment variables or a secret manager).
Use this token in the `Authorization: Bearer ` header when calling Authlete APIs for that service.
***
### Organization Token
Scoped to your **entire organization**. An Organization Token carries the broadest access: with the right permissions it can call any API on **any service** in the organization — including the Core API — plus organization-level operations such as creating services and reading the organization audit log. It is typically used for organization-wide management and for automation that spans multiple services (e.g. CI/CD, Terraform).
**How to get an Organization Token**
1. Log in to the [Authlete Console](https://console.authlete.com/).
2. Open the **Organization** you want a token for (or create one first).
3. In the sidebar, open **Organization Settings**.
4. On the **General** tab, scroll to **Organization Access Tokens**, then click **Create Token**.
5. In the dialog, set a **token name** and choose the org-level permissions you need — for example the **Administrator** preset (`create_service`, `modify_service`, `view_audit_log`) for CI/CD provisioning, or `view_audit_log` for read-only audit access. See [Permissions](#permissions) below.
6. Click **Create Token** to generate it, then **copy the token immediately** — it is shown only once. Store it securely.
Use this token in the `Authorization: Bearer ` header when calling Authlete APIs that require organization-level access.
***
**Important:** Tokens inherit the permissions of the account that creates them. **Service tokens** can only access their specific service. **Organization tokens** can access all services within your organization. Choose the token type and permissions that match what your application needs.
***
## Which token to use
Based on the principle of **least privilege**, here is the recommended token type for each use case — pick the narrowest one that covers the job:
| Use case | Token type |
| - | - |
| Core API (authorization, token, introspection) | **Service Access Token** |
| Managing a single service (clients, endpoints, config) | **Service Access Token** |
| Managing the organization or multiple services | **Organization Token** |
| Automation / CI/CD that creates or updates services | **Organization Token** |
For Core API calls, prefer a Service Access Token: it confines access to a single service, whereas an Organization Token would expose every service if it leaked. Reach for an Organization Token when the work genuinely spans multiple services or needs organization-level operations.
***
## Permissions
Whichever token type you choose, the token carries **fine-grained permissions** that decide which Authlete APIs it can call — for example `use_service` for an authorization server that calls the Core API, `use_introspection` for a resource server, or `create_client` for provisioning. You pick these when you create the token, so grant only what the workload needs (**least privilege**).
For the full list of permissions and the APIs each one allows, see [Service Access Tokens](/deployment-and-operations/access-control/service-access-tokens) in the developer guide.
***
## Token security best practices
* **Never commit tokens to version control** — Store them in environment variables or a secure secret manager (e.g. AWS Secrets Manager, HashiCorp Vault).
* **Rotate regularly** — Generate new tokens periodically and revoke old ones from the console.
* **Scope appropriately** — Request only the permissions your application needs when creating a token.
* **Revoke unused tokens** — Delete tokens you are no longer using from the console.
***
## Quick test
Verify your token works with a simple API call:
```bash theme={null}
curl -X GET "https://us.authlete.com/api/service/get/list" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```
Use a **Service Access Token** for service-scoped endpoints or an **Organization Token** for org-scoped endpoints. Replace `us.authlete.com` with your cluster host (e.g. `eu.authlete.com`, `jp.authlete.com`) if you use a different region.
***
## Next steps
* [Getting Started](/get-started/quickstarts/getting-started) — Create an account, organization, and service.
* [Using Demo Authorization Server](/get-started/quickstarts/using-demo-authorization-server) — Create a service, generate a Service Access Token, and run a demo authorization server.
* [OAuth 2.0 Basics](/get-started/quickstarts/oauth-2-0-basics) — A tutorial on how an authorization server leverages Authlete APIs.
# Client Management
Source: https://developers.authlete.com/get-started/concepts/client-management
Registering and managing client applications with the Management API: client CRUD operations, the management console, and dynamic client registration.
A **client** represents an application that requests authorization from your service. The Management API provides CRUD operations for clients, so you can register and maintain them programmatically — for example from your own developer portal or provisioning pipeline.
## Client Management APIs
| API | Purpose |
| - | - |
| [/client/create](/api-reference/client-management/create-client) | Register a new client. |
| [/client/get](/api-reference/client-management/get-client) | Retrieve a client's settings. |
| [/client/get/list](/api-reference/client-management/list-clients) | List clients registered in the service. |
| [/client/update](/api-reference/client-management/update-client) | Update a client's settings. |
You can also manage clients interactively in the [Authlete Management Console](https://console.authlete.com), which uses these APIs under the hood.
## End-User Grants per Client
Authlete records which client applications each end-user has authorized. The Management API exposes this relationship so you can build a "manage your authorized apps" screen:
* [/client/authorization/get/list](/api-reference/client-management/get-authorized-applications) — list the client applications an end-user has authorized
* [/client/authorization/update](/api-reference/client-management/update-client-tokens) — update the attributes (e.g. scopes) of the access tokens issued to a client for an end-user
* [/client/authorization/delete](/api-reference/client-management/delete-client-tokens) — delete all access tokens issued to a client for an end-user
See [Authlete's Policy on Managing Clients Which Have Been Authorized by User](/configuration-reference/client-management/authletes-policy-on-managing-clients-which-have-been-authorized-by-user) for the underlying model.
## Related Concepts
* [Using Client ID Alias](/v2/configuration-reference/client-management/client-id-alias) — assigning an arbitrary client ID in Authlete 2.x
* [Client Attributes](/configuration-reference/client-management/client-attributes) — attaching arbitrary key-value metadata to clients
## Next Steps
* [Token Management](/get-started/concepts/token-management) — operating on tokens issued to clients
* [Service Management](/get-started/concepts/service-management) — the service that clients belong to
# Management API Overview
Source: https://developers.authlete.com/get-started/concepts/overview
What the Management API is for: configuring services, managing clients, and operating issued tokens — out-of-band from the OAuth/OIDC protocol flows handled by the Core API.
The **Management API** is the group of Authlete APIs that manage the configuration of your authorization server and the entities inside it — services, clients, and tokens. Unlike the [Core API](/get-started/concepts/request-and-response), which handles OAuth 2.0 / OpenID Connect protocol requests at runtime, the Management API is used out-of-band to set up and operate the authorization server.
## Typical Use Cases
* **Provisioning** — Create and configure services from infrastructure-as-code pipelines, replicate environments across production, staging, and development.
* **Client lifecycle** — Register new client applications from your developer portal or admin tools, rotate secrets, deactivate clients.
* **Token administration** — List access tokens issued for an end-user or client, revoke tokens when a device is lost or abuse is reported, migrate tokens from a legacy authorization server.
* **End-user authorization screens** — Show the list of applications an end-user has authorized, and let them revoke individual clients.
* **Operations and audit** — Inspect issued tokens, adjust expiration policies, audit which clients exist.
## Authentication
Management API calls require a **Service Access Token** with the permissions appropriate for each operation. See [Authentication](/get-started/concepts/authentication) for how to create a token and assign permissions.
Service creation and deletion are exceptions: they live on the **IdP Endpoint** and require an **Organization Token**. See [Service Management](/get-started/concepts/service-management).
## Management API and the Management Console
The [Authlete Management Console](https://console.authlete.com) is built on top of the Management API. The operations you can perform in the console — creating services, registering clients, viewing tokens — are exposed through the same APIs, so you can choose whichever fits your workflow:
* Interactive changes by humans → use the console.
* Repeatable, automated, or auditable changes → use the Management API directly.
## Next Steps
* [Service Management](/get-started/concepts/service-management) — service-level settings and lifecycle.
* [Client Management](/get-started/concepts/client-management) — registering and operating client applications.
* [Token Management](/get-started/concepts/token-management) — administrative operations on issued tokens.
# Request and Response
Source: https://developers.authlete.com/get-started/concepts/request-and-response
The common request and response structure of Authlete's Core API: passing client request parameters as-is and interpreting resultCode, action, and responseContent.
The **Core API** is the group of Authlete APIs that process OAuth 2.0 and OpenID Connect protocol requests, such as [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request), [/auth/token](/api-reference/token-endpoint/process-token-request), and [/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request). Your authorization server receives requests from client applications at its endpoints and delegates the protocol processing to these APIs.
This page explains the request and response structure shared by the Core APIs.
```mermaid theme={null}
sequenceDiagram
autonumber
participant C as Client Application
participant AS as Authorization Server
participant API as Authlete Core API
C ->> AS: Protocol request (e.g. authorization request)
Note over AS: Pass parameters as-is
AS ->> API: POST /auth/... (JSON body)
API -->> AS: resultCode, action, responseContent, ...
Note over AS: Branch on action
AS -->> C: HTTP response built from responseContent
```
## Request Format
Core APIs accept an HTTP POST request with a JSON body. Authenticate with a Service Access Token in the `Authorization` header:
```bash theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "parameters": "response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb%2Fexample.com" }'
```
Replace `us.authlete.com` with your cluster host (e.g. `eu.authlete.com`, `jp.authlete.com`) if you use a different region. See [Authentication](/get-started/concepts/authentication) for how to obtain a Service Access Token.
### Main Request Parameters
The exact set of request parameters differs per API and depends on which OAuth/OIDC specifications your service supports, so it cannot be fully generalized — always check the [API Reference](/api-reference) of the API you are calling. That said, the following parameters appear across many Core APIs:
| Parameter | Description |
| - | - |
| `parameters` | The request your endpoint received from the client, passed as-is: the query string for the authorization endpoint, or the `application/x-www-form-urlencoded` request body for the token endpoint. |
| `clientId` | The client ID. When the client authenticates with `client_secret_basic`, extract it from the `Authorization: Basic` header of the request to your token endpoint. |
| `clientSecret` | The client secret, extracted from the `Authorization: Basic` header in the same way. |
| `clientCertificate` | The client certificate from the mutual TLS connection. Required when supporting mTLS client authentication or certificate-bound access tokens ([RFC 8705](https://www.rfc-editor.org/rfc/rfc8705)). |
| `ticket` | The ticket returned by the first API of a two-step call (e.g. [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request)), passed to the second API (e.g. [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)). See [Two-Step API Calls](/get-started/concepts/two-step-api-calls). |
| `token` | The access token to inspect, for APIs such as [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) and [/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request). |
| `subject` | The unique identifier of the end-user your server has authenticated, passed to issuing APIs such as [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response). |
Supporting additional specifications adds parameters accordingly — for example, DPoP ([RFC 9449](https://www.rfc-editor.org/rfc/rfc9449)) adds `dpop`, `htm`, and `htu` to token and introspection requests.
## Pass Client Request Parameters As-Is
Your authorization server is expected to pass the parameters it receives at its authorization endpoint and token endpoint to the corresponding Authlete APIs **as-is — you usually MUST NOT modify or pre-process them**.
For example, if you want to check the values of the `scope` parameter in an authorization request, you don't have to verify them before invoking the [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API. Instead, include the whole query string as the value of the `parameters` field. Authlete verifies the request and returns the validated scope values in its response, which your authorization server can then use safely.
Implementing your own verification or modification before invoking [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) or [/auth/token](/api-reference/token-endpoint/process-token-request) is risky. You might bypass Authlete's protocol handling and error checking, and in the worst case your authorization server could become non-compliant with OAuth 2.0 / OpenID Connect.
## Response Format
Core API responses share the following fields:
| Field | Description |
| - | - |
| `resultCode` | A code identifying the processing result (e.g. `A004001`). Useful for logging and troubleshooting. |
| `resultMessage` | A human-readable description of the result. |
| `action` | The next step your server should take. **This is the field your implementation must branch on.** |
| `responseContent` | Content to use when building the response to the client, such as a JSON body or a redirect URI with error parameters. |
The `action` field is the heart of the Core API programming model: it tells your authorization server what kind of HTTP response to return to the client, or which Authlete API to call next. See [Action Handling](/get-started/concepts/action-handling) for details.
Never branch your processing on `resultCode`. Result codes are diagnostic information; the contract for what to do next is expressed solely by `action`.
# Service Management
Source: https://developers.authlete.com/get-started/concepts/service-management
Managing services — authorization server instances — with the Management API: service CRUD operations, configuration retrieval, and the management console.
A **service** represents one authorization server instance. All settings that define your authorization server's behavior — supported grant types, token expiration, signing keys, endpoint configuration, and so on — belong to a service. Clients and tokens always live inside a service.
## Service Management APIs
Service retrieval, listing, and updates are served on the Authlete API host:
| API | Purpose |
| - | - |
| [/service/get](/api-reference/service-management/get-service) | Retrieve a service's settings. |
| [/service/get/list](/api-reference/service-management/list-services) | List services you can access. |
| [/service/update](/api-reference/service-management/update-service) | Update a service's settings. |
Day-to-day configuration is usually done in the [Authlete Management Console](https://console.authlete.com); the APIs are useful for automation such as infrastructure-as-code pipelines and environment replication.
[/service/update](/api-reference/service-management/update-service) replaces the whole service configuration with the request body. Retrieve the current settings with [/service/get](/api-reference/service-management/get-service) first, modify only the fields you need, and send the complete object back.
### Creating and deleting services (IdP Endpoint)
Service creation and deletion live on a **separate host — the IdP Endpoint** — and require an **Organization Token**. On Shared Cloud the IdP host is `login.authlete.com`.
| Operation | Endpoint (Shared Cloud) |
| - | - |
| Create a service | `POST https://login.authlete.com/api/service` |
| Delete a service | `POST https://login.authlete.com/api/service/remove` |
On Dedicated Cloud and Self-Managed (on-premises) deployments, replace `login.authlete.com` with the IdP host of your environment.
Calling service creation directly against the Authlete API host creates the service but does not link it to your organization — the service will not appear in the Console and cannot be operated with Organization Tokens. Always use the IdP Endpoint for creation and deletion.
## What a Service Defines
Service settings cover, among others:
* Supported grant types, response types, and scopes
* Token settings — formats and expiration of access tokens, refresh tokens, and ID tokens
* Endpoint URLs published in the discovery document
* Keys for signing and encryption (JWK Set)
For a walk-through of the individual settings, see [Service Management Guide](/configuration-reference/service-settings/service-management).
## Next Steps
* [Client Management](/get-started/concepts/client-management) — registering clients in your service
* [Token Management](/get-started/concepts/token-management) — operating on tokens issued by your service
# Token Management
Source: https://developers.authlete.com/get-started/concepts/token-management
Managing issued access tokens and refresh tokens outside the protocol flows: listing, creating, updating, and revoking tokens with the Management API.
While the Core API issues and validates tokens as part of OAuth/OIDC protocol flows, the **Management API** lets you operate on issued tokens directly — outside the flows. Typical use cases include building an administration screen, revoking tokens when a user reports a stolen device, or migrating tokens from an existing authorization server.
## Token Operation APIs
| API | Purpose |
| - | - |
| [/auth/token/get/list](/api-reference/token-operations/list-issued-tokens) | List issued tokens. You can filter by client and end-user (subject). |
| [/auth/token/create](/api-reference/token-operations/create-access-token) | Create an access token without going through an authorization flow. Useful for migration or testing. |
| [/auth/token/update](/api-reference/token-operations/update-access-token) | Update properties of an issued token, such as its expiration date and scopes. |
| [/auth/token/revoke](/api-reference/token-operations/revoke-access-token) | Revoke tokens. You can revoke by token identifier, or revoke all tokens of a client or an end-user at once. |
| [/auth/token/delete](/api-reference/token-operations/delete-access-token) | Delete a token entry itself from the database. |
Revocation initiated by a *client application* (RFC 7009) is part of the protocol flows and is handled by the Core API [/auth/revocation](/api-reference/revocation-endpoint/process-revocation-request). The token operation APIs above are for *administrative* operations performed by your server or back-office tools.
## Relation to Client and Service Lifecycle
Token lifecycle is tied to the entities that issued them:
* Deleting a client affects its issued tokens. See [Token Management Policy When Deleting Clients](/configuration-reference/client-management/token-management-policy-when-deleting-clients).
* Authlete tracks which clients an end-user has authorized, which you can manage per user. See [Authlete's Policy on Managing Clients Which Have Been Authorized by User](/configuration-reference/client-management/authletes-policy-on-managing-clients-which-have-been-authorized-by-user).
## Next Steps
* [Client Management](/get-started/concepts/client-management) — managing the clients that tokens are issued to
* [Service Management](/get-started/concepts/service-management) — service-level settings such as token expiration defaults
# Two-Step API Calls
Source: https://developers.authlete.com/get-started/concepts/two-step-api-calls
How actions such as INTERACTION are completed with a second API call — /auth/authorization/issue or /auth/authorization/fail — linked by a ticket.
Some actions cannot be completed in a single API call. When the [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API returns `action=INTERACTION`, your authorization server must authenticate the user and obtain consent before the authorization request can be answered — and that answer is produced by a **second** Authlete API call.
## Two Types of Authorization Endpoint APIs
The [Authorization Endpoint APIs](/api-reference/authorization-endpoint) consist of two types:
1. An API that interprets the authorization request and provides the information needed for the next step, such as end-user authentication:
* [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request)
2. APIs that issue tokens or codes, or return errors:
* [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response), [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request)
The typical flow for `action=INTERACTION` looks like this:
```mermaid theme={null}
sequenceDiagram
autonumber
participant UA as Web Browser
participant AS as Authorization Server
participant API as Authlete Core API
UA ->> AS: Authorization request
AS ->> API: POST /auth/authorization
API -->> AS: action=INTERACTION, ticket
AS ->> UA: Authentication and consent page
UA ->> AS: User authenticates and consents
AS ->> API: POST /auth/authorization/issue (ticket)
API -->> AS: action=LOCATION, responseContent
AS ->> UA: 302 Found (authorization code)
```
## Flow Steps
### The Authorization Request and the First Call
An OAuth 2.0 / OpenID Connect flow begins when a client redirects the end-user's browser to your authorization server's authorization endpoint. The request sent at that point — a query string containing `response_type`, `client_id`, `redirect_uri`, `scope`, and so on — is the "Authorization request" in the diagram above.
Your server passes the request through to [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) unmodified (see [Request and Response](/get-started/concepts/request-and-response) for details). When the authorization request requires interaction with the end-user, Authlete returns a response like this:
```json theme={null}
{
"action": "INTERACTION",
"ticket": "cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE",
"client": { "..." },
"service": { "..." }
}
```
* `action=INTERACTION` means the end-user must be involved — authenticated and asked for consent — before the authorization request can be answered.
* `ticket` carries the processing over to the second call (see [The Ticket](#the-ticket) below for its properties).
* The response also carries client and service information, along with the scopes and claims the client requested. Your server uses these to build the consent screen.
### Presenting the Authentication and Consent Screen
**Rendering the authentication/consent screen, authenticating the user, and obtaining consent are all your authorization server's responsibility** — Authlete does not render these screens. Your server uses the information from the first response to present the consent screen. Meanwhile, hold the `ticket` on the server side (for example, in the session) until the second call.
### The Second Call and Building the Authorization Response
Once the user has authenticated and consented, your server makes the second call to [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response). This request includes the `ticket` received in the first call and the `subject` — the identifier of the authenticated end-user.
```bash theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization/issue \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "ticket": "{the ticket from the first response}", "subject": "{the authenticated end-user identifier}" }'
```
If you use a different region, replace `us.authlete.com` with your cluster host (e.g. `eu.authlete.com`, `jp.authlete.com`). See [Authentication](/get-started/concepts/authentication) for how to obtain a Service Access Token.
When issuing an OpenID Connect ID token, you can also pass `authTime` (the time the user was authenticated), `acr` (the authentication context class), the requested claim values, and more. See the [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API reference for the full list of parameters.
On success, the response `action` is typically `LOCATION`, and `responseContent` holds the redirect URL containing the authorization code. Your server uses this value to return `302 Found` to the browser, delivering the authorization response to the client. Handle the `action` of this second response exactly as described in [Action Handling](/get-started/concepts/action-handling).
If the user cancels or authentication fails, call [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) — with the same `ticket` — instead of [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response), and Authlete builds the appropriate error response for the client.
## The Ticket
As shown above, a ticket is issued in the response of the first call and consumed by the second call to complete the processing — it is a single-use value. Its properties:
* A ticket expires in 24 hours. Expired tickets are deleted from Authlete's database.
* A ticket can be used only once. It is removed right after [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) or [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) successfully processes a request including it.
* Using a ticket that has already been used or expired results in an error like this:
```
[A041202] There is no entity having the ticket specified
in the /api/auth/authorization/issue request (ticket = {Ticket}).
```
Tickets are designed to be used only between your authorization server and the Authlete server. Do not expose them to user agents such as web browsers — for example, never use a ticket to manage browser sessions.
## The Same Model in Other APIs
The userinfo endpoint is a good example of the same model. The [/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request) API validates the access token presented by the client and returns an `action`. When the action is `OK`, your server gathers the end-user's claim values and calls [/auth/userinfo/issue](/api-reference/userinfo-endpoint/issue-userinfo-response) to build the userinfo response — Authlete formats it as plain JSON or as a signed/encrypted JWT according to the client's configuration. The userinfo endpoint does not use a ticket; here the access token itself carries the context between the two calls.
The token endpoint also has two-step variants: when your service supports the Resource Owner Password Credentials grant, the [/auth/token](/api-reference/token-endpoint/process-token-request) API returns `action=PASSWORD`, and your server validates the end-user's credentials before calling [/auth/token/issue](/api-reference/token-endpoint/issue-token-response) or [/auth/token/fail](/api-reference/token-endpoint/fail-token-request). The same pattern applies to `TOKEN_EXCHANGE` ([RFC 8693](https://www.rfc-editor.org/rfc/rfc8693)) and `JWT_BEARER` ([RFC 7523](https://www.rfc-editor.org/rfc/rfc7523)), where your server validates the subject token or assertion.
Once you understand the `action`-based model and the two-step pattern, every Core API follows naturally from its API reference.
# AI Agent Integration (llms.txt / MCP)
Source: https://developers.authlete.com/get-started/developer-resources/ai-agent-integration
Explains how to load the Authlete documentation into AI coding tools through llms.txt, Markdown pages, and the MCP server. No authentication or sign-up is required.
This documentation site is also published in formats that AI coding tools can read directly: [llms.txt](https://llmstxt.org/), a Markdown version of each page, and an [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server. With these formats, your tools can search and read API specifications, settings, and implementation guides. No authentication or prior sign-up is required.
You can use these formats to check specifications while implementing, and also to investigate errors or confirm behavior after release.
## Loading the documentation by URL
To give your AI agent the documentation, pass it one of the following URLs. No tool-side setup is needed.
| Purpose | URL |
| - | - |
| List of all pages | `https://developers.authlete.com/llms.txt` |
| Full text of all pages in one file | `https://developers.authlete.com/llms-full.txt` |
| A single page as Markdown (example: Action Handling) | `https://developers.authlete.com/get-started/concepts/action-handling.md` |
* `llms.txt` links to separate indexes for English and Japanese, and for 3.x and 2.x.
* `llms-full.txt` contains the English 3.x pages only. Its size is about 2.6 MB. For Japanese or 2.x pages, follow the indexes in `llms.txt` and read the pages you need.
* Appending `.md` to a page URL returns the Markdown version of the page. Japanese pages have `/ja/` at the start of the path, for example `https://developers.authlete.com/ja/get-started/concepts/action-handling.md`.
## Page menu actions
The menu next to **Copy page** at the top of each page provides the following actions.
| Action | Description |
| - | - |
| **Copy page** | Copies the page as Markdown |
| **View as Markdown** | Opens the page as Markdown |
| **Open in ChatGPT** / **Open in Claude** | Starts a conversation about the page |
| **Copy MCP Server** | Copies the MCP server URL |
| **Connect to Cursor** | Installs the MCP server in Cursor |
## Connecting to the MCP server
If your tool supports MCP, register the MCP server endpoint `https://developers.authlete.com/mcp`. The tool can then search and read the documentation directly.
```text theme={null}
https://developers.authlete.com/mcp
```
The MCP server also contains the Authlete 2.x documentation. If `version` is not specified, search results can include 2.x pages. Tell your AI agent which Authlete version you use, and have it specify `version` in `search_authlete_documentation` (`3.x` for Authlete 3.0).
The transport is HTTP. No authentication is required. The configuration format depends on the tool; see your tool's documentation for details. The following examples register the server as `authlete-docs`.
```bash Claude Code theme={null}
claude mcp add --transport http authlete-docs https://developers.authlete.com/mcp
```
```json Cursor (.cursor/mcp.json) theme={null}
{
"mcpServers": {
"authlete-docs": {
"url": "https://developers.authlete.com/mcp"
}
}
}
```
```json VS Code (.vscode/mcp.json) theme={null}
{
"servers": {
"authlete-docs": {
"type": "http",
"url": "https://developers.authlete.com/mcp"
}
}
}
```
```toml Codex (~/.codex/config.toml) theme={null}
[mcp_servers.authlete-docs]
url = "https://developers.authlete.com/mcp"
```
If you commit a project-level configuration file, such as `.cursor/mcp.json` or `.vscode/mcp.json`, to your repository, everyone on the project gets the same setup.
### MCP server tools
The MCP server provides the following three tools.
| Tool | Description |
| - | - |
| `search_authlete_documentation` | Searches the documentation. `language` (`en` by default, `ja` for Japanese) and `version` (`3.x` or `2.x`) narrow the results. |
| `query_docs_filesystem_authlete_documentation` | Reads pages and the OpenAPI specification with read-only commands such as `rg`, `cat`, and `tree`. |
| `submit_feedback` | Reports a documentation page that is incorrect, outdated, or incomplete to the Authlete documentation team. |
## Using AI agent answers
* Authlete 3.0 is the current version. Documentation for Authlete 2.x (pages under `/v2/`) is for users of the legacy version.
* The documentation is updated regularly. Do not rely on an AI agent's answer as is; confirm it on the relevant page.
* If you cannot find what you need in the documentation, do not guess. [Contact us](https://www.authlete.com/contact/).
# GitHub
Source: https://developers.authlete.com/get-started/developer-resources/github
TODO: Add content.
# Java SDK
Source: https://developers.authlete.com/get-started/developer-resources/java-sdk
# Reference Implementations
Source: https://developers.authlete.com/get-started/developer-resources/leveraging-reference-implementations
Develop your own authorization servers efficiently with Authlete's publicly available and open-sourced reference implementations downloadable from GitHub. These are also useful for direct interaction with Authlete APIs.
*Reference implementations in Authlete 's architecture*
Almost all of Authlete's features are [exposed as APIs](https://docs.authlete.com/) . "Reference implementations" utilizing the APIs are also publicly available and open-sourced. You may be able to efficiently develop your own authorization servers with them. These implementations are downloadable from GitHub.
[Authlete, Inc. · GitHub](https://github.com/authlete)
The reference implementations are useful even if you would like to directly interact with Authlete APIs. Codes and documents in the implementations would tell you what you should do pre and post processing for the APIs. For example, there are JavaDoc documents of classes representing responses of Authlete APIs ("\*Response" e.g. [AuthorizationResponse](http://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationResponse.html) under [com.authlete.common.dto](https://github.com/authlete/authlete-java-common/tree/master/src/main/java/com/authlete/common/dto) package in [authlete-java-common](https://github.com/authlete/authlete-java-common) library). They explain details on expected post processing in authorization server on receiving a response from Authlete APIs.
Popular reference implementations are as follows.
* Java
* [authlete-java-common](https://github.com/authlete/authlete-java-common): Authlete Common Library for Java
* [authlete-java-jaxrs](https://github.com/authlete/authlete-java-jaxrs): Authlete Library for JAX-RS (Java)
* [java-oauth-server](https://github.com/authlete/java-oauth-server): Authorization Server Implementation in Java
* C#
* [authlete-csharp](https://github.com/authlete/authlete-csharp): C# library for Authlete Web APIs
* [OAuth 2.0 and OpenID Connect implementation in C# (Authlete)](https://medium.com/@darutk/oauth-2-0-and-openid-connect-implementation-in-c-authlete-8a8f9efc9361)
* PHP
* [authlete-php](https://github.com/authlete/authlete-php): PHP library for Authlete Web APIs
* [authlete-php-laravel](https://github.com/authlete/authlete-php-laravel): A Laravel library for OAuth 2.0 and OpenID Connect
* [OAuth 2.0 and OpenID Connect Implementation in Laravel (Authlete)](https://medium.com/@darutk/oauth-2-0-and-openid-connect-implementation-in-laravel-authlete-4d32802ab335)
* Python
* [authlete-python](https://github.com/authlete/authlete-python): Authlete Library for Python
# OpenAPI
Source: https://developers.authlete.com/get-started/developer-resources/openapi
# Ruby SDK
Source: https://developers.authlete.com/get-started/developer-resources/ruby-sdk
TODO: Add content.
# TypeScript SDK
Source: https://developers.authlete.com/get-started/developer-resources/typescript-sdk
# Architecture
Source: https://developers.authlete.com/get-started/introduction-to-authlete/architecture
Authlete acts as the backend engine for OAuth 2.0 and OpenID Connect processing. It is not directly accessed by the end user or the client application. Instead, it is called by the service provider’s own systems, mainly the Authorization Server and API Server.
## Overall Architecture
```mermaid placement="top-right" theme={null}
%%{init: {
'themeVariables': {
'fontSize': '24px',
'nodeTextMargin': 40
},
'flowchart': {
'nodeSpacing': 40,
'rankSpacing': 40,
'padding': 5
}
}}%%
graph LR
subgraph user[End User]
direction TB
ro[Resource Owner]
ua[User Agent]
ro --> ua
end
subgraph app[Client Application]
direction TB
client[OAuth Client / OIDC RP]
end
subgraph provider[Service Provider]
direction TB
subgraph auth[Authentication Server]
direction TB
user_authn[User Authentication]
user_db[User Database]
user_authn --> user_db
end
subgraph as[Authorization Server]
direction TB
oauth_ep[OAuth/OIDC Endpoints]
authz_decision[Authorization Decision]
end
subgraph api[API Server]
direction TB
rs[Resource Server]
end
end
subgraph authlete[Authlete]
direction TB
protocol_processing[Protocol Processing]
token_management[Token Management]
client_management[Client Management]
service_management[Service Management]
end
%% User interaction with client
ua --> client
%% User authentication / consent
ua -- Login / Consent --> user_authn
%% Authorization request via User Agent
ua -- Authorization Request --> oauth_ep
%% Direct client-to-AS communication
client -- Token Request --> oauth_ep
%% Authorization server internal processing
oauth_ep --> authz_decision
oauth_ep --> authlete
%% API access
client -- API Request --> rs
rs --> authlete
```
The diagram has four main areas:
| Area | Role |
| - | - |
| End User | The actual user and their browser or user agent |
| Client Application | The OAuth Client / OIDC Relying Party, e.g., a web or mobile application |
| Service Provider | The system that manages the Authorization Server, Authentication Server, and API Server |
| Authlete | Backend service for OAuth/OIDC protocol logic, token management, client management, and service management |
***
## What Authlete Is Responsible For
Inside Authlete, the diagram shows four main responsibilities.
### Protocol Processing
Authlete handles the OAuth 2.0 and OpenID Connect protocol logic. For example, it helps process authorization requests, token requests, introspection requests, revocation requests, and other OAuth/OIDC-related operations.
### Token Management
Authlete manages tokens such as:
* Authorization codes
* Access tokens
* Refresh tokens
* ID tokens, in OpenID Connect scenarios
The Authorization Server exposes the endpoint to the client, but Authlete can handle much of the token-related logic behind the scenes.
### Client Management
Authlete manages OAuth/OIDC client information, such as:
* Client IDs and credentials (e.g., client secrets, JWKs)
* Redirect URIs
* Allowed grant types
* Allowed scopes
### Service Management
Authlete also manages service-level settings, such as:
* Issuer information
* Supported OAuth/OIDC flows
* Supported scopes
* Signing algorithms
* Service configuration
## What the Service Provider Still Owns
Even when using Authlete, the Service Provider still runs several important components.
### Authorization Server
The Service Provider owns the OAuth/OIDC endpoints, such as:
* Authorization endpoint
* Token endpoint
* Revocation endpoint
* Introspection endpoint (for Resource Servers)
* UserInfo endpoint (for OpenID Connect Relying Parties)
* Well-known configuration endpoint
However, these endpoints call Authlete to perform the actual OAuth/OIDC processing.
### Authentication Server
The Service Provider owns user authentication.
That means the following remain outside Authlete:
* Login screens
* User authentication logic
* User database
Authlete does not directly authenticate the user. Instead, it works with the result of authentication performed by the Service Provider.
### API Server
The API Server contains the Resource Server.
When the Client Application sends an API request with an access token, the Resource Server may call Authlete to validate or introspect the token.
# Typical Request Flow
A typical flow from the diagram is:
1. The End User uses a User Agent, such as a browser.
2. The User Agent interacts with the Client Application.
3. The Client Application sends an authorization request to the Authorization Server via the User Agent.
4. **The Authorization Server calls Authlete to process the request.**
5. The user is redirected or guided to login and consent.
6. The Authentication Server verifies the user using the User Database.
7. The Authorization Server makes the authorization decision.
8. **The Authorization Server calls Authlete to create an authorization response that includes an authorization code.**
9. The Authorization Server sends the authorization response back to the Client Application via the User Agent.
10. The Client Application calls the Authorization Server to exchange the authorization code for an access token at the token endpoint.
11. **The Authorization Server calls Authlete to process the token request.**
12. The Authorization Server sends the token response back to the Client Application.
13. The Client Application uses the token to call the API Server.
14. **The Resource Server may call Authlete to validate the token.**
15. The API Server responds to the Client Application.
```mermaid placement="top-right" theme={null}
sequenceDiagram
box End User
participant EndUser as End User
participant UserAgent as User Agent
end
box Client Application
participant ClientApp as Client Application
end
box Service Provider
participant AuthzServer as Authorization Server
participant UserAuthn as Authentication Server
participant APIServer as Resource Server
end
box Authlete
participant Authlete as Authlete
end
EndUser->>UserAgent: Use browser
UserAgent->>ClientApp: Interact with app
ClientApp-->>UserAgent: Authorization request
UserAgent->>AuthzServer: Authorization request
AuthzServer->>Authlete: Send authorization request content
Authlete-->>AuthzServer: "ticket" and other data (e.g., client info, requested scopes, etc.)
AuthzServer->>UserAuthn: Process user authentication
UserAuthn<<->>UserAgent: Verify user
UserAuthn->>UserAuthn: Verify user credentials
UserAuthn-->>AuthzServer: Authentication result
AuthzServer->>AuthzServer: Make authorization decision
AuthzServer->>Authlete: Send "ticket" and other data (e.g., subject identifier, claims, etc.)
Authlete-->>AuthzServer: Authorization response data
AuthzServer-->>UserAgent: Send authorization response
UserAgent->>ClientApp: Send authorization response
ClientApp->>AuthzServer: Token request
AuthzServer->>Authlete: Send token request content
Authlete-->>AuthzServer: Token response data
AuthzServer-->>ClientApp: Send token response
ClientApp->>APIServer: API request with token
APIServer->>Authlete: Token introspection (optional)
Authlete-->>APIServer: Token introspection result (optional)
APIServer-->>ClientApp: API response
ClientApp-->>UserAgent: Response
```
# Overview
Source: https://developers.authlete.com/get-started/introduction-to-authlete/overview
Authlete is a headless solution and provides a set of APIs specialized for OAuth 2.0 and OpenID Connect protocol processing and token lifecycle management.
# What is Authlete?
Authlete is a [BaaS (Backend as a Service)](https://en.wikipedia.org/wiki/Backend_as_a_service) for implementing [OAuth 2.0](https://tools.ietf.org/html/rfc6749) and [OpenID Connect](https://openid.net/). By using the [Web APIs](https://docs.authlete.com/) that Authlete provides, you can implement OAuth 2.0 and OpenID Connect functionality with less effort.
# Why do you need Authlete?
When you implement a Web API, you need to implement OAuth 2.0 (and OpenID Connect) on the service provider side. That implementation is not trivial and often requires significant resources and time. Managing personal data also demands great care. Using limited engineering resources to address these challenges while launching before competitors is difficult.
Authlete helps you address these challenges. Authlete supports many finalized and draft API authorization-related specifications. Using Authlete makes it relatively easy to implement a Web API.
# Authlete features
Because Authlete provides APIs to build an authorization server rather than a prebuilt server, it offers the following benefits.
What distinguishes Authlete is that all functionality required to implement an OAuth 2.0 and OpenID Connect server is designed and implemented as Web APIs. Not only client application registration and authorization server metadata management, but also the logic behind authorization and token endpoints is provided via Web APIs.
As a result, you can use Authlete with any language or framework—Java, Ruby, PHP, C#, and more. Using OSS libraries, you can implement a server in days to weeks.
Authlete delivers OAuth/OIDC behavior through Web APIs. The authorization and token endpoints run in your environment. So you can design the consent UI and user flow to match your product and brand.
With Authlete you can operate multiple OAuth/OIDC server instances.
The management console is built for managing multiple instances. You can add and manage instances with different settings from the console without writing extra code.
You can run separate OAuth/OIDC servers for different use cases—for example, one for mobile apps and one for server-to-server integration, or one for end users and one for admin users—without a large increase in development effort.
Authlete focuses on authorization and works with any user authentication, identity, or API management solution. You can keep your existing authentication and identity infrastructure and add OAuth/OIDC with minimal development.
When you use Authlete, the only end-user information you need to pass to Authlete is a stable subject identifier per user. Authlete accepts that identifier and associates it with tokens and other protocol data.
You do not need to share end-user names, email addresses, or credentials with Authlete. This is a major difference from all-in-one authentication-and-authorization solutions.
When generating ID tokens, you can send the claims to embed to Authlete’s API to fulfill OpenID Connect behavior.
Authlete supports RFC 6749 and many other specifications. It is OpenID Connect certified and was the first production-ready solution to achieve Financial-grade API (FAPI) certification. The supported specifications are listed per Authlete version on the Spec Sheet.
Because OAuth/OIDC behavior is delivered via Web APIs, supporting new specifications places minimal burden on your code. For example, to support PKCE, while the client requests will now include the new PKCE-related parameters, no changes are required to your authorization server implementation.
# OpenID Certification
Authlete is certified for several [OpenID Provider (OP) profiles](https://openid.net/certification/). The certified profiles — OpenID Provider, FAPI 1.0 Advanced, FAPI-CIBA and FAPI 2.0 — are listed under [Certification and Conformance](https://www.authlete.com/product#conformance), and the [Spec Sheet](https://www.authlete.com/legal/spec_sheet/) shows which Authlete version each one applies to.
# Summary
You can support OAuth 2.0 and OpenID Connect in your web service with Authlete. To try it out, continue with [Getting Started](/get-started/quickstarts/getting-started).
# First API Call 🚀
Source: https://developers.authlete.com/get-started/quickstarts/getting-started
Set up your Authlete account and make your first API call in just a few minutes.
This page is for Authlete 3.0. For 2.x, see [Getting Started (2.x)](/v2/get-started/quickstarts/getting-started).
## 1. Create an Authlete Account
To use Authlete, you need an account. Once registered, you can configure all features through the management console.
[Visit the Authlete website](https://www.authlete.com) and click [Free Trial](https://console.authlete.com/register) in the upper-right corner.
Choose your preferred registration method. You can use **social login (Google or GitHub)** or sign up with your **email address**.
After verifying your social login or email address, complete the profile page by entering your **first name**, **last name**, and **organization name** (and **Password** if using email registration). Review Authlete’s Privacy Policy and Terms of Service, then click **Register** to agree and complete your signup.
***
## 2. Create a Service
Immediately after signing up, you will be directed to the **Services** page. This page displays all services under your organization (which was created during the signup process).
A **Service** represents the backend of your Authorization Server. It contains all the configurations required to operate a production-grade authorization server, including OAuth/OIDC settings, token management, and security policies.
Click **Create Service** to add a new Authlete service to your organization.
1. Select your preferred **API Cluster** location (e.g., `US`). All service data will be stored in this selected cluster. Choose the cluster based on your data residency and environment isolation requirements.
2. Enter a **Service Name** (e.g., `Sample Service`).
3. Click **Create** to complete the setup.
Now you have completed to create a new Service.
***
## 3. Create a Service Access Token
You will need a **Service Access Token** to authenticate your Authlete API calls.
Click **Service Settings** for your newly created service.
1. Note down the **Service Identifier** value (e.g., `21653835348762`), which is required when making requests in many Authlete APIs.
2. Click **Create Token** to create a new Service Access Token.
1. Enter a **token Name** (e.g., `oauth-server-token`).
2. Specify the **Expiration Date**.
3. Select the appropriate **Permissions** (e.g., **Authorization Server** preset).
4. Click **Create Token**.
The new Service Access Token will appear in the list.
1. Click the **Copy** or **Show** icon to retrieve the token value, as it is required for most Authlete API requests. Please note that this value is only displayed once upon creation.
2. Click **Back to Service Overview** to return to the main Service page.
*Now you are ready to test the Authlete API!*
***
## 4. Call the Authlete API
Let’s make your first API request to Authlete’s [/service/configuration](/api-reference/service-management/get-service-configuration) API, which acts as the backend for the OIDC discovery endpoint, generating OpenID provider metadata so that the endpoint can return in response to requests from clients (OIDC relying parties).
```mermaid placement="top-right" theme={null}
%%{init: {
'themeVariables': {
'fontSize': '20px',
'nodeTextMargin': 40
},
'flowchart': {
'nodeSpacing': 40,
'rankSpacing': 40,
'padding': 5
}
}}%%
graph LR
client[OIDC RP]
subgraph as[OIDC IdP]
direction TB
disco[Discovery Endpoint]
end
subgraph authlete[Authlete]
direction TB
config["/service/configuration API"]
end
client -- Metadata Request --> disco
disco -- Service Configuration Request --> config
```
Below are examples using **curl** and the **API Explorer**. Please replace the placeholder values with your actual environment details.
| Item | Value |
| :- | :- |
| **API Cluster Hostname** | `https://us.authlete.com` |
| **Service Access Token** | `UpUodxmvxO7Yd06O9QCxForhwM5Mz5PHZuVW3WFQgsc` |
| **Service ID** | `21653835348762` |
### Using curl
Run the following command in your terminal to send an HTTP GET request:
```bash For Linux/MacOS theme={null}
curl -X GET "https://us.authlete.com/api/21653835348762/service/configuration" \
-H "Authorization: Bearer UpUodxmvxO7Yd06O9QCxForhwM5Mz5PHZuVW3WFQgsc"
```
```powershell For Windows theme={null}
curl.exe -X GET "https://us.authlete.com/api/21653835348762/service/configuration" `
-H "Authorization: Bearer UpUodxmvxO7Yd06O9QCxForhwM5Mz5PHZuVW3WFQgsc" | ConvertFrom-Json
```
If successful, the API will return OpenID Provider Metadata similar to this:
```json theme={null}
{
"issuer": "https://authlete.com",
[...]
"scopes_supported": [
"address",
"email",
"openid",
"offline_access",
"phone",
"profile",
"grant_management_query",
"grant_management_revoke"
],
"response_types_supported": [
"none",
"code",
"id_token",
"code id_token"
],
[...]
}
```
### Using API Explorer
Open the [/service/configuration API reference page](/api-reference/service-management/get-service-configuration) and click the **Try it** button.
1. Choose the **API Cluster Hostname**.
2. Enter the **Service Access Token**.
3. Enter the **Service ID**.
4. Click **Send**.
The API response will be displayed in the Response section.
***
## Next Steps
Now that your Authlete account is set up and your service is configured, it's time to explore further:
* [**OAuth 2.0 Basics - API Tutorials**](/get-started/quickstarts/oauth-2-0-basics): Learn how Authlete works under the hood by calling the APIs directly using curl and other tools.
* [**Using Demo Authorization Server**](/get-started/quickstarts/using-demo-authorization-server): Deploy an open-source authorization server frontend that integrates with Authlete APIs to test live OAuth/OIDC flows.
***
## Extra: OAuth/OIDC Processing
Let's explore Authlete's OAuth/OIDC protocol processing by testing a sample authorization request.
### Create a Client
A **Client** represents an application that interacts with the Authorization Server to request tokens, or with the Resource Server to access protected resources. Clients are managed within the context of a specific Authlete Service.
On the Service page, click the **Create Client** button at the top-right of the screen.
Enter a **Client Name** and click **Create**.
Once created, the client details will be displayed. Make a note of the **Client ID** and **Client Secret** if needed. In the **Redirect URIs** section, click **Add** to register your application's redirect URIs.
For example, enter `https://client.example.org/cb/example.com` and click **Save Changes**.
### Call "Process Authorization Request" API
Navigate to the [Try /auth/authorization API](/api-reference/authorization-endpoint/process-authorization-request?playground=open) tool.
1. Choose the **API Cluster Hostname**.
2. Enter the **Service Access Token**.
3. Enter the **Service ID**.
4. Enter the authorization request content below into the **parameters** field. Please note that you must replace the example `client_id` value (`2329821874`) with the one you created in the previous step.
5. Click **Send**.
| Parameter | Value |
| :- | - |
| **parameters** | `redirect_uri=https://client.example.org/cb/example.com&client_id=2329821874&response_type=code` |
You will see the API response in the Response section.
# OAuth 2.0 Basics
Source: https://developers.authlete.com/get-started/quickstarts/oauth-2-0-basics
A tutorial to understand how OAuth 2.0 authorization server leverages Authlete APIs.
This page is for Authlete 3.0. For 2.x, see [this page](/v2/get-started/core-concepts/oauth-basics).
# Preface
This tutorial describes the basic usage of [Authlete APIs](/api-reference) to implement an OAuth 2.0 authorization server that supports the authorization code grant flow. It also shows how Resource Servers can use Authlete APIs to quickly validate the access tokens, providing secure and authorized access to APIs.
**Prerequisites:**
* Basic knowledge of OAuth 2.0.
* Access to an Authlete account. [Sign up here if needed](https://console.authlete.com/register).
# Components
A typical OAuth 2.0 architecture involves multiple components, shown in the flowchart below. In this tutorial, we use the live, public cloud version of the [Authlete Management Console](https://console.authlete.com) and Authlete API Cluster running in the US region (`https://us.authlete.com`). The Authorization and Resource servers are simulated using `curl` commands etc., showing how an Authorization Server and Resource Server make API requests to Authlete for authorization, token issuance, and introspection.
```mermaid theme={null}
flowchart LR
subgraph EndUser [End User]
userAgent["User Agent (N/A)"]
resourceOwner["Resource Owner (N/A)"]
end
subgraph APIClient [API Client]
client["Client (N/A)"]
end
subgraph APIServer [API Server]
authServer["Authorization Server (curl)"]
resourceServer["Resource Server (curl)"]
authAdmin["Authlete Administrator"]
end
subgraph Authlete [Authlete]
mgmtConsole["Authlete Management Console"]
authAPI["Authlete API"]
end
resourceOwner --> userAgent
userAgent -- "Access to Client" --> client
userAgent -- "Authorization Request (User Authentication and Consent)" --> authServer
client -- "Token Request" --> authServer
client -- "API Request" --> resourceServer
%% API Server interactions with Authlete
authAdmin -- "Service/Client Management" --> mgmtConsole
mgmtConsole -- "API Request" --> authAPI
authServer -- "API Request" --> authAPI
resourceServer -- "API Request" --> authAPI
```
The FQDNs for each component are listed below. While the Authorization Server and Client are simulated, their FQDNs are used to illustrate 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 |
# Environment Setup
Follow the [First API Call](/get-started/quickstarts/getting-started) to create a new Authlete Service and Client. Also, generate a Service Access Token, which is needed to call Authlete APIs. Enter your own values below, and the code/command samples on this page will update automatically. (In the SDK tabs, the service access token is read from the `SERVICE_ACCESS_TOKEN` environment variable rather than hard-coded.)
# Walk-through
The sequence diagram below shows the entire OAuth 2.0 flow as used in this tutorial. Refer back to it as you proceed through each step.
```mermaid theme={null}
sequenceDiagram
autonumber
participant RO as Resource Owner
participant UA as User Agent
participant C as Client
participant AS as Authorization Server
participant RS as Resource Server
participant API as Authlete API
RO ->> UA: Start
UA ->> C: Make a request
C -->> UA: Authorization request
UA ->> AS: Forward the request
AS ->> API: POST /auth/authorization
API -->> AS: OK to proceed (including ticket)
AS -->> UA: User authentication and consent page
RO <<->> UA: Enter credentials
UA ->> AS: Login and consent
AS ->> API: POST /auth/authorization/issue (with ticket)
API -->> AS: Authorization response content (including authorization code)
AS -->> UA: Authorization response
UA ->> C: Forward the response
C ->> AS: Token request (with authorization code)
AS ->> API: POST /auth/token
API -->> AS: Token response content (including access token)
AS -->> C: Token response
C ->> RS: Access protected resource (with access token)
RS ->> API: POST /auth/introspection
API -->> RS: Introspection response
RS -->> C: Response to client
C -->> UA: Content
UA -->> RO: End
```
## Authorization request from the client to the authorization server
The client makes an authorization request to the authorization server via the user agent (Steps 3 and 4).
In this tutorial, we’ll assume the following values are specified as parameters in the request. The values from the **Environment Setup** inputs are reflected below.
The authorization server is expected to receive the following content (folded for readability) as an HTTP GET query string from the user agent.
A typical authorization server should evaluate the following rules before proceeding with the authorization code grant flow.
* Verify that a client associated with the client ID is registered with the authorization server.
* Check that the redirect URI matches one of the URIs registered for the client.
* Confirm that other parameter values, such as `response_type` and `scope`, are permitted for the client to specify in its request.
Authlete's [`/auth/authorization`](/api-reference/authorization-endpoint/process-authorization-request) API performs the above request validation on behalf of the authorization server.
Use sample command/code to make a request to the Authlete API (Step 5) by choosing one from the following language tab; the values will be updated as you edit the inputs above.
```bash cURL theme={null}
curl -X POST "https://us.authlete.com/api/933860280/auth/authorization" \
-H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
-H "Content-Type: application/json" \
-d '{ "parameters": "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com"}'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
$body = '{ "parameters": "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/933860280/auth/authorization" `
-Headers $headers -ContentType "application/json" -Body $body
```
```java Java theme={null}
// authlete-java-common — initialize the client once and reuse it across the steps below.
AuthleteConfiguration config = new AuthleteSimpleConfiguration()
.setApiVersion("V3")
.setBaseUrl("https://us.authlete.com")
.setServiceApiKey("933860280")
.setServiceAccessToken(System.getenv("SERVICE_ACCESS_TOKEN"));
AuthleteApi authleteApi = AuthleteApiFactory.create(config);
AuthorizationRequest request = new AuthorizationRequest()
.setParameters("response_type=code"
+ "&client_id=12818600553323"
+ "&redirect_uri=https://client.example.org/cb/example.com");
AuthorizationResponse response = authleteApi.authorization(request);
System.out.println(response.getAction()); // INTERACTION
System.out.println(response.getTicket());
```
```typescript TypeScript theme={null}
import { Authlete } from "@authlete/typescript-sdk";
// Initialize the client once and reuse it across the steps below.
const authlete = new Authlete({
serverURL: "https://us.authlete.com",
bearer: process.env["SERVICE_ACCESS_TOKEN"] ?? "",
});
const SERVICE_ID = "933860280";
const res = await authlete.authorization.processRequest({
serviceId: SERVICE_ID,
authorizationRequest: {
parameters:
"response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com",
},
});
console.log(res.action); // "INTERACTION"
console.log(res.ticket);
```
```ruby Ruby theme={null}
require "authlete_ruby_sdk"
# Initialize the client once and reuse it across the steps below.
client = Authlete::Client.new(
bearer: ENV.fetch("SERVICE_ACCESS_TOKEN"),
server_url: "https://us.authlete.com"
)
SERVICE_ID = "933860280"
req = Authlete::Models::Components::AuthorizationRequest.new(
parameters: "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com"
)
res = client.authorization.process_request(service_id: SERVICE_ID, authorization_request: req)
puts res.authorization_response.action # "INTERACTION"
puts res.authorization_response.ticket
```
```go Go theme={null}
package main
import (
"context"
"log"
"os"
authlete "github.com/authlete/authlete-go-sdk"
"github.com/authlete/authlete-go-sdk/models/components"
)
func main() {
ctx := context.Background()
// Initialize the client once and reuse it across the steps below.
s := authlete.New(
authlete.WithServerURL("https://us.authlete.com"),
authlete.WithSecurity(os.Getenv("SERVICE_ACCESS_TOKEN")),
)
const serviceID = "933860280"
res, err := s.Authorization.ProcessRequest(ctx, serviceID, components.AuthorizationRequest{
Parameters: "response_type=code&client_id=12818600553323&redirect_uri=https://client.example.org/cb/example.com",
})
if err != nil {
log.Fatal(err)
}
log.Printf("action=%v", *res.AuthorizationResponse.Action) // INTERACTION
log.Printf("ticket=%s", *res.AuthorizationResponse.Ticket)
}
```
If the request is valid, Authlete returns a response like this (Step 6).
```json theme={null}
{
"action": "INTERACTION",
"resultCode": "A004001",
"resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 933860280) for the authorization request from the client (ID = 12818600553323). [response_type=code, openid=false]",
"ticket": "cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE",
"client": {...},
"service": {...},
}
```
* `resultMessage` provides human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. (See also [Action Handling](/get-started/concepts/action-handling))
* `ticket` is required for the authorization server to make a request to another API in the following step. (See also [Two-Step API Calls](/get-started/concepts/two-step-api-calls))
Authlete also provides service and client information in the response. The authorization server utilizes them to ask the resource owner if he or she authorizes access for the client to the service.
## User authentication and confirmation of granting access
The actual interaction between the resource owner and the authorization server is out of scope for this tutorial. Typically, the authorization server would authenticate the user with credentials (e.g., ID and password), determine the user’s roles and privileges, and ask if they authorize access (Steps 7, 8 and 9).
## Issuing an authorization code
Assume the authorization server reaches the following state after completing the previous process:
* The authorization server has authenticated the resource owner and determined that the identifier to be shared with Authlete as the value of the `subject` parameter is `testuser01`.
* The authorization server has obtained consent from the resource owner.
The authorization server then makes a request to Authlete's [`/auth/authorization/issue`](/api-reference/authorization-endpoint/issue-authorization-response) to issue an authorization code. It includes the values of `subject` and `ticket` from the `/auth/authorization` API response as request parameters (Step 10). Enter your values below and the samples update automatically.
```bash cURL theme={null}
curl -X POST "https://us.authlete.com/api/933860280/auth/authorization/issue" \
-H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
-H "Content-Type: application/json" \
-d '{ "ticket": "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc","subject": "testuser01"}'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
$body = '{ "ticket": "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc", "subject": "testuser01" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/933860280/auth/authorization/issue" `
-Headers $headers -ContentType "application/json" -Body $body
```
```java Java theme={null}
// Reuse the authleteApi client created in the authorization step.
AuthorizationIssueRequest request = new AuthorizationIssueRequest()
.setTicket("5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc")
.setSubject("testuser01");
AuthorizationIssueResponse response = authleteApi.authorizationIssue(request);
System.out.println(response.getAction()); // LOCATION
System.out.println(response.getAuthorizationCode());
System.out.println(response.getResponseContent());
```
```typescript TypeScript theme={null}
// Reuse the `authlete` client and SERVICE_ID created in the authorization step.
const res = await authlete.authorization.issue({
serviceId: SERVICE_ID,
authorizationIssueRequest: {
ticket: "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc",
subject: "testuser01",
},
});
console.log(res.action); // "LOCATION"
console.log(res.authorizationCode);
console.log(res.responseContent);
```
```ruby Ruby theme={null}
# Reuse the `client` and SERVICE_ID created in the authorization step.
req = Authlete::Models::Components::AuthorizationIssueRequest.new(
ticket: "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc",
subject: "testuser01"
)
res = client.authorization.issue_response(service_id: SERVICE_ID, authorization_issue_request: req)
puts res.authorization_issue_response.action # "LOCATION"
puts res.authorization_issue_response.authorization_code
puts res.authorization_issue_response.response_content
```
```go Go theme={null}
// Reuse the `s` client and serviceID created in the authorization step.
res, err := s.Authorization.Issue(ctx, serviceID, components.AuthorizationIssueRequest{
Ticket: "5tqii9i_pUp1iteacZGUtdjikRnqGSrPwW7lqoH1Pcc",
Subject: "testuser01",
})
if err != nil {
log.Fatal(err)
}
log.Printf("action=%v", *res.AuthorizationIssueResponse.Action) // LOCATION
log.Printf("authorizationCode=%s", *res.AuthorizationIssueResponse.AuthorizationCode)
log.Printf("responseContent=%s", *res.AuthorizationIssueResponse.ResponseContent)
```
If the request is valid, Authlete generates the following response (Step 11).
```json theme={null}
{
"action": "LOCATION",
"authorizationCode": "tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI",
"idToken": null,
"jwtAccessToken": null,
"responseContent": "https://client.example.org/cb/example.com?code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&iss=https%3A%2F%2Fauthlete.com",
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"ticketInfo": {
"context": null
}
}
```
* `resultMessage` provides a human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. The value in this response is `LOCATION`, meaning the authorization server should make a redirection response back to the user agent. (See also [Action Handling](/get-started/concepts/action-handling))
* `responseContent` represents the content of the response from the authorization server. (See also [Action Handling](/get-started/concepts/action-handling))
The authorization server is expected to send the following response (folded for readability) to the user agent (Step 12).
```http theme={null}
HTTP/1.1 302 Found
Location: https://client.example.org/cb/example.com
?code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI
```
There are cases where the authorization server may decide not to issue tokens to the client due to the result of the previous authentication, user consent confirmation, or other missing context that prohibits token issuance. In these situations, the authorization server must inform the client that the authorization flow has been terminated with an appropriate protocol response.
Authlete's [`/auth/authorization/fail`](/api-reference/authorization-endpoint/fail-authorization-request) API supports the termination process in terms of messages to be sent to the client, and transfer method for the response.
To summarize, an authentication server usually makes a request to either `/auth/authorization/issue` or `/auth/authorization/fail` API depending on result of user authentication and consent.
## Token request
Here, we assume that the user agent receives the redirection response from the authorization server. It then sends the following request (folded for readability) to the client (Step 13). Enter the **authorization code** (the `code` from the previous response) below and the samples update.
The client extracts the value of the `code` parameter, crafts a token request using this value, and sends it to the authorization server as follows (folded for readability). In this tutorial, `https://as.example.com/token` is the token endpoint URI (Step 14).
The authorization server evaluates the parameters in the request and then sends a token response back to the client.
In this tutorial, we’ll use Authlete's [`/auth/token`](/api-reference/token-endpoint/process-token-request) API to evaluate the request and generate the response.
```bash cURL theme={null}
curl -X POST "https://us.authlete.com/api/933860280/auth/token" \
-H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
-H "Content-Type: application/json" \
-d '{ "clientId": "12818600553323","clientSecret": "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w","parameters": "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
$body = '{ "clientId": "12818600553323", "clientSecret": "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w", "parameters": "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/933860280/auth/token" `
-Headers $headers -ContentType "application/json" -Body $body
```
```java Java theme={null}
// Reuse the authleteApi client created in the authorization step.
TokenRequest request = new TokenRequest()
.setClientId("12818600553323")
.setClientSecret("AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w")
.setParameters("grant_type=authorization_code"
+ "&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI"
+ "&redirect_uri=https://client.example.org/cb/example.com");
TokenResponse response = authleteApi.token(request);
System.out.println(response.getAction()); // OK
System.out.println(response.getAccessToken());
System.out.println(response.getResponseContent());
```
```typescript TypeScript theme={null}
// Reuse the `authlete` client and SERVICE_ID created in the authorization step.
const res = await authlete.token.process({
serviceId: SERVICE_ID,
tokenRequest: {
clientId: "12818600553323",
clientSecret:
"AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w",
parameters:
"grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com",
},
});
console.log(res.action); // "OK"
console.log(res.accessToken);
console.log(res.responseContent);
```
```ruby Ruby theme={null}
# Reuse the `client` and SERVICE_ID created in the authorization step.
req = Authlete::Models::Components::TokenRequest.new(
client_id: "12818600553323",
client_secret: "AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w",
parameters: "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com"
)
res = client.tokens.process_request(service_id: SERVICE_ID, token_request: req)
puts res.token_response.action # "OK"
puts res.token_response.access_token
puts res.token_response.response_content
```
```go Go theme={null}
// Reuse the `s` client and serviceID created in the authorization step.
res, err := s.Token.Process(ctx, serviceID, components.TokenRequest{
ClientID: types.String("12818600553323"), // types: github.com/authlete/authlete-go-sdk/types
ClientSecret: types.String("AqZKLbUFfRurwZ0-zN1BwAXfo-odPwrZa6XoSOrm-AKtbaaOAq6uzWM3oVG4w"),
Parameters: "grant_type=authorization_code&code=tJlGEKt8y0DLpjIA_jweywRtJs2fh83ZGNiwFRmIwYI&redirect_uri=https://client.example.org/cb/example.com",
})
if err != nil {
log.Fatal(err)
}
log.Printf("action=%v", *res.TokenResponse.Action) // OK
log.Printf("accessToken=%s", *res.TokenResponse.AccessToken)
log.Printf("responseContent=%s", *res.TokenResponse.ResponseContent)
```
If the request is valid, Authlete responds as follows (Step 16).
```json theme={null}
{
"accessToken": "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
"accessTokenDuration": 86400,
"accessTokenExpiresAt": 1730203012061,
"action": "OK",
"clientAuthMethod": "CLIENT_SECRET_BASIC",
"clientId": 12818600553323,
"grantType": "AUTHORIZATION_CODE",
"refreshToken": "1mQRm2XRwaBNYRIjxjO0ls9hb4eDKo9I3ChU0yy8lHo",
"refreshTokenDuration": 864000,
"refreshTokenExpiresAt": 1730980612061,
"responseContent": "{\"access_token\":\"gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g\",\"token_type\":\"Bearer\",\"expires_in\":86400,\"scope\":null,\"refresh_token\":\"1mQRm2XRwaBNYRIjxjO0ls9hb4eDKo9I3ChU0yy8lHo\"}",
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"subject": "testuser01",
}
```
* `resultMessage` provides a human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. The value in this response is `OK`, meaning the authorization server should send a token response back to the client. (See also [Action Handling](/get-started/concepts/action-handling))
* `responseContent` contains the content of the response from the authorization server. (See also [Action Handling](/get-started/concepts/action-handling))
The authorization server is expected to send the following response to the client (Step 17).
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token":"gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
"refresh_token":"1mQRm2XRwaBNYRIjxjO0ls9hb4eDKo9I3ChU0yy8lHo",
"scope":null,
"token_type":"Bearer",
"expires_in":86400
}
```
The authorization server has now successfully created the tokens and provided them to the client. By leveraging Authlete APIs, the authorization server avoids the need to implement complex logic to evaluate parameters in authorization and token requests and can respond correctly using the appropriate methods.
## API request (access token introspection)
In most cases, the client sends a request with the access token to the resource server providing the APIs (Step 18). The resource server is responsible for evaluating the token's validity, retrieving information about the user and client associated with the token, and determining how to respond to the API request.
Authlete provides the [`/auth/introspection`](/api-reference/introspection-endpoint/process-introspection-request) API for this purpose. It verifies the token's validity and provides the necessary information.
Enter the access token received by the resource server below, then use the following command to introspect it using the Authlete API.
```bash cURL theme={null}
curl -X POST "https://us.authlete.com/api/933860280/auth/introspection" \
-H "Authorization: Bearer DL7jo1z3iUIXyI5MnX" \
-H "Content-Type: application/json" \
-d '{ "token": "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer DL7jo1z3iUIXyI5MnX" }
$body = '{ "token": "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/933860280/auth/introspection" `
-Headers $headers -ContentType "application/json" -Body $body
```
```java Java theme={null}
// Reuse the authleteApi client created in the authorization step.
IntrospectionRequest request = new IntrospectionRequest()
.setToken("gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g");
IntrospectionResponse response = authleteApi.introspection(request);
System.out.println(response.getAction()); // OK
System.out.println(response.isUsable()); // true
System.out.println(response.getSubject()); // testuser01
System.out.println(response.getClientId()); // 12818600553323 (long)
```
```typescript TypeScript theme={null}
// Reuse the `authlete` client and SERVICE_ID created in the authorization step.
const res = await authlete.introspection.process({
serviceId: SERVICE_ID,
introspectionRequest: {
token: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
},
});
console.log(res.action); // "OK"
console.log(res.subject); // "testuser01"
console.log(res.clientId); // 12818600553323 (number)
```
```ruby Ruby theme={null}
# Reuse the `client` and SERVICE_ID created in the authorization step.
req = Authlete::Models::Components::IntrospectionRequest.new(
token: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g"
)
res = client.introspection.process_request(service_id: SERVICE_ID, introspection_request: req)
puts res.introspection_response.action # "OK"
puts res.introspection_response.subject # "testuser01"
puts res.introspection_response.client_id # 12818600553323 (Integer)
```
```go Go theme={null}
// Reuse the `s` client and serviceID created in the authorization step.
res, err := s.Introspection.Process(ctx, serviceID, components.IntrospectionRequest{
Token: "gV5ByjqCVjyinFugV9Wlc0oAxkEP0osifSajXSfJV9g",
})
if err != nil {
log.Fatal(err)
}
log.Printf("action=%v", *res.IntrospectionResponse.Action) // OK
log.Printf("subject=%s", *res.IntrospectionResponse.Subject) // testuser01
log.Printf("clientId=%d", *res.IntrospectionResponse.ClientID) // 12818600553323 (int64)
```
If the request is valid, Authlete returns the following response (Step 20).
```json theme={null}
{
"action": "OK",
"authTime": 0,
"clientId": 12818600553323,
"expiresAt": 1730203012000,
"grantType": "AUTHORIZATION_CODE",
"issuableCredentials": null,
"properties": null,
"refreshable": true,
"resources": null,
"responseContent": "Bearer error=\"invalid_request\"",
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"scopeDetails": null,
"scopes": null,
"serviceAttributes": null,
"subject": "testuser01",
"sufficient": false,
"usable": true
}
```
The resource server can retrieve various information, such as the token’s expiration time (`expiresAt`), the identifier of the user who approved access (`subject`), the grant type used to obtain the token, and the client identifier (`clientId`). It then determines how to respond to the API request (Step 21).
# Recap
In this tutorial, we covered how to use Authlete APIs to implement the authorization code grant flow in an authorization server.
# Next Steps
Let’s dig deeper into Authlete by exploring the following features.
* [Authlete API Tutorial - OIDC Basics](/get-started/quickstarts/oidc-basics)
* [PKCE (RFC 7636)](/protocols-and-flows/protocol-extensions/proof-key-for-code-exchange-pkce)
* [Authorization Server Reference Implementations](https://github.com/authlete/java-oauth-server)
# OIDC Basics
Source: https://developers.authlete.com/get-started/quickstarts/oidc-basics
A tutorial to understand how OpenID Connect identity provider leverages Authlete APIs.
This page is for Authlete 3.0. For 2.x, see [this page](/v2/get-started/core-concepts/oidc-basics).
# Introduction
This document is a tutorial to describe basic usage of [Authlete APIs](/api-reference) in order to implement OpenID Connect (OIDC) identity provider (IdP) server that supports the [authorization code flow](https://youtu.be/x2qtJZ3Wivc?feature=shared).
In completing the tutorial, you will
* Make requests simulating the requests which come from the authorization server to Authlete
* Set the issuer identifier in the Authlete Management Console, include additional claims in the authorization request to the Authlete authorize endpoint, and confirm that both the issuer and additional claims are included in the ID Token by decoding it.
# Authorization Code Flow Components
**Note**
In this tutorial, OIDC identity provider and relying party are described as authorization server and client respectively for convenience.
In a typical Authlete implementation, the authorization server middleware would be responsible for passing requests to and handling responses from the Authlete API. In this tutorial, you will make these requests directly with the bash/zsh `curl` command. You may of course use an alternate HTTP client of your choice (PowerShell, Postman, etc.), but you'll need to know how to adapt the instructions for yourself in this instance.
```mermaid theme={null}
flowchart LR
subgraph EndUser [End User]
userAgent["User Agent (N/A)"]
resourceOwner["Resource Owner (N/A)"]
end
subgraph APIClient [API Client]
client["Client (N/A)"]
end
subgraph APIServer [API Server]
authServer["Authorization Server/IdP (curl)"]
resourceServer["Resource Server (curl)"]
authAdmin["Authlete Administrator"]
end
subgraph Authlete [Authlete]
mgmtConsole["Authlete Management Console"]
authAPI["Authlete API"]
end
resourceOwner --> userAgent
userAgent -- "Access to Client" --> client
userAgent -- "Authorization Request (User Authentication and Consent)" --> authServer
client -- "Token Request" --> authServer
client -- "API Request" --> resourceServer
%% API Server interactions with Authlete
authAdmin -- "Service/Client Management" --> mgmtConsole
mgmtConsole -- "API Request" --> authAPI
authServer -- "API Request" --> authAPI
resourceServer -- "API Request" --> authAPI
```
To understand how each piece fits together in this OAuth flow, we've provided a list of fully qualified domain names (FQDN) for each component. The FQDNs for the authorization server and the client are invented only for reference, and would of course be substituted for real ones in a real implementation.
| 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 |
**Note**
API endpoints are cluster specific. If using a service in another cluster, you'd substitute in the cluster's two character ISO code for the complete domain name for that API endpoint (e.g. br, eu, jp).
# Environment setup
**Note**
Settings in this section are the same as described in Authlete API Tutorial - [OAuth 2.0 Basics](/get-started/quickstarts/oauth-2-0-basics). If you've already completed that tutorial, you can skip this section.
Consult the [First API Call](/get-started/quickstarts/getting-started) guide for how to create an Authlete API service and register a client to the service.
Take care to note your Service ID, Service Access Token, and Client Secret values.
If you've already completed other Authlete tutorials, you can reuse your existing service. Otherwise use the following values to create your service, leaving all other fields as default:
| Field | Value |
| - | - |
| API Cluster | 🇺🇸 US |
| Service Name | Demo AS |
| Service Description | Example authorization service |
You can use the following values to create your client, leaving all other fields as default:
| Field | Value |
| - | - |
| Client Name | Demo OIDC Client |
| Client Description | Example client for OIDC tutorial |
| Client Type | CONFIDENTIAL |
Once your client is created, go to Client Settings > Endpoints > Global Settings. In the Redirect URIs section, click Add to enter and save the URI where the client will receive authorization responses.
The following example properties are generated or specified when you create a client. These are values required when making requests as part of the `code` flow that you will complete. Use the values you noted down in the environment setup section.
| Item | Value |
| - | - |
| Service ID | Auto-generated e.g. `10738933707579` |
| Service Access Token | Auto-generated e.g. `Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ` |
| Client ID | Auto-generated e.g. `12898884596863` |
| Client Secret | Auto-generated e.g. `-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg` |
| Client Type | `CONFIDENTIAL` |
| Redirect URIs | `https://client.example.org/cb/example.com` |
| Client Authentication Method | `CLIENT_SECRET_BASIC` |
Now, let's try the OIDC authorization code flow using this environment.
# Walk-through
We've provided the following sequence diagram to help you understand each of the steps involved in the code flow. You can identify where you are at in the walk-through using the numbers which correspond to specific steps in the diagram.
```mermaid theme={null}
sequenceDiagram
autonumber
participant RO as Resource Owner
participant UA as User Agent
participant C as Client
participant AS as Authorization Server
participant RS as Resource Server
participant API as Authlete API
RO ->> UA: Start
UA ->> C: Make a request
C -->> UA: Authorization request
UA ->> AS: Forward the request
AS ->> API: POST /auth/authorization
API -->> AS: OK to proceed (including ticket)
AS -->> UA: User authentication and consent page
RO <<->> UA: Enter credentials
UA ->> AS: Login and consent
AS ->> API: POST /auth/authorization/issue (with ticket)
API -->> AS: Authorization response content (including authorization code)
AS -->> UA: Authorization response
UA ->> C: Forward the response
C ->> AS: Token request (with authorization code)
AS ->> API: POST /auth/token
API -->> AS: Token response content (including id token & access token)
AS -->> C: Token response
```
## Authorization request from the client to the authorization server
We'll use the following values as example parameters for the request (as specified previously).
| Item | Value | Description |
| - | - | - |
| client\_id | `12898884596863` | Registered client ID |
| response\_type | `code` | A value stating OIDC authorization code flow (when `scope` contains `openid`) |
| redirect\_uri | `https://client.example.org/cb/example.com` | One of registered redirect URIs |
| scope | `openid` | A value stating this request is OIDC authentication request |
| nonce | `n-0S6_WzA2Mj` | Nonce value (See [3.1.2.1. Authentication Request - OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)) |
When the resource owner attempts to access a protected resource through the client (steps #1, #2) the client makes an OIDC authentication request (authorization request) to the authorization server via user agent (steps #3, #4).
The authorization server receives the above data here shown as the HTTP `GET` query string from the user agent.
```shell theme={null}
redirect_uri=https://client.example.org/cb/example.com
&response_type=code
&client_id=12898884596863
&scope=openid
&nonce=n-0S6_WzA2Mj
```
The job of the authorization server is to evaluate these parameters. Here are some typical evaluation rules:
* Whether a client associated with the client ID `12898884596863` has been registered to the authorization server. It must be an OIDC relying party because of `scope=openid`.
* Whether the value of the redirect URI `https://client.example.org/cb/example.com` matches with one of URIs registered to the client
* Whether values of other parameters such as `response_type`, `scope` are applicable for the client, i.e. permitted for the client to specify in its request
After that, the authorization server would process the OIDC authorization code flow since the values of `scope` and `response_type` are `openid` and `code` respectively.
However, in the Authlete architecture the authorization server is simply a proxy for the actual authorization logic contained in the Authlete API which is surfaced at the [`/auth/authorization`](/api-reference/authorization-endpoint/process-authorization-request) endpoint. Once received, the Authlete API does the evaluation process on the authorization server's behalf.
You'll now simulate the authorization server's request to this API.
Execute the `curl` command as follows (message #5). Make sure to replace ``, `` and `` by your own values generated in the previous step.
```text cURL theme={null}
curl -s -X POST https://us.authlete.com/api//auth/authorization \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=&scope=openid&nonce=n-0S6_WzA2Mj" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer " }
$body = '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=&scope=openid&nonce=n-0S6_WzA2Mj" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/
If successful, Authlete will reply with the following response (shortened for brevity) (message #6).
```json theme={null}
{
"resultMessage" : "[A004001] Authlete has successfully issued a ticket to the service (API Key = 10738933707579) for the authorization request from the client (ID = 12898884596863). [response_type=code, openid=true]",
"resultCode" : "A004001",
"client" : { [...] },
"ticket" : "bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU",
"action" : "INTERACTION",
[...]
"service" : {
[...]
"supportedClaims" : null,
"supportedScopes" : null,
}
}
```
Pay attention to the three key/value pairs in the response; `resultMessage`, `action` and `ticket` as seen above.
* `resultMessage` provides a human-readable result of the request processing (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes)). `openid=true` indicates the request is to be processed in accordance with the OIDC protocol.
* `action` indicates what the authorization server should do next. (See also [Action Handling](/get-started/concepts/action-handling))
* `ticket` is required for the authorization server to make a request to another API in the next step. (See also [Two-Step API Calls](/get-started/concepts/two-step-api-calls))
Authlete also provides service and client information in the response. The authorization server uses them to ask the resource owner if he or she authorizes access for the client to the service.
## User authentication and confirmation of sharing authentication result
Actual interaction between the resource owner and the authorization server is out of scope in this tutorial. In most cases, authorization server would authenticate user with some credentials (e.g. ID/password), determine roles and privileges for the user, and ask the user if he or she authorizes to share the authentication result with the client (steps #7, #8, #9).
## Issuing an authorization code
Let's assume the authorization server reaches the following state after completion of the previous process:
* The authorization server has authenticated the resource owner, and determined that an identifier for the resource owner, to be shared with Authlete as a value of `subject` parameter, is `testuser01`.
* The authorization server has got consent from the resource owner.
The authorization server makes a request to Authlete's [`/auth/authorization/issue`](/api-reference/authorization-endpoint/issue-authorization-response) in order to issue an authorization code. It specifies values of `subject` and `ticket` that were a part of the response from `/auth/authorization` API, as request parameters.
Execute the following `curl` command (message #10), making sure to replace ``, `` and `` by your own values generated in the previous step.
```text cURL theme={null}
curl -s -X POST https://us.authlete.com/api//auth/authorization/issue \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{ "ticket": "", "subject": "testuser01" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer " }
$body = '{ "ticket": "", "subject": "testuser01" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/
If successful, Authlete will reply with the following response (step #11).
```json theme={null}
{
"action" : "LOCATION",
"resultCode" : "A040001",
"accessTokenDuration" : 0,
"accessTokenExpiresAt" : 0,
"resultMessage" : "[A040001] The authorization request was processed successfully.",
"responseContent" : "https://client.example.org/cb/example.com?code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U",
"authorizationCode" : "GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U",
...
}
```
Pay attention to three key/value pairs in the response; `resultMessage`, `action` and `responseContent`.
* `resultMessage` provides a human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. The value in this response is `LOCATION`, which means the authorization server should make a redirection response back to the user agent. (See also [Action Handling](/get-started/concepts/action-handling))
* `responseContent` is the content of the response from the authorization server. (See also [Action Handling](/get-started/concepts/action-handling))
The authorization server is expected to make the following response (new line for readability) to the user agent (step #12).
```http theme={null}
HTTP/1.1 302 Found
Location: https://client.example.org/cb/example.com
?code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U
```
## Failure to issue an authorization code
Alternatively, the authorization server may fail to receive authorization. In this instance, it won't issue tokens to the client. In that situation the authorization server would notify the client that the authorization flow is terminated. Authlete's [`/auth/authorization/fail`](/api-reference/authorization-endpoint/fail-authorization-request) API supports the termination process in terms of messages to be sent to the client, and transfer method for the response.
To summarize, under most circumstances, the authentication server sends a request to either `/auth/authorization/issue` or `/auth/authorization/fail` API depending on the result of user authentication and consent.
## Token request
Here we assume that the user agent receives the redirection response form the authorization server. It would send the following request (new line for readability) to the client (step #13).
```http theme={null}
GET /cb/example.com?code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U HTTP/1.1
Host: client.example.org
```
The client needs to extract the value of the `code` parameter, construct a token request using its value and send it to the authorization server as follows (new lines for readability). `https://as.example.com/token` is the token endpoint URI in this tutorial (step #14).
```http theme={null}
POST /token HTTP/1.1
Host: as.example.com
Authorization: Basic base64(12898884596863:-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg)
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U
&redirect_uri=https://client.example.org/cb/example.com
```
Once again, the authorization server passes the request on to the Authlete API to evaluate parameters in the request and get a token response back to the client.
Once again, let's simulate this step using Authlete's [`/auth/token`](/api-reference/token-endpoint/process-token-request) API to evaluate the request and create the response.
Execute the following `curl` command (step #15), again making sure to replace ``, ``, ``, `` and `` by your own values generated in the previous step.
```text cURL theme={null}
curl -s -X POST https://us.authlete.com/api//auth/token \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{ "clientId": "", "clientSecret": "", "parameters": "grant_type=authorization_code&code=&redirect_uri=https://client.example.org/cb/example.com" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer " }
$body = '{ "clientId": "", "clientSecret": "", "parameters": "grant_type=authorization_code&code=&redirect_uri=https://client.example.org/cb/example.com" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/
If successful, Authlete will reply with the following response (message #16).
```json theme={null}
{
"resultMessage" : "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"action" : "OK",
"clientIdAliasUsed" : false,
"subject" : "testuser01",
"resultCode" : "A050001",
"refreshTokenExpiresAt" : 1730552811449,
"grantType" : "AUTHORIZATION_CODE",
"accessToken" : "7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY",
"idToken" : "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk",
"responseContent" : "{\"access_token\":\"7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY\",\"refresh_token\":\"T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE\",\"scope\":\"openid\",\"id_token\":\"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk\",\"token_type\":\"Bearer\",\"expires_in\":86400}",
"scopes" : [
"openid"
],
"accessTokenDuration" : 86400,
"refreshToken" : "T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE",
"accessTokenExpiresAt" : 1730552811449,
"refreshTokenDuration" : 864000,
"clientId" : 12898884596863
}
```
Pay attention to three key/value pairs in the response; `resultMessage`, `action` and `responseContent`.
* `resultMessage` provides human-readable result of the request processing. (See also [Interpreting Authlete's result codes](/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes))
* `action` indicates what the authorization server should do next. The value in this response is `OK`, which means the authorization server should make a token response back to the client. (See also [Action Handling](/get-started/concepts/action-handling))
* `responseContent` is supposed to be content of the response from the authorization server. (See also [Action Handling](/get-started/concepts/action-handling))
The authorization server would then pass the following response to the client (message #17).
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token":"7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY",
"refresh_token":"T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE",
"scope":"openid",
"id_token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk",
"token_type":"Bearer",
"expires_in":86400
}
```
The client now has the tokens which have been generated by the Authlete API and passed back through the authorization server.
## Decoding ID token
The most common next step is for the client to decode the value of `id_token` in the response and verify it.
To perform this function, we're going to decode the token with the free open source tool [Online JWT Verfier](https://kjur.github.io/jsrsasign/tool/tool_jwtveri.html).
Open the link above, and paste the value of the `id_token` into the text area in *(Step 1) Set JWT (JSON Web Token) to verify* on the JWT Verifier UI. In this tutorial, the value is: `eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk`
Click the **Just Decode JWT** button in *(Step 3) Verify* and see decoded content in the *Parsed JWT* section.
The decoded results are as follows.
**Header:**
```json theme={null}
{
"alg": "HS256"
}
```
**Payload:**
```json theme={null}
{
"sub": "testuser01",
"aud": [
"12898884596863"
],
"iss": "https://authlete.com",
"exp": 1730552811,
"iat": 1730552811,
"nonce": "n-0S6_WzA2Mj"
}
```
Note above there are a couple of items that need to be corrected.
* `iss` is `https://authlete.com`, which is the default value of Authlete. It must be `https://as.example.com`, that is the identifier of the authorization server in this tutorial.
* `sub` is the only attribute related to the user's identity. It may be better to include other user attributes for the client's convenience.
Let's fix the `iss` value and add other claims in the next section.
# Modifying ID token
## Issuer identifier
Login to the Management Console [https://console.authlete.com](https://console.authlete.com) and select the *Demo AS* service previously created during this tutorial. Click the **Service Settings** button to access the Service Settings.
Note that the default value of *Issuer Identifier* on the *General* tab is `https://authlete.com`. Change it to `https://as.example.com` and click the **Save Changes** button in the bottom of the page.
Now that the Token Issuer Identifier `iss` has been fixed.
## Authorization Request
Let's make the same authorization request as the previous one (using the same `nonce` value for convenience) to Authlete's [`/auth/authorization`](/api-reference/authorization-endpoint/process-authorization-request) API (step #5). Make sure to replace ``, `` and `` by your own values generated in the previous step.
```text cURL theme={null}
curl -s -X POST https://us.authlete.com/api//auth/authorization \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=&scope=openid&nonce=n-0S6_WzA2Mj" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer " }
$body = '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=&scope=openid&nonce=n-0S6_WzA2Mj" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/
You should receive the following response (shortened for brevity).
```json theme={null}
{
[...]
"action" : "INTERACTION",
"resultCode" : "A004001",
"resultMessage" : "[A004001] Authlete has successfully issued a ticket to the service (API Key = 10738933707579) for the authorization request from the client (ID = 12898884596863). [response_type=code, openid=true]",
"ticket" : "JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A",
}
```
## Additional claims
Let's make a request to Authlete's [`/auth/authorization/issue`](/api-reference/authorization-endpoint/issue-authorization-response) for issuance of an authorization code. Make sure to replace ``, `` and `` by your own values generated in the previous step.
This time the following additional claims are included on making the request to the API.
| Item | Value |
| - | - |
| `name` | `Test User` |
| `email` | `testuser01@example.com` |
| `email_verified` | true |
You can use the `claims` parameter to add claims. The request will be constructed as follows.
```text cURL theme={null}
curl -s -X POST https://us.authlete.com/api//auth/authorization/issue \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{ "ticket": "", "subject": "testuser01", "claims": "{\"name\": \"Test User\", \"email\": \"testuser01@example.com\", \"email_verified\": true}" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer " }
$body = '{ "ticket": "", "subject": "testuser01", "claims": "{\"name\": \"Test User\", \"email\": \"testuser01@example.com\", \"email_verified\": true}" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/
Then you will receive the following response.
```json theme={null}
{
"responseContent" : "https://client.example.org/cb/example.com?code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI",
"accessTokenDuration" : 0,
"authorizationCode" : "ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI",
"accessTokenExpiresAt" : 0,
"resultMessage" : "[A040001] The authorization request was processed successfully.",
"resultCode" : "A040001",
"action" : "LOCATION"
}
```
We assume the authorization server makes a redirection response to the user agent, and then the user agent makes the following HTTP `GET` request to the client.
```shell theme={null}
GET /cb/example.com?code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI HTTP/1.1
Host: client.example.org
```
## Token request
The client makes a token request (new lines for readability) to the authorization server.
```http theme={null}
POST /token HTTP/1.1
Host: as.example.com
Authorization: Basic base64(12898884596863:-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg)
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI
&redirect_uri=https://client.example.org/cb/example.com
```
The authorization server is supposed to make a request to Authlete's [`/auth/token`](/api-reference/token-endpoint/process-token-request) API.
```text cURL theme={null}
curl -s -X POST https://us.authlete.com/api//auth/token \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{ "clientId": "", "clientSecret": "", "parameters": "grant_type=authorization_code&code=&redirect_uri=https://client.example.org/cb/example.com" }'
```
```powershell PowerShell theme={null}
$headers = @{ Authorization = "Bearer " }
$body = '{ "clientId": "", "clientSecret": "", "parameters": "grant_type=authorization_code&code=&redirect_uri=https://client.example.org/cb/example.com" }'
Invoke-RestMethod -Method Post `
-Uri "https://us.authlete.com/api/
Authlete returns the following response.
```json theme={null}
{
"grantType" : "AUTHORIZATION_CODE",
"responseContent" : "{\"access_token\":\"R4sd3s02Y1Gj72iI5Md6ZkGapXZ6mSnIEdihTvrM_Ro\",\"refresh_token\":\"k4WqWw2tcDOHxXXo29NxOCwQJyeDOtZ6aw_Y9Ugy-6U\",\"scope\":\"openid\",\"id_token\":\"eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0\",\"token_type\":\"Bearer\",\"expires_in\":86400}",
"resultMessage" : "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessTokenExpiresAt" : 1730554257440,
"accessToken" : "R4sd3s02Y1Gj72iI5Md6ZkGapXZ6mSnIEdihTvrM_Ro",
"resultCode" : "A050001",
"scopes" : [
"openid"
],
"refreshTokenExpiresAt" : 1730554257440,
"subject" : "testuser01",
"action" : "OK",
"refreshTokenDuration" : 864000,
"accessTokenDuration" : 86400,
"refreshToken" : "k4WqWw2tcDOHxXXo29NxOCwQJyeDOtZ6aw_Y9Ugy-6U",
"clientIdAliasUsed" : false,
"idToken" : "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0",
"clientId" : 12898884596863
}
```
The authorization server is expected to return the following response to the client (message #17).
```json theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token":"R4sd3s02Y1Gj72iI5Md6ZkGapXZ6mSnIEdihTvrM_Ro",
"refresh_token":"k4WqWw2tcDOHxXXo29NxOCwQJyeDOtZ6aw_Y9Ugy-6U",
"scope":"openid",
"id_token":"eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0",
"token_type":"Bearer",
"expires_in":86400
}
```
As the client on receiving the response, let's try to decode the token with [Online JWT Verfier](https://kjur.github.io/jsrsasign/tool/tool_jwtveri.html) again.
Open the link above, and paste the value of the `id_token` to textarea in *Step 1*. In this tutorial, the value is: `eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0`
Click the **Just Decode JWT** button in *(Step 3) Verify* and see decoded content in the *Parsed JWT* section.
The decoded results are as follows.
**Header:**
```json theme={null}
{
"alg": "HS256"
}
```
**Payload:**
```json theme={null}
{
"name": "Test User",
"email": "testuser01@example.com",
"email_verified": true,
"iss": "https://as.example.com",
"sub": "testuser01",
"aud": [
"12898884596863"
],
"exp": 1559137301,
"iat": 1559050901,
"nonce": "n-0S6_WzA2Mj"
}
```
Now we can confirm the correct `iss` value exists, and find the additional claims, `name`, `email` and `email_verified` are included as expected.
# Conclusion
In this tutorial, you were able to simulate an authorization server acting as an OpenID Connect Provider, issuing ID Tokens using the authorization code flow with the Authlete API. You completed the following high level steps.
* How to use Authlete APIs to implement authorization code flow into authorization server (OIDC identity provider)
* Set issuer identifier values in the Authlete Management Console including additional claims in an authorization request and see them decoded from the ID Token
While this walk through cut out some key pieces in the whole process, hopefully you have a deeper understanding of how an authorization server, hosted in your own infrastructure, would make use of Authlete as a back end service. Implementing such an authorization server on your own may contain complex logic and require detailed attention to potential secure treatment of codes and secrets. By using the Authlete API as a backend your own authorization server implementation is greatly simplified.
# Next steps
Let's dig deeper on Authlete by playing with the following features.
* [`/auth/authorization/fail`](/api-reference/authorization-endpoint/fail-authorization-request) API (See [Generating error response with "fail" API](/configuration-reference/error-handling-debugging/generating-error-response-using-fail-api))
* [`/auth/userinfo`](/api-reference/userinfo-endpoint/process-userinfo-request) API (See [Access token verification in Userinfo API](/configuration-reference/endpoints/access-token-verification-in-userinfo-api))
* [Token Management](/api-reference/token-operations/list-issued-tokens) API (See [Token Management](/configuration-reference/tokens-and-claims/managing-authorizations-issued-tokens-granted-for-a-client-by-a-user))
* [Authorization server implementations](https://github.com/authlete) (See [Getting Started](/get-started/quickstarts/getting-started))
* Signing an ID token with public key cryptography (See [Changing signing key for ID token](/configuration-reference/tokens-and-claims/changing-signing-key-for-id-tokens))
# Java SDK Overview
Source: https://developers.authlete.com/get-started/quickstarts/overview
The Authlete Java SDK (authlete-java-common) is a wrapper library for the Authlete Web APIs, letting you call Authlete from Java to build an OAuth 2.0 and OpenID Connect authorization server.
The **Authlete Java SDK** is the [`authlete-java-common`](https://github.com/authlete/authlete-java-common) library — a wrapper library for the [Authlete Web APIs](/get-started/concepts/request-and-response).
[Authlete](https://www.authlete.com/) is a cloud service that provides an implementation of OAuth 2.0 and OpenID Connect. Your authorization server calls the Web APIs provided by Authlete to handle the OAuth/OIDC protocol logic, and this library lets you call those Web APIs from a Java application without dealing with HTTP requests and JSON payloads directly.
## What the SDK Provides
* **API client interfaces** for both the [Core API](/get-started/concepts/request-and-response) (runtime OAuth/OIDC protocol handling) and the [Management API](/get-started/concepts/overview) (service, client, and token management).
* **Typed request and response classes** under `com.authlete.common.dto` that model every Authlete API, with JavaDoc describing the pre- and post-processing expected at each step.
* **Utility classes** for common authorization-server tasks such as handling endpoint actions returned by Authlete.
## Related Libraries and Implementations
The SDK is the foundation of Authlete's Java integration stack:
* [`authlete-java-common`](https://github.com/authlete/authlete-java-common) — the core library described here.
* [`authlete-java-jaxrs`](https://github.com/authlete/authlete-java-jaxrs) — JAX-RS (`javax.*`) integration built on top of the common library.
* [`authlete-java-jakarta`](https://github.com/authlete/authlete-java-jakarta) — the Jakarta EE (`jakarta.*`) counterpart of `authlete-java-jaxrs`.
* [`java-oauth-server`](https://github.com/authlete/java-oauth-server) — a reference authorization server implementation. It is a good starting point for your own server; see [Leveraging Reference Implementations](/get-started/developer-resources/leveraging-reference-implementations).
## Installation
Add the dependency with Maven:
```xml theme={null}
com.authleteauthlete-java-common${authlete-java-common.version}
```
Refer to [CHANGES.md](https://github.com/authlete/authlete-java-common/blob/master/CHANGES.md) for the latest version to use in place of `${authlete-java-common.version}`.
## Resources
* **Source code:** [github.com/authlete/authlete-java-common](https://github.com/authlete/authlete-java-common)
* **JavaDoc:** [authlete.github.io/authlete-java-common](https://authlete.github.io/authlete-java-common/)
## Next Steps
To see Authlete in action before writing integration code, follow [Using Demo Authorization Server](/get-started/quickstarts/using-demo-authorization-server) to stand up an authorization server that works as a frontend for Authlete.
# Sample Server
Source: https://developers.authlete.com/get-started/quickstarts/sample-server
After the tutorials, try Authlete with a runnable sample authorization server. A Java reference implementation is available, with more languages planned.
Once you have worked through the [OAuth 2.0](/get-started/quickstarts/oauth-2-0-basics) and [OpenID Connect](/get-started/quickstarts/oidc-basics) tutorials, the next step is to see Authlete working inside an actual authorization server.
Authlete is the backend that handles the OAuth 2.0 and OpenID Connect protocol logic — it does not expose endpoints to clients or end users directly. To run real flows end to end, you pair it with an authorization server *frontend* that calls the Authlete APIs. To make this easy to try, Authlete publishes runnable **sample servers** that you can deploy and exercise against your own service.
## Java
The Java reference implementation, [`java-oauth-server`](https://github.com/authlete/java-oauth-server), is a full-featured authorization server. Beyond the basic OAuth 2.0 / OpenID Connect flows, it supports advanced specifications including **FAPI** (1.0 and 2.0), **OpenID Federation**, and **OpenID for Verifiable Credential Issuance** (OID4VCI), as well as CIBA (Client-Initiated Backchannel Authentication), the OAuth 2.0 Device Authorization Grant (Device Flow), Pushed Authorization Requests (PAR), Grant Management, and Dynamic Client Registration.
To set it up and run OAuth 2.0 / OpenID Connect flows against it, see [**Using Demo Authorization Server**](/get-started/quickstarts/using-demo-authorization-server).
## Other languages
Sample servers for other languages are planned, and will be listed here as they become available.
# Using Demo Authorization Server
Source: https://developers.authlete.com/get-started/quickstarts/using-demo-authorization-server
Quickly set up an authorization server that works as a frontend for Authlete to test OAuth2 and OpenID Connect flows.
This page is for Authlete 3.0. For 2.x, see [Using Demo Authorization Server (2.x)](/v2/get-started/quickstarts/using-demo-authorization-server).
This guide will help you set up and run your authorization server(AS) that works as a frontend for Authlete. Follow these steps to create and configure your service, set up a demo authorization server, and test it.
> **Note:** Get an overview of the Authlete Management Console by reading [Getting Started](/get-started/quickstarts/getting-started).
## Requirements
This quickstart requires either:
* Docker 1.29 or later
* Java
## Step 1: Register for an Authlete Account
To manage and run authorization services, you need an [Authlete Account](https://console.authlete.com/). An Authlete account is free for up to 30 days. You can use a social login, or login using a traditional email/password.
## Step 2: Create an Organization
1. **Log in** to the [Authlete Console](https://console.authlete.com/).
2. **Create a new organization** named `acme`.

## Step 3: Create a Service
1. **Switch** to your newly created `acme` organization.
2. **Create a new service** with a name like `Demo AS`, under this organization.
3. **Keep all settings as default** for now.
4. Click **Create**. Make sure to **note down the Service ID** for use in Step 5.

## Step 4: Generate a Service Access Token
1. Go to **Service Settings** and select **Basic Settings**.
2. Open the **Advanced** tab.
3. In the **Access Tokens** section, enter a name like `Demo AS`.
4. Click **Create** to generate the token. Make sure to **copy the token** for use in Step 5.

## Step 5: Set Up Demo AS
1. **Clone** the Authlete Java OAuth Server repository, one of several reference authorization server implementations [available on GitHub](https://github.com/authlete):
```bash theme={null}
git clone https://github.com/authlete/java-oauth-server.git
cd java-oauth-server
```
2. **Configure** the `authlete.properties` file to connect the authorization server to the Authlete service created in the Step 3.
* Replace `<>` with the Service ID from Step 3.
* Replace `<>` with the Access Token from Step 4.
* Adjust the `base_url` according to your api cluster region:
* `base_url = https://<>.authlete.com`
```properties theme={null}
api_version = V3
base_url = https://us.authlete.com
service.api_key = <>
service.access_token = <>
```
* The `authlete.properties` file includes commented values for Authlete properties that will not be processed by default. Be sure to uncomment Authlete property values before running the server.
3. **Run the server** using either Docker (recommended) or Java:
* **Using Docker** (preferred):
Use the following command to start the server:
```bash theme={null}
docker compose up
```
* **Using Java**:
Ensure that you have Maven installed and `JAVA_HOME` is set correctly. Then, run the following command:
```bash theme={null}
mvn jetty:run
```
## Step 6: Verify the Setup
1. Open your browser and go to `http://localhost:8080`.
2. You should see the home page of the Demo authorization server.

## Step 7: Create a Client
1. To test the authorization server, you need to create a client in the Authlete Console.
2. Navigate to your **Demo AS** within the Authlete Console.
3. Click **Create Client**, enter a descriptive name for the client, and click **Create**.

4. Note down the **Client ID** for later use.
5. Go to **Endpoints -> Global Settings -> Redirect URIs** and click **Add URI**. Add the following Redirect URI:
* `https://oidcdebugger.com/debug`
6. Leave all other settings as default and click **Save Changes**.

## Step 8: Test Your Authorization Server
### OAuth2 Authorization Code Flow
1. Visit [OpenID Connect Debugger](https://oidcdebugger.com/) and provide the following details:
* **Authorize URI**: `http://localhost:8080/api/authorization`
* **Client ID**: Enter the Client ID you copied from Step 6.
2. Set the **Response type** to `code` and **Response mode** to `form_post`.
3. (Optional) To test PKCE, select the **Use PKCE** checkbox. PKCE is enabled by default on your Authlete Service.
4. Scroll down and click the **Send Request** button to start the authorization process.

This will redirect you to the login page of the Demo AS. Enter the following credentials and click **Authorize**:
* **Login ID**: `max`
* **Password**: `max`
You will be redirected back to the OIDC Debugger with an Authorization Code issued by Authlete.

### Issue an ID Token (Implicit Flow)
1. Click **Start over** to return to the home page.
2. Set the **Response type** to `id_token`.
3. Click **Send Request** to initiate a new authorization process.
4. You will be redirected back to the debugger with a JWT ID Token issued by Authlete.

> **Security Note:** ⚠️ For this demo setup, the ID Token is signed using the `client_secret` and the `HS256` algorithm. This configuration is intended for demo purposes only. For real-world applications, you should **upload your JSON Web Key (JWK)** and use an asymmetric signing algorithm like `RS256`.
And that’s it! 🎉 You’ve successfully set up and tested your authorization server with Authlete.
Now that your authorization server is up and running, you can explore more advanced features and configurations:
* Explore the **API Reference Documentation** for more details on Authlete APIs used by your authorization server: [Check the API Docs](/api-reference)
# CBOR Zone
Source: https://developers.authlete.com/get-started/tools/cbor
# mkjose
Source: https://developers.authlete.com/get-started/tools/mkjose
# Health Check
Source: https://developers.authlete.com/api-reference/lifecycle/health-check
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples get /api/lifecycle/healthcheck
Perform a health check of the server.
# Native SSO Logout Processing
Source: https://developers.authlete.com/api-reference/native-sso/native-sso-logout-processing
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/nativesso/logout
The `/nativesso/logout` API is supposed to be used to support the concept of "logout from all applications" in the context of [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) (Native SSO). This is accomplished by deleting access/refresh token records associated with the specified session ID. In Authlete's implementation, access/refresh token records can be associated with a session ID only through the mechanism introduced by Native SSO.
A response from the `/nativesso/logout` API contains `action` response parameter. The possible values
are:
## OK
When the action is `OK`, it indicates that the `/nativesso/logout` API call completed successfully.
## SERVER\_ERROR
When the action is `SERVER_ERROR`, it indicates that something has gone wrong on the Authlete side.
## CALLER\_ERROR
When the action is `CALLER_ERROR`, it indicates that the `/nativesso/logout` API call contained a
problem. For example, the call may have been missing the required request parameter `sessionId`.
# Native SSO Processing
Source: https://developers.authlete.com/api-reference/native-sso/native-sso-processing
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/nativesso
This API should be called by the implementation of a token endpoint to generate the ID token and token response that comply with [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) (Native SSO) when Authlete’s `/auth/token` response indicates `action = NATIVE_SSO` (after you validate the session id and verify or generate the device secret as required by the flow). The token endpoint implementation should retrieve the value of `action` from the response and take the following steps according to the value.
## OK
When the action is `OK`, it indicates that the `/nativesso` API processing has successfully completed.
In this case, the token endpoint implementation should return a successful response (`200 OK`) to
the client. The value of the responseContent property in the `/nativesso` API response can be used
directly as the message body of the token response. Therefore, the success response can be constructed
as follows:
```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
(Embed the value of responseContent here.)
```
## INTERNAL\_SERVER\_ERROR
When the action is `INTERNAL_SERVER_ERROR`, it indicates that something has gone wrong on the Authlete
side. For example, an issue such as a database error might have occurred when retrieving the access
token specified by the accessToken parameter from the database.
In such cases, the token endpoint implementation should return an error response to the client.
The simplest implementation would be to return a `500 Internal Server Error`.
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
(Embed the value of responseContent here.)
```
However, in a production environment, it may be better to return a more abstract error (one that
does not directly describe the nature of the issue), rather than a `500` error.
## CALLER\_ERROR
When the action is `CALLER_ERROR`, it indicates that the issue lies with the caller of the API
(i.e., the implementation of the OpenID Provider). For example, this could be due to missing a
required parameter such as accessToken.
If `CALLER_ERROR` is returned, please review the implementation of your OpenID Provider.
# Create Credential Offer
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/create-credential-offer
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/offer/create
Create a verifiable credential offer
# Get Credential Offer Information
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/get-credential-offer-information
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/offer/info
Get information about a verifiable credential offer
# Issue Batch Credentials
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/issue-batch-credentials
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/batch/issue
Issue multiple verifiable credentials in batch
# Issue Deferred Credential
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/issue-deferred-credential
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/deferred/issue
Issue a deferred verifiable credential
# Issue Single Credential
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/issue-single-credential
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/single/issue
Issue a single verifiable credential
# Parse Batch Credentials
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/parse-batch-credentials
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/batch/parse
Parse multiple verifiable credentials in batch
# Parse Deferred Credential
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/parse-deferred-credential
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/deferred/parse
Parse a deferred verifiable credential
# Parse Single Credential
Source: https://developers.authlete.com/api-reference/verifiable-credential-issuer/parse-single-credential
https://spec.speakeasy.com/authlete/sdk-workspace/authlete-api-explorer-with-code-samples post /api/{serviceId}/vci/single/parse
Parse a single verifiable credential
# Access Token Verification in Userinfo API
Source: https://developers.authlete.com/configuration-reference/endpoints/access-token-verification-in-userinfo-api
Technical information on how Authlete's Userinfo API internally verifies access tokens eliminating the need for the authorization server to make a separate request to Authlete's introspection API.
Authlete's [/auth/userinfo/issue](/api-reference/userinfo-endpoint/issue-userinfo-response) API does internally verify access token in a request from authorization server.
Thus authorization server doesn't have to make a request to Authlete's introspection API ([/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request)) in advance of using the Userinfo API.
# Adding Custom Claims to Userinfo API Responses
Source: https://developers.authlete.com/configuration-reference/endpoints/adding-custom-claims-to-userinfo-api-responses
How to determine and add claims to the Userinfo API payload
## Introduction
The claims returned by the Userinfo API can be customized with the `claims` parameter just like those in [How to Add Custom Claims to ID Tokens](/configuration-reference/tokens-and-claims/adding-claims-to-an-id-token).
This article explains how to add arbitrary claims to the Userinfo endpoint response and which Authlete features help determine the right claims.
## Basic Implementation of the Userinfo Endpoint
When an OpenID Provider builds a Userinfo API endpoint with Authlete, it needs to call the following two APIs:
* Userinfo Request [/api/\{serviceId}/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request)
* Issue Userinfo Response [/api/\{serviceId}/auth/userinfo/issue](/api-reference/userinfo-endpoint/issue-userinfo-response)
Authlete's Userinfo Request endpoint validates the presented access token and returns the user information tied to that token. The OpenID Provider inspects the Userinfo Request response to confirm the token validity and the associated subject, retrieves the subject's attributes from its data store, and submits them to the Issue Userinfo Response API.
### Calling the Userinfo Request API
In practice, the OpenID Provider forwards the user's access token to Authlete's Userinfo Request API:
```sh theme={null}
curl --request POST \
--url https://jp.authlete.com/api/{{serviceId}}/auth/userinfo \
--header 'authorization: Bearer {{serviceAccessToken}}' \
--header 'content-type: application/json' \
--data '{"token": "{{accessToken}}"}'
```
If the access token is valid, the response looks like the following:
```sh theme={null}
{
"action": "OK",
"clientId": 2064424232,
"subject": "john",
"scopes": [
"openid",
"email"
],
"claims": [
"email",
"email_verified"
],
"token": "{{accessToken}}",
"clientIdAlias": "2064424232",
"clientIdAliasUsed": true,
"consentedClaims": [
"email",
"email_verified"
],
"clientEntityIdUsed": false,
"resultCode": "A091001",
"resultMessage": "[A091001] The access token presented at the userinfo endpoint is valid."
}
```
Here `action` is `OK`, which proves the token is valid. Because the `subject` is `john`, the OpenID Provider can collect the attributes linked to `john` and provide them to the Issue Userinfo Response API.
### Calling the Issue Userinfo Response API
Since Authlete does not store attribute values linked to users, the OpenID Provider must prepare the attribute values to be included in the response from the UserInfo endpoint. On the other hand, the OpenID Provider can use the claims parameter returned by Authlete to determine which attributes to include in the UserInfo response.
The `claims` parameter contained in the Userinfo Request API response is a merged list of [claims derived from the scopes specified at the authorization endpoint](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) and [claims explicitly requested via the claims parameter](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter), just like the logic described in [Determining Claims to Add to an ID Token](/configuration-reference/tokens-and-claims/identifying-claims-expected-to-be-included-into-an-id-token).
In the previous example, the requested scope included `openid` and `email`, so the `email` scope expanded to `email` and `email_verified`, and both claims appeared in the `claims` array. For the list of scopes and their corresponding claims, refer to the Authlete Java Common Javadoc [ClaimsScope](https://authlete.github.io/authlete-java-common/com/authlete/common/types/ClaimsScope.html).
Below is an example where the OpenID Provider injects the following claims from Authlete's `claims` response into the Userinfo payload:
| Claim | Value |
| - | - |
| "email" | "[john@example.com](mailto:john@example.com)" |
| "email\_verified" | true |
The request that adds these claims (line breaks inserted for readability) looks like this:
```sh highlight={6-8} theme={null}
curl --request POST \
--url https://jp.authlete.com/api/{{serviceId}}/auth/userinfo/issue \
--header 'authorization: Bearer {{serviceAccessToken}}' \
--header 'content-type: application/json' \
--data '{"token": "{{accessToken}}",
"claims": "{
\"email\": \"john@example.com\",
\"email_verified\": true}" }'
```
A successful response shows that the specified claims were embedded in `responseContent`:
```json theme={null}
{
"action": "JSON",
"responseContent": "{\"sub\":\"john\",\"email\":\"john@example.com\",\"email_verified\":true}",
"resultCode": "A096001",
"resultMessage": "[A096001] User information was obtained successfully."
}
```
## Customizing the List of Claims to Include
The `claims` array calculated by Authlete follows OpenID Connect Core, but it may not match real-world requirements. For example, you might need to return a different set of claims from the Userinfo endpoint than from the ID token, or you may accept only the `openid` and `email` scopes while still needing to send additional user-consented claims in the Userinfo response.
In that case, the OpenID Provider can leverage `consentedClaims` to propagate the claims that the end-user allowed the RP to access at the authorization endpoint to both the Introspection and Userinfo endpoints.
> `consentedClaims` is available in Authlete 2.3 and later.
> For details on `consentedClaims`, please refer to the Authlete JavaDoc: [AuthorizationIssueRequest.html#setConsentedClaims()](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationIssueRequest.html#setConsentedClaims%28%29).
### Telling Authlete Which Claims the User Consented To
Assume that during authorization the user agreed to share their full name, username, and email address with the RP, and the OpenID Provider wants to return those attributes from the Userinfo endpoint.
The authorization endpoint first sends the following request to Authlete and receives a ticket:
```sh theme={null}
curl --request POST \
--url https://jp.authlete.com/api/{{serviceId}}/auth/authorization \
--header 'authorization: Bearer {{serviceAccessToken}}' \
--header 'content-type: application/json' \
--data '{"parameters": "client_id=rest-client-client&scope=openid%20email&response_type=code&redirect_uri=https://example.com&state=xyz&nonce=12345"}'
```
Partial response:
```json theme={null}
{
"action": "INTERACTION",
"service": {
},
"client": {
},
"display": "PAGE",
"maxAge": 0,
"scopes": [
{
"name": "email",
"defaultEntry": false,
"description": "A permission to request an OpenID Provider to include the email claim and the email_verified claim in an ID Token. See OpenID Connect Core 1.0, 5.4. for details."
},
{
"name": "openid",
"defaultEntry": false,
"description": "A permission to request an OpenID Provider to issue an ID Token. See OpenID Connect Core 1.0, 3.1.2.1. for details."
}
],
"ticket": "IWjy7qiCFJsHYHBKAfwJNLHfoV86lL7KYC0ojmi-Qrk",
"resultCode": "A004001",
"resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 666990377) for the authorization request from the client (ID = 1110197986). [response_type=code, openid=true]"
}
```
After obtaining the user's consent to share their full name, username, and email address, the OpenID Provider calls the `auth/authorization/issue` endpoint. It passes `consentedClaims` with `name`, `username`, `email`, and `email_verified`.
```sh theme={null}
curl --request POST \
--url https://jp.authlete.com/api/{{serviceId}}/auth/authorization/issue \
--header 'authorization: Bearer {{serviceAccessToken}}' \
--header 'content-type: application/json' \
--data '{
"ticket": "{{ticket}}",
"subject": "john",
"claims": "{\"claim_for_id_token\":\"value_for_id_token\"}",
"consentedClaims": ["name", "username", "email", "email_verified"]
}'
```
Authlete stores the `consentedClaims` specified at authorization time and later includes them in the responses from `/auth/userinfo` and `/auth/introspection`.
> Ensure that any claim you plan to list in `consentedClaims` has already been registered under the target service's `Tokens & Claims` > `Claims` > `Supported Claims` settings.
>
>
*>*
### Using Consented Claims in the Userinfo API Response
When the access token tied to the above authorization flow is sent to the Userinfo Request API, the response is as follows:
```sh theme={null}
curl --request POST \
--url https://jp.authlete.com/api/{{serviceId}}/auth/userinfo \
--header 'authorization: Bearer {{serviceAccessToken}}' \
--header 'content-type: application/json' \
--data '{"token": "{{accessToken}}"}'
```
```json theme={null}
{
"action": "OK",
"clientId": 1110197986,
"subject": "john",
"scopes": [
"email",
"openid"
],
"claims": [
"email",
"email_verified"
],
"token": "ytaniPwocUVRSDuCBn6EEIR7SYYRxIHkmvv2ht7oPLU",
"clientIdAlias": "rest-client-client",
"clientIdAliasUsed": true,
"clientEntityIdUsed": false,
"consentedClaims": [
"name",
"username",
"email",
"email_verified"
],
"resultCode": "A091001",
"resultMessage": "[A091001] The access token presented at the userinfo endpoint is valid."
}
```
Instead of the `claims` array, the OpenID Provider can use the `consentedClaims` array and return the claims the user agreed to share as the Userinfo endpoint payload.
```sh highlight={6-10} theme={null}
curl --request POST \
--url https://jp.authlete.com/api/{{serviceId}}/auth/userinfo/issue \
--header 'authorization: Bearer {{serviceAccessToken}}' \
--header 'content-type: application/json' \
--data '{"token": "{{accessToken}}",
"claims": "{
\"name\": \"John Doe\",
\"username\": \"jdoe\",
\"email\": \"john@example.com\",
\"email_verified\": true}" }'
```
As a result of the above request, the Userinfo endpoint returns a response containing the user's name, username, and email address, which the user has consented to provide, as shown below.
```json theme={null}
{
"action": "JSON",
"responseContent": "{\"sub\":\"john\",\"name\":\"John Doe\",\"email\":\"john@example.com\",\"email_verified\":true,\"username\":\"jdoe\"}",
"resultCode": "A096001",
"resultMessage": "[A096001] User information was obtained successfully."
}
```
# Checking if an access token has particular scopes
Source: https://developers.authlete.com/configuration-reference/endpoints/checking-if-an-access-token-has-particular-scopes
Information on utilizing Authlete's API feature to determine if an access token has specific scopes for validation in a resource server.
## Overview
Authlete's [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API has a feature that checks if an access token for introspection has particular token. You can specify the scopes as values of an array, in a request to the API.
## Checking scopes associated with a token
Here we assume there is an “Order API” in a resource server. It checks an access token attached with a request to the API and processes the request at least when the token has a **payment** scope.
With Authlete's [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API, you have two implementation options:
1. The resource server sends only the access token to [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API, to get “a list of scopes associated with the token” and determine if it has the **payment** scope
2. The resource server sends “scopes that the token must have” along with the access token to [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API, and gets a response that states if the token is valid
The latter option relatively reduces the complexity of resource server implementation as Authlete manages to check the existence of the scopes.\\
## How to use
[/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API can accept "scopes" parameter. You can use it to check if an access token that is a value of "token" parameter covers particular scopes that are specified the "scopes" parameter.
The "scopes" parameter is an array. It includes each scope as a value. For example, you can check if an access token covers "account" and "payment" scopes by describing them as follows:
```
"scopes": ["account","payment"]
```
Note that the following example doesn't work as expected.
```
"scopes": ["account payment"]
```
Authlete would recognize this value as a single scope, "account payment", that doesn't actually exist, and understand no values are specified for the scopes parameter:
```
"scopes": []
```
As a result, Authlete would do nothing in terms of checking scopes.
## Examples
The following request/response examples show how to check if an access token (MhVD...) with three scopes (**openid**, **profile**, and **payment**) has particular scopes. curl command is used for the examples. (folded for readability)
### When it has particular scopes
* Request
The request is made to check if the token has **openid** and **payment** scopes.
```shell highlight={6} theme={null}
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{
"token":"MhVD...",
"scopes": ["openid","payment"]
}'
```
* Response
Authlete sends back a successful response as the token is valid.
```
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
"clientId": ...,
"expiresAt": ...,
"responseContent": "Bearer error=\"invalid_request\"",
"scopes": [
"openid",
"profile",
"payment"
],
"subject": "testuser01",
...
}
```
### When it doesn't have particular scopes
* Request
The request is made to check if the token has **openid** and **email** scopes.
```shell highlight={6} theme={null}
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{
"token":"MhVD...",
"scopes":["openid","email"]
}'
```
* Response
Authlete sends back an error response as the token that doesn't have the **email** scope is not valid.
```json highlight={4,10-11} theme={null}
{
"resultCode": "A064302",
"resultMessage":
"[A064302] The access token does not cover the required scope 'email'.",
"action": "FORBIDDEN",
"clientId": ...,
"expiresAt": ...,
"responseContent":
"Bearer error=\"insufficient_scope\",
error_description=\"[A064302] The access token does not cover
the required scope 'email'.\",
error_uri=\"https://docs.authlete.com/#A064302\",
scope=\"openid email\"",
"scopes": [
"openid",
"profile",
"payment"
],
"subject": "testuser01",
...
}
```
# Client authentication using client_secret_jwt method
Source: https://developers.authlete.com/configuration-reference/endpoints/client-authentication-using-client-secret-jwt-method
Technical information about client authentication using the client_secret_jwt method in OAuth 2.0 and OpenID Connect.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/client-authentication/client-secret-jwt).
## Preface
**client\_secret\_jwt** is one of client authentication methods defined in [OpenID Connect Core 1.0, 9. Client Authentication](http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) .
On a token request, a client crafts a JWT assertion that contains a message authentication code (MAC) in its signature part, and includes it to the request. Then an authorization server authenticates the client by verifying the signature and payload of the assertion.
Authlete supports client\_secret\_jwt as a client authentication method so that authorization servers can enable it. This article describes overview of the method and setup instructions with Authlete.
## Requirements for client\_secret\_jwt
The following section describes details on both client and authorization server side.
## Client
A client must include the following parameters in a token request when using the client\_secret\_jwt method.
| Parameter | Description |
| - | - |
| client\_assertion\_type | A type of client\_assertion. Its value must be "**urn:ietf:params:oauth:client-assertion-type:jwt-bearer**". |
| client\_assertion | A JWT that contains information for client authentication. It must contain\*\* a MAC using a shared key\*\*. See below for details. |
The value of **client\_assertion** must satisfy the following requirements on its JWT payload and JWT signature. You can find an example JWT in the "[Generating a JWT assertion](#example) " section.
### Payload
A JWT assertion must contain the REQUIRED claims listed below.
| Claim | Description |
| - | - |
| iss | \[REQUIRED] Issuer. This must contain the client\_id of the OAuth client. |
| sub | \[REQUIRED] Subject. This must contain the client\_id of the OAuth client. |
| aud | \[REQUIRED] Audience. A value that identifies the authorization server as an intended audience. The authorization server must verify that it is an intended audience for the token. The audience should be the URL of the authorization server's token endpoint. |
| jti | \[REQUIRED] JWT ID. A unique identifier for the token, which can be used to prevent reuse of the token. These tokens must only be used once unless conditions for reuse were negotiated between the parties; any such negotiation is beyond the scope of this specification. |
| exp | \[REQUIRED] Expiration time on or after which the JWT must not be accepted for processing. |
| iat | \[OPTIONAL] Time at which the JWT was issued. |
### Signature
* The signature of the JWT must be calculated using **HMAC-SHA algorithm** e.g. HS256.
* Client secret must be used as a shared key on calculating the signature.
## Authorization server
An authorization server must process a token request as per the specifications listed below. The details are omitted here because **you can offload these operations from your authorization server to Authlete**.
* [JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants (RFC7523)](https://tools.ietf.org/html/rfc7523)
* [Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants (RFC7521)](https://tools.ietf.org/html/rfc7521)
***
## Configuration settings
This section explains settings for enabling the client\_secret\_jwt method. You have to configure both Authlete service and its client to be authenticated with the method.
## Service Settings
Configure the following setting in the Authlete Management Console.
| Tab | Key | Value |
| - | - | - |
| Service Settings > Endpoints > Token | Supported Client Authentication Methods | Enable "**CLIENT\_SECRET\_JWT**" |
To configure Authlete's Service Settings:
1. Log in to the [Authlete Management Console](https://console.authlete.com/)
2. Click on your Organization name and choose your Service.
3. Navigate to **Service Settings > Endpoints > Token**
4. Under the **Supported Client Authentication Methods** section, select the `CLIENT_SECRET_JWT` checkbox.
5. Click Save Changes to update your service settings.

## Client Settings
Configure the following setting in the Authlete Management Console.
| Tab | Key | Value |
| - | - | - |
| Basic | Client Type | **CONFIDENTIAL** |
| Endpoints > Token | Client Authentication Method | **CLIENT\_SECRET\_JWT** |
| Endpoints > Token | Assertion Signature Algorithm | **HS256**, \*\*HS384 \*\*or **HS256** |
Configure Basic Settings:
1. Navigate to **Client Settings > Basic Settings > General**
2. For Client Type, choose the `CONFIDENTIAL` radio button.
3. Click Save Changes to apply the updates.

Configure Endpoints:
1. Navigate to **Client Settings > Endpoints > Token > General**
2. Under the **Client Authentication Method** section, open the dropdown menu and select `CLIENT_SECRET_JWT`
3. Under the **Assertion Signature Algorithm** section, open the dropdown menu and select `HS256`
4. Click Save Changes to apply the updates.

***
## Example
This example shows how to use the `client_secret_jwt` method for client authentication at the token endpoint of the authorization server.
## Generating a JWT assertion
Let's generate a JWT that will be used as a value of the **client\_assertion** in a token request.
### Preparing a JWT payload
First, create a JSON formatted payload and save it as "payload.json".
```
{
"jti": "myJWTId001",
"sub": "38174623762",
"iss": "38174623762",
"aud": "https://as.example.com/token",
"exp": 1536165540,
"iat": 1536132708
}
```
### Generating a JWT
Generate a JWT assertion including the payload, and a MAC using the client's shared key (client secret). The example below is an instruction using an [authlete-jose library](https://github.com/authlete/authlete-jose) . Or you can use [mkjose.org](https://mkjose.org/) website to do that.
```
$ bin/jose-generator \
--payload-file payload.json \
--sign \
--signing-alg HS256 \
--signing-alg-key TzPTZDtcw9ek41H1VmofRoXQddP5cWCXPWidZHSA2spU6gZN9eIFUiXaHD7OfxtBhTxJsg_I1tdFI_CkKl8t8Q
```
The generated JWT will look like this (with line breaks for display purposes only):
```
eyJhbGciOiJIUzI1NiJ9.
ewogICJqdGkiOiJteUpXVElkMDAxIiwKICAic3ViIjoiMzgxNzQ2MjM3NjIiLAogIC
Jpc3MiOiIzODE3NDYyMzc2MiIsCiAgImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5j
b20vdG9rZW4iLAogICJleHAiOjE1MzYxNjU1NDAsCiAgImlhdCI6MTUzNjEzMjcwOA
p9Cg.
UaxLRRjeNP0eTW1qUTS9fwATSO4ubu3bMCJi-OIVBxo
```
This JWT serves as the value for `client_assertion`, which the client includes when making a token request.
## Token request and response
### Token request from the client to the authorization server
Assume the client that has the assertion makes a token request to the authorization server. (folded for readability)
```http highlight={8-16} theme={null}
POST /token HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=Gw30fMKJBHkcOBSde5awLrMm4ahvgCNM2cFSTUOUflY&
redirect_uri=https://example.com/redirection&
client_assertion_type=
urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
client_assertion=
eyJhbGciOiJIUzI1NiJ9.
ewogICJqdGkiOiJteUpXVElkMDAxIiwKICAic3ViIjoiMzgxNzQ2MjM3NjIiLAogIC
Jpc3MiOiIzODE3NDYyMzc2MiIsCiAgImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5j
b20vdG9rZW4iLAogICJleHAiOjE1MzYxNjU1NDAsCiAgImlhdCI6MTUzNjEzMjcwOA
p9Cg.
UaxLRRjeNP0eTW1qUTS9fwATSO4ubu3bMCJi-OIVBxo
```
### API request from the authorization server to Authlete
The authorization server forwards the request content to Authlete's [/auth/token API](/api-reference/token-endpoint/process-token-request). Make sure the `` in the token request matches your Authlete Service ID. (folded for readability)
```shell highlight={8-16} theme={null}
curl -v -X POST https://us.authlete.com/api//auth/token \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ' \
-d '{
"parameters":"grant_type=authorization_code&
code=Gw30fMKJBHkcOBSde5awLrMm4ahvgCNM2cFSTUOUflY&
redirect_uri=https://example.com/redirection&
client_assertion_type=
urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
client_assertion=
eyJhbGciOiJIUzI1NiJ9.
ewogICJqdGkiOiJteUpXVElkMDAxIiwKICAic3ViIjoiMzgxNzQ2MjM3NjIiLAogIC
Jpc3MiOiIzODE3NDYyMzc2MiIsCiAgImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5j
b20vdG9rZW4iLAogICJleHAiOjE1MzYxNjU1NDAsCiAgImlhdCI6MTUzNjEzMjcwOA
p9Cg.
UaxLRRjeNP0eTW1qUTS9fwATSO4ubu3bMCJi-OIVBxo}'
```
### API response from Authlete to the authorization server
Authlete processes the request and send back an API response to the authorization server as follows. (folded for readability)
```
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "kwXY57oN4nBOqxk57vW2fo-WzgezrwSl2h1N_xW8aKI",
"responseContent": {
"access_token": "kwXY57oN4nBOqxk57vW2fo-WzgezrwSl2h1N_xW8aKI",
"refresh_token": "5zBNsdrlMojcMH3wCrfaXpmAY6vKqOqeV3q1ebRJzGM",
"scope": null,
"token_type": "Bearer",
"expires_in": 3600
},
...
}
```
### Token response from the authorization server to the client
The authorization server extracts the value of "responseContent" and send it back to the client as a token response (details omitted).
***
## See Also
* [Configuring Client Authentication](/configuration-reference/endpoints/configuring-client-authentication)
> This article describes basics of client authentication configuration in Authlete.
* [Client authentication using private\_key\_jwt method](/configuration-reference/endpoints/client-authentication-using-private-key-jwt-method)
> Authlete supports **private\_key\_jwt** as a client authentication method so authorization servers can enable it. This article describes overview of the method and setup instructions with Authlete.
* [OAuth 2.0 Client Authentication - Takahiko Kawasaki - Medium](https://medium.com/@darutk/oauth-2-0-client-authentication-4b5f929305d4)
> This article explains “**OAuth 2.0 client authentication**”. In addition to the client authentication methods described in [RFC 6749](https://tools.ietf.org/html/rfc6749) , this article explains methods that utilize a client assertion and a client certificate.
# Client authentication using private_key_jwt method
Source: https://developers.authlete.com/configuration-reference/endpoints/client-authentication-using-private-key-jwt-method
Technical information on client authentication using the private_key_jwt method in OAuth 2.0 with an overview of the method setup instructions with Authlete and requirements for both the client and the authorization server side.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/client-authentication/client-auth-private-key-jwt).
## Preface
**private\_key\_jwt** is one of client authentication methods defined in [OpenID Connect Core 1.0, 9. Client Authentication](http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
.
On a token request, a client crafts a digitally signed JWT assertion and includes it to the request. Then an authorization server authenticates the client by verifying the signature and payload of the assertion.
Authlete supports `private_key_jwt` as a client authentication method so that authorization servers can enable it. This article describes overview of the method and setup instructions with Authlete.\\
## Requirements for private\_key\_jwt
The following section describes details on both client and authorization server side.
## Client
A client must include the following parameters in a token request when using the private\_key\_jwt method.
| Parameter | Description |
| - | - |
| client\_assertion\_type | A type of client\_assertion. Its value must be "**urn:ietf:params:oauth:client-assertion-type:jwt-bearer**". |
| client\_assertion | A JWT that contains information for client authentication. It must be **digitally signed using a private key**. See below for details. |
The value of **client\_assertion** must satisfy the following requirements on its JWT payload and JWT signature. You can find an example JWT in the "[Generating a JWT assertion](#example)
" section.
### Payload
A JWT assertion must contain the REQUIRED claims listed below.
| Claim | Description |
| - | - |
| iss | \[REQUIRED] Issuer. This must contain the client\_id of the OAuth client. |
| sub | \[REQUIRED] Subject. This must contain the client\_id of the OAuth client. |
| aud | \[REQUIRED] Audience. A value that identifies the authorization server as an intended audience. The authorization server must verify that it is an intended audience for the token. The audience should be the URL of the authorization server's token endpoint. |
| jti | \[REQUIRED] JWT ID. A unique identifier for the token, which can be used to prevent reuse of the token. These tokens must only be used once unless conditions for reuse were negotiated between the parties; any such negotiation is beyond the scope of this specification. |
| exp | \[REQUIRED] Expiration time on or after which the JWT must not be accepted for processing. |
| iat | \[OPTIONAL] Time at which the JWT was issued. |
### Signature
* A JWT assertion must be digitally signed using **a private key in asymmetric cryptography** (e.g. RS256).
* A client using the authentication method has to register **its public key** to an authorization server in advance so that the server can verify the assertion.
## Authorization server
An authorization server must process a token request as per the specifications listed below. The details are omitted here because **you can offload these operations from your authorization server to Authlete**.
* [JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants (RFC7523)](https://tools.ietf.org/html/rfc7523)
* [Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants (RFC7521)](https://tools.ietf.org/html/rfc7521)
***
## Configuration settings
This section explains settings for enabling the private\_key\_jwt method. You have to configure both Authlete service and its client to be authenticated with the method.
## Service settings
Log in to the Authlete Management Console and navigate to **Service Settings > Endpoints > Token**. Under the **Supported Client Authentication Methods** section, enable the `PRIVATE_KEY_JWT` checkbox.

## Client settings
| Tab | Key | Value |
| - | - | - |
| Basic Settings | Client Type | **CONFIDENTIAL** |
| Endpoints > Token | Client Authentication Method | **PRIVATE\_KEY\_JWT** |
| Endpoints > Token | Assertion Signature Algorithm | **RS256**, **RS384**, **RS512**,**ES256**, **ES384**, **ES512**,**PS256**, **PS384**, or **PS512** |
| Key Management > JWK Set | JWK Set Content | A JWK set including **a public key used for verifying the client's JWT assertion** |
Navigate to **Client Settings > Endpoints > Token**. Under the **Client Authentication Method** section, open the dropdown menu and select `PRIVATE_KEY_JWT` and select `ES256` as the **Assertion Signature Algorithm**.
Click Save Changes to apply the updates.

Navigate to **Key Management > JWK Set**. Under the **JWK Set Content** section, enter your JWK Set.
Click Save Changes to apply the updates.

## Example
This example shows client authentication using `private_key_jwt` at the token endpoint of the authorization server.
## Generating a JWT assertion
Let's generate a JWT that will be used as a value of the **client\_assertion** in a token request.
### Preparing a JWT payload
First, create a JSON formatted payload and save it as "payload.json".
```json theme={null}
{
"jti": "myJWTId001",
"sub": "38174623762",
"iss": "38174623762",
"aud": "https://as.example.com/token",
"exp": 1536165540,
"iat": 1536132708
}
```
### Preparing a JWK set
Then, prepare a JWK set for signing and verification. Here we use [mkjwk.org](https://mkjwk.org/)
to generate a JWK set.\\
The examples below are ones generated using `ES256` algorithm, and saved the public and private key pair as a file "key\_pair.jwk".
> Make sure to register the public key (the latter one) to Authlete and configure Client Authentication Method (PRIVATE\_KEY\_JWT) and Client Assertion Algorithm (ES256). These settings can be done via the Authlete Management Console, as described in the previous section.
* Public and private key pair
```json theme={null}
{
"kty": "EC",
"d": "ukQKQexNI8PtEv7SKpqUDnbZ-WkN6HaQqcVrVV8ZWRQ",
"use": "sig",
"crv": "P-256",
"x": "9Yxd2TvwBbgmupZh3bpg3umKihM_FNAk2_uI_-Edv_Q",
"y": "BOUFuyvWoBZ9-RVSeHJLF-L4I3ORv0xbaM1CKCFJr54",
"alg": "ES256"
}
```
* Public key (to be registered to Authlete via the Authlete Management Console)
```json theme={null}
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"x": "9Yxd2TvwBbgmupZh3bpg3umKihM_FNAk2_uI_-Edv_Q",
"y": "BOUFuyvWoBZ9-RVSeHJLF-L4I3ORv0xbaM1CKCFJr54",
"alg": "ES256"
}
```
### Generating a JWT
Generate a JWT assertion including the payload and signed with the private key. The example below is an instruction using an [authlete-jose library](https://github.com/authlete/authlete-jose)
. Or you can use [mkjose.org](https://mkjose.org/)
website to do that.
```bash theme={null}
bin/jose-generator \
--payload-file payload.json \
--sign \
--signing-alg ES256 \
--jwk-signing-alg-file key_pair.jwk
```
The generated JWT will look like this (with line breaks for display purposes only):
```plaintext theme={null}
eyJhbGciOiJFUzI1NiJ9.
ewogICJqdGkiOiJteUpXVElkMDAxIiwKICAic3ViIjoiMzgxNzQ2MjM3NjIiL
AogICJpc3MiOiIzODE3NDYyMzc2MiIsCiAgImF1ZCI6Imh0dHBzOi8vYXMuZX
hhbXBsZS5jb20vdG9rZW4iLAogICJleHAiOjE1MzYxNjU1NDAsCiAgImlhdCI
6MTUzNjEzMjcwOAp9Cg.
YB4gdhWUGRjWEsEbKDs7-G2WFH2oYz7bAEP5AtegHXInkY9ncA2V3IoA6O_HV
QuFxyCRIklrxsMk32MfNF_ABA
```
This JWT is a value of `client_assertion`, to be used by the client on making a token request.
## Token request and response
### Token request from the client to the authorization server
Assume the client that has the assertion makes a token request to the authorization server. (folded for readability)
```plaintext theme={null}
POST /token HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=Gw30fMKJBHkcOBSde5awLrMm4ahvgCNM2cFSTUOUflY&
redirect_uri=https://example.com/redirection&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
client_assertion=eyJhbGciOiJFUzI1NiJ9.ewogICJqdGkiOiJteUpXVElkMDAxIiwKICAic3ViIjoiMzgxNzQ2MjM3NjIiLAogICJpc3MiOiIzODE3NDYyMzc2MiIsCiAgImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20vdG9rZW4iLAogICJleHAiOjE1MzYxNjU1NDAsCiAgImlhdCI6MTUzNjEzMjcwOAp9Cg.YB4gdhWUGRjWEsEbKDs7-G2WFH2oYz7bAEP5AtegHXInkY9ncA2V3IoA6O_HVQuFxyCRIklrxsMk32MfNF_ABA
```
### API request from the authorization server to Authlete
The authorization server forwards the request content to Authlete's [/auth/token](/api-reference/token-endpoint/process-token-request). (folded for readability)
```bash theme={null}
$ curl -s -X POST https://us.authlete.com/api/auth/token \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{
"parameters":"grant_type=authorization_code&code=Gw30fMKJBHkcOBSde5awLrMm4ahvgCNM2cFSTUOUflY&redirect_uri=https://example.com/redirection&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=eyJhbGciOiJFUzI1NiJ9.ewogICJqdGkiOiJteUpXVElkMDAxIiwKICAic3ViIjoiMzgxNzQ2MjM3NjIiLAogICJpc3MiOiIzODE3NDYyMzc2MiIsCiAgImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20vdG9rZW4iLAogICJleHAiOjE1MzYxNjU1NDAsCiAgImlhdCI6MTUzNjEzMjcwOAp9Cg.YB4gdhWUGRjWEsEbKDs7-G2WFH2oYz7bAEP5AtegHXInkY9ncA2V3IoA6O_HVQuFxyCRIklrxsMk32MfNF_ABA"
}'
```
### API response from Authlete to the authorization server
Authlete processes the request and sends back an API response to the authorization server as follows. (folded for readability)
```json theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "ni6uDszfkeR5GH96k3cUjt3R7MHG9-xRbMDObaKGY2A",
"responseContent": {
"access_token": "ni6uDszfkeR5GH96k3cUjt3R7MHG9-xRbMDObaKGY2A",
"refresh_token": "dyzc8D96hSdrCmaPaB75uFiqjWTIWHXq-_OjVN17gAk",
"scope": null,
"token_type": "Bearer",
"expires_in": 3600
},
...
}
```
### Token response from the authorization server to the client
The authorization server extracts the value of "responseContent" and sends it back to the client as a token response (details omitted).
## See Also
* [Configuring Client Authentication](/configuration-reference/endpoints/configuring-client-authentication)
> This article describes basics of client authentication configuration in Authlete.
* [Client Authentication Using client\_secret\_jwt Method](/configuration-reference/endpoints/client-authentication-using-client-secret-jwt-method)
> Authlete supports **client\_secret\_jwt** as a client authentication method so authorization servers can enable it. This article describes overview of the method and setup instructions with Authlete.
* [OAuth 2.0 Client Authentication - Takahiko Kawasaki - Medium](https://medium.com/@darutk/oauth-2-0-client-authentication-4b5f929305d4)
> This article explains “**OAuth 2.0 client authentication**”. In addition to the client authentication methods described in [RFC 6749](https://tools.ietf.org/html/rfc6749)
> , this article explains methods that utilize a client assertion and a client certificate.
# Client authentication using tls_client_auth method
Source: https://developers.authlete.com/configuration-reference/endpoints/client-authentication-using-tls-client-auth-method
Overview and instructions on enabling client authentication using TLS client authentication method in Authlete.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/client-authentication/tls-client-auth).
# Client Authentication Using the tls\_client\_auth Method
## Preface
[A section of RFC 8705, “2. Mutual TLS for OAuth Client Authentication”](https://www.rfc-editor.org/rfc/rfc8705.html#name-mutual-tls-for-oauth-client)
defines how authorization servers authenticate clients using mutual TLS.
Authlete offers a feature that enables authorization servers to authenticate clients using TLS client certificates. This article describes an overview of the feature and instructions to enable it.
## How TLS Client Authentication Works in Authlete
The TLS client authentication feature of Authlete employs `client ID` and `subject name` (subject distinguished name / subject alternative name) to authenticate clients.
When processing token requests, an authorization server is responsible for providing the `client ID` and `subject name` properties to Authlete. Client ID is included in content of a token request by a client, so an authorization server doesn't have to care. “Subject name” is not in the content but in a client certificate, that can be obtained from a mutual TLS connection between a client and an authorization server.
On Authlete's side, you have to enable `TLS_CLIENT_AUTH` client authentication method and register the client's `subject name`.
## Service settings
| Tab | Item | Value |
| - | - | - |
| Service Settings > Endpoints > Token | Supported Client Authentication Methods | TLS\_CLIENT\_AUTH |
To enable the TLS Client Authentication feature of Authlete for your service:
1. Log in to the [Authlete Management Console](https://console.authlete.com/).
2. Click on your Organization name and choose your Service.
3. Navigate to **Service Settings > Endpoints > Token > General**
4. Under `Client Authentication Method`, select the `TLS_CLIENT_AUTH` method.
5. Click Save Changes to update your service settings.

## Client settings
> This example uses Subject Distinguished Name (Subject DN) as a type of “subject name” though, Authlete in fact supports other various types of Subject Alternative Name (SAN) in addition to Subject DN.
>
> | Tab | Item | Value |
> | - | - | - |
> | Client Settings > Endpoints > Token | Client Authentication Method | TLS\_CLIENT\_AUTH |
> | Client Settings > Endpoints > Token | Subject Distinguished Name | (e.g. CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP) |
To enable the `TLS_CLIENT_AUTH` method for your client:
1. Log in to the [Authlete Management Console](https://console.authlete.com/).
2. Click on your Organization name and choose your Service.
3. Navigate to **Client Settings > Endpoints > Token > General**
4. Under `Client Authentication Method`, open the dropdown menu and select the `TLS_CLIENT_AUTH` method.
5. Click Save Changes to apply the updates.

To register the `subject name` for your client:
1. Navigate to **Client Settings > Endpoints > Token > MTLS**
2. Enter a desired value for `Subject Distinguished Name`. You can also specify other name values used for client authentication, including:
* Subject Alternative Name DNS
* Subject Alternative Name IP Address
* Subject Alternative Name URI
* Subject Alternative Name Email
* Self Signed Certificate Key ID
3. Click Save Changes to apply the updates.

With the settings above, Authlete will support mutual TLS authentication as a client authentication method and apply the method to process token requests from the client above. Subject DN CN=client.example.org, ... is used as the identifier of the client.
## Example
The following is an example of a request to [/auth/token](/api-reference/token-endpoint/process-token-request) (folded for readability). An authorization server establishes a mutual TLS connection with a client, obtains the client's certificate from the connection, and makes the request to the API.
Content of a token request, that includes a client ID (client\_id), is specified as a value of "parameters," and a client certificate as a value of "clientCertificate".
```
curl -v -X POST https://us.authlete.com/api/21653835348762/auth/token \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ' \
-d '{
"parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&client_id=591205987816490&code=HVIza0dGG9nDKGStAzMObYH9GkXME0aRSaLEcToHEI8",
"clientCertificate":"-----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-----"}'
```
## See Also
* [Configuring Client Authentication](/configuration-reference/endpoints/configuring-client-authentication)
> This article describes basics of client authentication configuration in Authlete.
* [Issuing mutual-TLS Certificate-bound Access Tokens](/configuration-reference/tokens-and-claims/issuing-mutual-tls-certificate-bound-access-tokens)
> This article describes setup instructions for Authlete to use “Mutual-TLS certificate-bound access tokens,” defined in “RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens.”
* [Authlete FAPI Enhancements](https://www.authlete.com/resources/videos/20180724/authlete-fapi-enhancements/)
> This video is one of the sessions at “[Financial APIs Workshop 2018](https://financial-api.net/)
> ”, held on July 24th, 2018 in Tokyo. Justin Richer from Authlete talks about comparison of Authlete’s unique semi-hosted approach and traditional approaches for deploying OAuth infrastructure, and how Authlete has extended its client authentication functions and supported mutual TLS to implement Financial-grade API (FAPI).
# Configuring Client Authentication
Source: https://developers.authlete.com/configuration-reference/endpoints/configuring-client-authentication
Basics of client authentication configuration in Authlete and how client authentication works in the context of processing token requests.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/client-authentication/client-authentication).
## Preface
This article provides an overview of the fundamental aspects of configuring client authentication in Authlete.
## How Client Authentication Works in Authlete
Authlete performs client authentication during token requests processing ([/auth/token](/api-reference/token-endpoint/process-token-request)) by using both **pre-configured information** and **information obtained at runtime**.
### Pre-configured Information
You will configure both an **Authlete Service** (an API instance acting as a backend of an authorization server) and a **client** in the service.
* **Authlete service**
Specify which client authentication methods are enabled by the authorization server. You can enable multiple methods at the same time.
* **Client**
Specify which one of the enabled methods is applicable for the client, registered in the service (i.e. establishing a connection with the authorization server). You may also configure information required for the authentication method (e.g. the client's public key, subject name of the client's certificate).
### Information obtained at runtime
On receiving a token request from a client, an authorization server passes the content of the request to Authlete's [/auth/token](/api-reference/token-endpoint/process-token-request) API that processes it. Authlete parses the content, detects the identity of the client, determines the pre-specified authentication method, and fulfills client authentication.
Some client authentication methods require additional information such as a value of Authorization header in HTTP request from the client to the authorization server, the client's certificate used in mutual TLS connection between the parties. In such cases, the authorization server extracts those information from the HTTP request or the mutual TLS, and sends it to Authlete, along with the content of the token request.
***
## Configuration example
This section shows an example to use **CLIENT\_SECRET\_BASIC** method to authenticate a client (ID: **1257...**) and fulfill a token request.
### Service Settings
The following settings are done in the pre-configuration.
To enable the `CLIENT_SECRET_BASIC` method in your service:
1. Log in to the [Authlete Management Console](https://console.authlete.com/)
2. Click on your Organization name and choose your Service.
3. Navigate to **Service Settings > Endpoints > Token**
4. Under the **Supported Client Authentication Methods** section, select the `CLIENT_SECRET_BASIC` checkbox.
5. Click Save Changes to apply the updates.

### Client Settings
To enable the `CLIENT_SECRET_BASIC` method for your client:
1. Navigate to **Client Settings > Endpoints > Token > General**
2. Under the **Client Authentication Method** section, open the dropdown menu and select `CLIENT_SECRET_BASIC`
3. Click Save Changes to apply the updates.

### Client ID and Client Secret in Client Basic Settings
**CLIENT\_SECRET\_BASIC** requires only “client ID” and “client secret” to authenticate the client. In Authlete, these values are automatically generated ("**1257...**" and "**gTyu...**" respectively in this example). The client administrator sets the auto-generated values to the client.
To view your Client ID and Client Secret in the Authlete Management Console:
1. Log in to the [Authlete Management Console](https://console.authlete.com/)
2. Click on your Organization name and choose your Service.
3. Navigate to **Client Settings > Basic Settings > General**

### Client Request Flow from Start to Finish
Here are flows from a token request by the client to fulfillment by Authlete.
**Step 1**
The client prepares a token request. The request will be sent to the authorization server with information needed for client authentication. In this case, the client sets its client ID (**1257...**) and secret (**gTyu...**) to Authorization header of the HTTP request.
```http highlight={2} theme={null}
POST /token HTTP/1.1
Authorization: Basic base64(1257...:gTyu...)
Host: as.example.com
...
grant_type=authorization_code&
code=...&
redirect_uri=...
```
**Step 2**
The authorization server obtains the actual content of the token request ("grant\_type=authorization\_code&..." in this example) from body part of the HTTP request. The client's ID and secret are also extracted from the Authorization in the HTTP header part at the same time.
| Item | Value |
| - | - |
| Content of token request | `grant_type=authorization_code&code=...&redirect_uri=...` |
| Client ID | `1257...` |
| Client secret | `gTyu...` |
**Step 3**
The authorization server makes a request to Authlete's [/auth/token](/api-reference/token-endpoint/process-token-request) API. The request contains the values that have been obtained in the step 2; the content of the token request and the client's ID and secret, as "parameters", "clientId", "clientSecret" respectively.
```http highlight={6-10} theme={null}
POST /api/{Service ID}/auth/token HTTP/1.1
Host: us.authlete.com
...
{
"clientId":"1257...",
"clientSecret":"gTyu...",
"parameters":
"grant_type=authorization_code
&code=...&redirect_uri=..."}
```
**Step 4**
By using the client's ID in the API request, Authlete determines the identity of the client that is the source of the token request. Authlete eventually recognizes that CLIENT\_SECRET\_BASIC is the method to authenticate the client, checks the value of the client's secret, and decides if the authentication is successful or not.
***
## Method Specific Configuration Tips
Both client information to be pre-configured in an Authlete service, and tasks to be done by an authorization server on receiving an token request, are different for each client authentication method.
This table describes configuration tips for some of the methods supported by Authlete.
| **Authentication Method** | **Client Behavior** | **Authlete Settings** | **Authorization Server Settings** |
| - | - | - | - |
| **CLIENT\_SECRET\_BASIC** | The client sets its ID and secret in the `Authorization` header when sending a token request. | No additional settings required; Authlete automatically generates and manages the client’s ID and secret. | Extracts the client’s ID and secret and include them as parameters in the [/auth/token](/api-reference/token-endpoint/process-token-request) |
| **CLIENT\_SECRET\_POST** | The client includes its ID and secret as parameters in the token request. | No additional settings required, similar to `CLIENT_SECRET_BASIC`. | No additional operations needed; the client’s ID and secret are part of the token request content. |
| **CLIENT\_SECRET\_JWT** | The client generates a JWT assertion containing a MAC (message authentication code) calculated using the client's secret and adds it as a parameter in the token request. For more details, see [Client authentication using client\_secret\_jwt method](/configuration-reference/endpoints/client-authentication-using-client-secret-jwt-method) | Specify the “assertion signing algorithm” for the JWT assertion in Authlete. Authlete automatically generates and manages the client’s secret. | No additional operations needed; the JWT assertion is part of the token request content. |
| **PRIVATE\_KEY\_JWT** | The client generates a digitally signed JWT assertion using public key cryptography and adds it as a parameter in the token request. For more details, see [Client authentication using private\_key\_jwt method](/configuration-reference/endpoints/client-authentication-using-private-key-jwt-method) | You have to Specify the “assertion signing algorithm” of the JWT assertion and register the client’s public key in Authlete. | The server doesn't have to do additional operations; the JWT assertion is part of the token request content. |
| **TLS\_CLIENT\_AUTH** | The client establishes a mutual TLS connection and is authenticated using the client's certificate obtained from the connection. For more details, see [Client authentication using tls\_client\_auth method](/configuration-reference/endpoints/client-authentication-using-tls-client-auth-method). | You have to specify the `“subject name”` of the client’s certificate in Authlete. | Extracts the client’s certificate from the mutual TLS connection and includes it as a parameter in the [/auth/token](/api-reference/token-endpoint/process-token-request). |
# Configuring JARM
Source: https://developers.authlete.com/configuration-reference/endpoints/enabling-jarm
Technical guide on enabling JARM a response mode for encoding authorization responses to JWTs for secure authorization responses.
## Preface
[JARM (JWT Secured Authorization Response Mode for OAuth 2.0)](https://bitbucket.org/openid/fapi/src/master/Financial_API_JWT_Secured_Authorization_Response_Mode.md)
is a response mode to encode authorization responses to JWTs. It allows authorization servers to provide secure authorization responses with signature, encryption, sender authentication, audience restriction etc.
This article describes instructions to enable JARM.
## Registering a JWK set to an Authlete service
This article assumes that you have registered a set of JWK to your Authlete service. See [the related article](/configuration-reference/key-management/jwk-set-settings-for-an-authlete-service)
for the registration. The following screenshot is an example showing the registered JWK set.
*JWK Set Content*
## Enabling signing for authorization responses to a client
Log in to the Authlete Management Console that corresponds to the service above, and you will see "Your Apps” page that includes a list of clients of the service. Click “Edit” button of the client that may ask the service to create JARM compliant authorization responses.
*Your Apps*
Go to Authorization tab and you will see "Authorization Response Signature Algorithm” in Authorization Endpoint section. Choose an appropriate algorithm that matches to the one of keys. For example, in this article, “ES256” has been selected because it is the only algorithm registered to the service. \\
*Authorization Response Signature Algorithm*
## Testing the configuration
Now that you have completed the basic JARM settings for the Authlete service, that supports authorization requests including JARM parameters e.g. **response\_mode=jwt**. The service will make an authorization response in accordance with the request parameter, for example:
```
https://client.example.org/cb/example.com
?response=eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.
eyJhdWQiOiIxNzU2NjE2MDYwMzc2NiIsImNvZGUiOiJF
V2RYbkE0TEZYRFNGTGVnTmlMTVRoUHlITjhwTUlaelVN
Tmo5N28wbnBJIiwiaXNzIjoiaHR0cHM6Ly9hcy5leGFt
cGxlLmNvbSIsImV4cCI6MTU5MTA4MDk0OH0.
dGi84kTrwX-5bX3S0Mca7_2f7GhEnGt6Dj01b60s67GP
VJkwzuEr9y8C2KLgEpkS35zZO41mmRNkpRo8NUlkvw
```
# Handling Parameters in Authorization Requests and Token Requests
Source: https://developers.authlete.com/configuration-reference/endpoints/handling-request-parameters
How to manage parameters in authorization and token requests through Authlete APIs.
*Using "parameters" on /auth/authorization API*
An authorization server frontend is expected to pass parameters, which are received at authorization endpoints and token endpoints, to respective Authlete APIs **as-is, i.e. you usually MUST NOT modify/pre-process them**.
For example, if you want to check values of scope parameter in an authorization request, you don't have to have the authorization server to check them before invoking Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API; the server should include whole query parameters as a value of parameters key of the API. Authlete will be making a response that includes values, that are verified by Authlete, in scope parameter. Then your authorization server frontend will be able to check those verified scope values.
There are some risks to implement your own verification/modification processes before invoking [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) or [/auth/token](/api-reference/token-endpoint/process-token-request) API; You might fail to use Authlete's functions for OAuth / OpenID Connect, such as protocol handling and error checking. In the worst-case scenario, your authorization server would be non-compliant to OAuth / OpenID Connect if you don't fully understand such standards.
# Handling Responses from Authlete API
Source: https://developers.authlete.com/configuration-reference/endpoints/handling-responses-from-authlete-apis
Handling responses from Authlete API and the importance of processing based on the action parameter rather than the resultCode value.
Most of Authlete APIs make a response that includes "action" parameter. On implementing an authorization server with Authlete, there is a principle of processing; making a request to one of Authlete's APIs, parsing a response from the API, checking a value of "action" in the response and processing in accordance with the value.
> You **NEVER** use a value of resultCode as condition of processing.
For example, an response from [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API includes either the following values in "action" field:
* BAD\_REQUEST
* FORM
* INTERACTION
* INTERNAL\_SERVER\_ERROR
* LOCATION
* NO\_INTERACTION
**action=INTERACTION** indicates that an authorization request is valid. Your authorization server is expected to generate a webpage (HTML) to authenticate a user and ask his/her consent, send the webpage to the user's Web browser, and do some interaction with him/her. Once the auth process is done successfully, your authorization server will make a request to [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API. if unsuccessful (e.g. the user cancelled the process), the server will do a request to [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) API instead. (cf. [Ticket Parameter in Authorization Endpoint](/configuration-reference/endpoints/ticket-parameter-in-authorization-endpoint) )
**action=BAD\_REQUEST** indicates that an authorization request is invalid and your authorization server should make a "400 Bad Request" response to Web browser. The response will be like this:
```
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
(Add responseContent value here)
```
**action=LOCATION** indicates that your authorization server is expected to make a "302 Found" response to Web browser. The response will be like this:
```
HTTP/1.1 302 Found
Location: (Add responseContent value here)
Cache-Control: no-store
Pragma: no-cache
```
# Implementing an Authorization Endpoint with Authlete
Source: https://developers.authlete.com/configuration-reference/endpoints/implementing-an-authorization-endpoint-with-authlete
Authlete makes it easy to implement OAuth 2.0 and OpenID Connect in your authorization endpoint.
Authlete implements the OAuth 2.0 and OpenID Connect specifications; all your authorization endpoint implementation needs to do is pass client requests to the Authlete API and take the actions indicated in the Authlete API response.
There are three entities involved in the authorization process:
**Client**
A client application that makes an authorization request.
**Service**
Your implementation of an authorization endpoint.
**Authlete**
The Web APIs that you use to implement your authorization endpoint.
## Interactive Authorization Flow
The figure below shows the most typical data flow. An HTML page is displayed to an end-user and they decide to either authorize the client application to access their resources or deny the authorization request.
1. The client application sends an authorization request to the service's authorization endpoint.
2. The authorization endpoint implementation passes the authorization request parameters to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API.
3. Authlete parses the request parameters, computes request information (such as client information, requested scopes, etc.) that the service will need in order to build the UI, and returns it to the service.
4. The service renders the UI to authenticate the end-user and let the end-user authorize the client application.
5. The end-user logs in to the service and decides whether to allow or deny the authorization request.
6. The service receives the decision from the end-user and calls either the [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) or [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) API accordingly.
7. Authlete generates a response and returns it to the service. The response may contain an authorization code, an access token and/or an ID token, or an error code, depending on the original authorization request from the client application.
8. The service returns the final response to the client application. In a typical case, the HTTP status of the response is `302 Found` and a `Location` header containing the client's redirect URI with query parameters.
## Non-Interactive Authorization Flow
[OpenID Connect](https://openid.net/connect/) introduced a `prompt` request parameter. When its value is `none`, the authorization request is processed without user interaction. The figure below illustrates the flow.
1. The client application sends an authorization request with `prompt=none` to the service's authorization endpoint.
2. The authorization endpoint implementation passes the authorization request parameters to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API.
3. Authlete parses the request parameters, computes request information (such as client information, requested scopes, etc.) that the service will need in order to build the UI, and returns it to the service.
4. The service verifies that the authorization request satisfies necessary conditions.
5. The service calls either the [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) or the [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) API.
6. Authlete generates a response and returns it to the service. The response may contain an authorization code, an access token and/or an ID token, or an error code, depending on the original authorization request from the client application.
7. The service returns the final response to the client application. In a typical case, the HTTP status of the response is `302 Found` and a `Location` header containing the client's redirect URI with query parameters.
## Handling Bad Requests
When an authorization request from a client application is invalid, Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API generates a response containing an error report which the service can return to the client. In this case, the service does not have to call the [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) or [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) API. The figure below illustrates the flow.
1. The client application sends an authorization request with `prompt=none` to the authorization endpoint of the service.
2. The authorization endpoint implementation passes the authorization request parameters to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API.
3. Authlete parses the request parameters, detects the error, builds an error report and returns it to the service.
4. The service returns the final response containing the error report to the client application. In a typical case, the HTTP status of the response is `302 Found` and `Location` header contains the client's redirect URI with the error report. However, if the error was detected before the client's redirect URI was determined, the service should return `400 Bad Request` to the client application.
# Introspection Response for Expired Access Token
Source: https://developers.authlete.com/configuration-reference/endpoints/introspection-response-for-expired-access-token
Details on how Authlete handles requests with expired access tokens in its Introspection API and the resulting UNAUTHORIZED response.
When an resource server makes a request to Authlete's [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request)
API, and the request includes an expired access token, Authlete works as follows:
* To the first request: Authlete determines the token has been expired and then removes the token from its database.
* To the second and subsequent requests: Authlete determines the token doesn't exist. Because the token has been removed at the first request.
In either case, a value of "action" in a response from the API would be "UNAUTHORIZED".
See also:
* [JavaDoc for Class IntrospectionResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/IntrospectionResponse.html)
# JWT Response for OAuth Token Introspection
Source: https://developers.authlete.com/configuration-reference/endpoints/jwt-response-for-oauth-token-introspection
Technical information on configuring services and resource servers to enable the JWT Response for OAuth Token Introspection feature.
## Overview
This provides describes overview of [**JWT Response for OAuth Token Introspection**](https://www.ietf.org/id/draft-ietf-oauth-jwt-introspection-response-12.html)
and instructions on how to configure services and resource servers to enable the feature.
> **JWT Response for OAuth Token Introspection** feature has been available since Authlete 3.0.
## Introduction
Below is a diagram that roughly illustrates token introspection flow in JWT Response for OAuth Token Introspection.
*Token Introspection Flow in JWT Response for OAuth Token Introspection*
### 1. Introspection Request
First, the resource server sends an introspection request to the authorization server including an HTTP **Accept** header set to "**application/token-introspection+jwt**". The header value indicates that the resource server is requesting a JWT introspection response from the authorization server.
### 2. Authenticate Resource Server
Once the introspection request is accepted, the authorization server must authenticate the resource server. If the authentication succeeds, the authorization server then retrieves the necessary information about the resource server (e.g. the resource server's URI, the sign algorithm for JWT introspection responses, etc), which is required for signing/encrypting introspection responses.
### 3. Request to Authlete /auth/introspection/standard API
Then, the authorization server calls Authlete **/auth/introspection/standard** API including the resource server's information obtained in step 2.
### 4. Response from Authlete /auth/introspection/standard API
If the request is valid, Authlete returns an API response including a JWT introspection response. The authorization server is then expected to return this response to the resource server.
### 5. JWT Introspection Response
After receiving a successful API response from Authlete, the authorization server returns the JWT introspection response, contained in the Authlete API response, back to the resource server.\
Service configuration
Configure your service (authorization server) as below.
| Property | Description |
| - | - |
| JWK Set | NOTE: This configuration is only required if "JWK Set" is used.
Set a JWK set containing at least one sign key for JWT introspection responses. |
| JWK Set Endpoint URI | NOTE: This configuration is only required if "JWK Set Endpoint URI" is used.
Set a URI that starts with **https**. The URI needs to point to a JWK set containing at least one sign key for JWT introspection responses. |
## Resource server information
Below is information about your resource server that your authorization server needs to pass to Authlete at runtime after authenticating it.
| Property | Description |
| - | - |
| resource server URI | Required. The URI of the resource server making the introspection request. |
| JWS alg algorithm | Optional. The JWS alg algorithm for signing the introspection response. This parameter corresponds to introspection\_signed\_response\_alg defined in "[6. Client Metadata](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-introspection-response#section-6)" of "[JWT Response for OAuth Token Introspection](https://www.ietf.org/id/draft-ietf-oauth-jwt-introspection-response-12.html)".
The default value is RS256. |
| JWE alg algorithm | Optional. The JWE alg algorithm for encrypting the introspection response. This parameter corresponds to introspection\_encrypted\_response\_alg defined in "[6. Client Metadata](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-introspection-response#section-6)" of "[JWT Response for OAuth Token Introspection](https://www.ietf.org/id/draft-ietf-oauth-jwt-introspection-response-12.html)".
If the this property is specified, Authlete generates a JWT introspection response encrypted with the algorithm specified by this property and the algorithm specified by the JWE enc algorithm. |
| JWE enc algorithm | Optional. The JWE enc algorithm for encrypting the introspection response. This parameter corresponds to introspection\_encrypted\_response\_enc defined in "[6. Client Metadata](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-introspection-response#section-6)" of "[JWT Response for OAuth Token Introspection](https://www.ietf.org/id/draft-ietf-oauth-jwt-introspection-response-12.html)".
The default is A128CBC\_HS256. |
## API call test
In this section, we simulate API calls that the authorization server sends to Authlete on access token introspection.
Suppose the resource server sends an introspection request to the authorization server, requesting a JWT introspection response. The request would look as follows.
```
POST /introspect HTTP/1.1
Host: as.example.com
Authorization: Basic cnMwOnJzMC1zZWNyZXQ=
Accept: application/token-introspection+jwt
Content-Type: application/x-www-form-urlencoded
token=MaSp49VXQnAmg_Si2i02PsvKlTSHMuxp3zJH00OA5mc
```
After receiving the introspection request, the authorization server must authenticate the resource server somehow and get the resource server's information. Here, we also assume that the authorization server has successfully authenticated the resource server and obtained "**[https://rs.example.com](https://rs.example.com)**" as the resource server URI and "**ES256**" as the JWS alg algorithm for introspection responses.
Then, the authorization server sends a request to Authlete "**/auth/introspection/standard**" API including the resource server's information. The following curl command is an example of an API call to Authlete "**/auth/introspection/standard**" API.\
NOTE: We also need to send the value of the HTTP Accept Header in the original introspection request.
```
curl -v -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection/standard" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H "Content-Type:application/json" \
-d '{
"parameters": "token=MaSp49VXQnAmg_Si2i02PsvKlTSHMuxp3zJH00OA5mc",
"httpAcceptHeader": "application/token-introspection+jwt",
"rsUri": "https://rs.example.com",
"introspectionSignAlg": "ES256"
}'
```
If the request has been successfully processed at Authlete, the authorization server gets a response as shown below.
```
{
"resultCode": "A145001",
"resultMessage": "[A145001] Introspection was performed successfully (type=access_token, active=true).",
"action": "JWT",
"responseContent": "eyJhbGciOiJFUzI1NiIsInR5cCI6InRva2VuLWludHJvc3BlY3Rpb24rand0In0.eyJpc3MiOiJodHRwczovL2F1dGhsZXRlLmNvbSIsImF1ZCI6Imh0dHBzOi8vcnMuZXhhbXBsZS5jb20iLCJpYXQiOjE3MDAxMjgzMTYsInRva2Vu_X2ludHJvc3BlY3Rpb24iOiJ7XCJhY3RpdmVcIjp0cnVlLFwic2NvcGVcIjpcImVtYWlsXCIsXCJjbGllbnRfaWRcIjpcIjM2Mzk1NzE4NjNcIixcInRva2Vu_X3R5cGVcIjpcIkJlYXJlclwiLFwiZXhwXCI6MTcwMTEyODI5NixcInN1YlwiOlwiam9oblwiLFwiYXVkXCI6W1wiaHR0cHM6Ly9ycy5leGFtcGxlLmNvbVwiXSxcImlzc1wiOlwiaHR0cHM6Ly9hdXRobGV0ZS5jb21cIn0ifQ.l_BZfoqEQsX3CSC2OOpdIt1vOUT5vcZb9kTL2H6EW9latqqtrNi9ZjiXZ7-Jbfsdv388el9oFAPszKfFMMR9LQ"
}
```
The "**responseContent**" property in the response contains a JWT, which is to be returned to the resource server as an introspection response. The payload of the JWT is as follows.
```
{
"iss": "https://authlete.com",
"aud": "https://rs.example.com",
"iat": 1700128316,
"token_introspection": "{\"active\":true,\"scope\":\"email\",\"client_id\":\"3639571863\",
\"token_type\":\"Bearer\",\"exp\":1701128296,\"sub\":\"john\",\"aud\":[\"https://rs.example.com\"],
\"iss\":\"https://authlete.com\"}"
}
```
# JWT Secured Authorization Requests (JAR)
Source: https://developers.authlete.com/configuration-reference/endpoints/jwt-secured-authorization-requests-jar
Technical information about using JWT Secured Authorization Requests (JAR) in OAuth 2 deployments for increased security and validation of authorization requests.
## Introduction
Between the security concerns often raised to OAuth 2 deployments are the exposure of authorization requests parameters and how they can be easily tampered. This was addressed by OpenID Connect protocol by introducing [Requests Objects](https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests)
on authorization request, but it was restricted to OpenID Foundation.
With the [JWT Secured Authorization Requests](https://www.rfc-editor.org/rfc/rfc9101.html)
(or JAR for short), the IETF brings the option for serializing the authorization request in a JWT format. The specifications are very close but there are some differences on how to process JAR and OIDC Requests Object in the context on the authorization requests. If you are looking for the OIDC Request Object, check [Using Request Objects](/configuration-reference/endpoints/using-request-objects)
note.
Using a signed JWT structure for the authorization request, the server can validate if the request was tainted and the source of the request can be authenticated. Encryption is also available, allowing the authorization requests to be transported publicly with the proper secrecy required by the use case.
This article describes how AS and clients can leverage JAR support on Authlete.
## Authlete support for JAR
The JAR related configuration can be done in two levels on: Service and Client based.
## Configuring JAR configuration on service level
The administrator can define the security requirements to JAR and its processing across the clients. You can find the JAR related parameters grouped under "Request Object" section on Authorization tab of the Service Owner console.
The most impactful parameter is the "Request Object Processing", as its background is the conflict between OpenID specification and JAR. This parameter changes how the authorization requests parameters are processed when they are duplicated in the request URI and JWT are processed. For a more detailed description of the differences, check the article [Implementer’s note about JAR](https://darutk.medium.com/implementers-note-about-jar-fff4cbd158fe)
under section "Conflicts".
When running with "JAR compatible" selected, the parameters on the request URI are ignored. When running on "Backward compatible" the parameters are merged. If you are configuring Authlete service for FAPI, check the note Configuring Authlete for FAPI compliance.
| Parameter | Description |
| - | - |
| **Request Object** | if **Mandatory**, every authorization request, including pushed authorizations, is required to be in JWT format. If **Optional**, the administrator can require JAR for individual clients. |
| **Request Object Processing** | check description above |
| **nbf Claim** | if **Required,** the JWT must contain a "Not Before" claim, this will force the requests to be time boxed. This reduce the attack vectors on AS. |
| **Encryption In Front Channel** | If \*\*Required, \*\*the JWT send to /api/auth/authorization endpoint are required to be encrypted. In other words, if the authorization request is not pushed, it will be required to be encrypted to ensure that authorization request is not exposed on user agent.If **Optional**, the administrator can require JAR for individual clients. |
| **Encryption Algorithm Match** | when **Required** the encryption algorithm of key transport (the Content Encryption Key - CEK) used on JWT must match the client level config. If **Optional**, the administrator can require JAR for individual clients. |
| **Encryption Encoding Algorithm Match** | when **Required** the encryption algorithm of the JWT must match the algorithm defined on the client level. If **Optional**, the administrator can require JAR for individual clients. |
*JAR related configuration on Authlete Service Owner console*
## Configuring JAR support on client level
There are 2 configuration portions per client that are available to administrators on Developers Console.
### Setup required keys
For the usage of JAR requests, the client must be configured with public keys using the JWK format. This is available under JWK Set tab for the specific client.
Authlete allows the JWK Set to be assigned directly to the client, using the JWK Set Content attribute on the image below, or a URL pointing to a public JWK endpoint containing the keys used to sign and optionally encrypt the JAR.
*JWK Set configuration per client on Developer console*
### JAR requirements on client level
Authlete allow the administrator to tight the security for specific client by requiring the usage of JAR, encryption of it and the usage of specific algorithms during encryption. The opposite direction is not available: For instance if the service is configure to require JAR, every client will be required, regardless if the client is configured for Optional.
| | Description |
| - | - |
| **Request Object** | if **Mandatory**, every authorization request of this client, including pushed authorizations, is required to be in JWT format. Considered only if the service is configured for **Optional**. |
| **Request Object Signature Algorithm** | The algorithm to be used by the client to sign its Request Objects. if not defined, the client can use any algorithm supported by Authlete.The algorithm selected here must have a key for the same algorithm under JWK Set Content or JWK Set URI |
| **Request Object Encryption Algorithm** | The encryption algorithm for key transport used on JWT. As the Signature, a public key needs to be available under JWK Set Content for the selected algorithm.For instance, if RSA-OAEP is selected, a key with the same alg must be available on JWK Set. |
| **Request Object Encryption Encoding Algorithm** | The encryption algorithm for the JWT content. |
| **Request URIs** | List of preregistered request\_uri's that this client can use for authorization. The intent here is to allow authorization requests to be created, signed and encrypted before hand. |
| **Encryption In Front Channel** | If **Required** the JWT send to /api/auth/authorization endpoint are required to be encrypted. In other words, if the authorization request is not pushed, it will be required to be encrypted. This will ensure that authorization request is not exposed to user agent.Considered only if the service is configured for **Optional**. |
| **Encryption Algorithm Match** | when **Required** the encryption algorithm of key transport used on JWT must match the client level config. Considered only if the service is configured for **Optional**. |
| **Encryption Encoding Algorithm Match** | when **Required** the encryption algorithm of the JWT must match the algorithm defined on the client level. Considered only if the service is configured for **Optional**. |
*JAR configuration for individual clients available via the Authlete Management Console*
## JWT Authorization Request structure
The structure of the JWT containing the authorization request is a direct map from uri parameters do JWT claims like below:
| Authorization Request (URI) | JAR equivalent |
| - | - |
| response\_type=code \&client\_id=3280859750204 \&redirect\_uri=https%3A%2F%2Fmobile.example.com%2Fcb \&code\_challenge=W78h...uAFaT4 & code\_challenge\_method=S256 | `{"iss":"3280859750204","response_type":"code","client_id":"3280859750204","redirect_uri":"https://mobile.example.com/cb","code_challenge":"W78h...uAFaT4","code_challenge_method":"S256","aud":"https://as.example.com/"}` |
The "iss" and "aud" claims are mapped from the client\_id and the issuer configured on Authlete service respectively.
For the signing and encryption, check the libraries available to your env and language.
## Signed JAR
Signed JAR is the most basic form, as JAR specification requires that JWT to be signed. The clients have available multiple libraries that support [JWT signing as per specification](https://www.rfc-editor.org/rfc/rfc7515.txt).
Regardless of submitting the JAR via URI on authorization request or via body of PAR authorization, the structure of the JWT and its content is the same.
For instance:
| Authorization Request | JAR content |
| - | - |
| response\_type=code \&client\_id=3991284690217797 \&redirect\_uri=[https://mobile.example.com/cb](https://mobile.example.com/cb) \&scope=openid \&state=af0ifjsldkj \&code\_challenge=2vChdex6dr...VAB0 \&code\_challenge\_method=S256 | "iss": "3991284690217797", "aud": "[https://authlete.com](https://authlete.com)", "response\_type": "code", "client\_id": "3991284690217797", "redirect\_uri": "[https://mobile.example.com/cb](https://mobile.example.com/cb)", "scope": "openid", "state": "af0ifjsldkj", "code\_challenge": "2vChdex6dr...VAB0", "code\_challenge\_method": "S256", "iat": 1628124927, "nbf": 1628124927, "exp": 1628125532, "jti": "A\_erxcM69yNFZ22jqQXm2" |
That JWT content when signed using RSA256 and a random key created using [mkjwk](https://mkjwk.org/) will be as below
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.
eyJpc3MiOiIzOTkxMjg0NjkwMjE3Nzk3IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb
20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjM5OTEyODQ2OTAyMT
c3OTciLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiI
sInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxl
bmdlIjoiMnZDaGRleDZkcmRtZnpKSmFDVXBiSHpDVl9Hb3dvTXlQeXJrRUNYVkFCMCIsI
mNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJpYXQiOjE2MjgxMjQ5MjcsIm5iZi
I6MTYyODEyNDkyNywiZXhwIjoxNjI4MTI1NTMyLCJqdGkiOiJBX2VyeGNNNjl5TkZaMjJ
qcVFYbTIifQ.IRvOu10QHcvi1BgYCS43N7034KxZOMBRL07HupKn4d5cvkah-tnNr8X8b
mgE2SKSAGmK2jp8az7bgKb4g2bBxHd1Crrehxg9tvHswjQCdO7G--MP2M8ey55u5iL-76
S8HU_QiNDXHjuWAmS0godCD2XHxmjUiXjYX2H-cK8vEJy1Pl6-up_ugdJBPYMOtB3AX3w
WYjHGBlabB6S1lUkmksVtJ473zzExW97RVJuU2uFFHdi0XkKFgRRlvqGeZOA8VuKM4te3
qn-XOdV_HCGRPFuPLN1HK5CtM-Rd-iOxjwh3r9xF6cuXPhtxNn_LY7mkK-AEWrqW2jLqR
xZJRxgiXA
```
The examples below shows the same JWT sent directly to authorization endpoint and pushed to par endpoint. Both examples shows the same request with different security properties.
## Authorization request
The sample below shows the request the authorization server can make to Authlete authorization endpoint to submit the JAR request above as request uri, or *front-channel*. Be aware that any other parameter on the parameter content will be ignored as per JAR specification requirement. The `parameters` value below packs the `client_id` and `request` (the JAR JWT shown above) query parameters into a single string.
```shell highlight={7-21} theme={null}
curl --request POST \
"https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data '{
"parameters":
"client_id=3991284690217797&
request=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.
eyJpc3MiOiIzOTkxMjg0NjkwMjE3Nzk3IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb
20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjM5OTEyODQ2OTAyMT
c3OTciLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiI
sInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxl
bmdlIjoiMnZDaGRleDZkcmRtZnpKSmFDVXBiSHpDVl9Hb3dvTXlQeXJrRUNYVkFCMCIsI
mNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJpYXQiOjE2MjgxMjQ5MjcsIm5iZi
I6MTYyODEyNDkyNywiZXhwIjoxNjI4MTI1NTMyLCJqdGkiOiJBX2VyeGNNNjl5TkZaMjJ
qcVFYbTIifQ.IRvOu10QHcvi1BgYCS43N7034KxZOMBRL07HupKn4d5cvkah-tnNr8X8b
mgE2SKSAGmK2jp8az7bgKb4g2bBxHd1Crrehxg9tvHswjQCdO7G--MP2M8ey55u5iL-76
S8HU_QiNDXHjuWAmS0godCD2XHxmjUiXjYX2H-cK8vEJy1Pl6-up_ugdJBPYMOtB3AX3w
WYjHGBlabB6S1lUkmksVtJ473zzExW97RVJuU2uFFHdi0XkKFgRRlvqGeZOA8VuKM4te3
qn-XOdV_HCGRPFuPLN1HK5CtM-Rd-iOxjwh3r9xF6cuXPhtxNn_LY7mkK-AEWrqW2jLqR
xZJRxgiXA" }'
```
Authlete authorization endpoint will be process the JAR request, validate it and having a valid authorization request, the response will resemble the structure below. It seems like a regular authorization response (with the action and ticket attributes), but also includes the *requestObjectPayload*, which is the JSON content of the JAR request.\
if the authorization server is required to reason about the authorization request and further process it before proceeding. the object payload can be parsed and processed accordingly.
```json highlight={4,6,8} theme={null}
{
"resultCode": "A004001",
"resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 943876981845) for the authorization request from the client (ID = 3991284690217797). [response_type=code, openid=true]",
"action": "INTERACTION",
...
"requestObjectPayload": "{\"iss\":\"3991284690217797\",\"aud\":\"https://authlete.com\",\"response_type\":\"code\",\"client_id\":\"3991284690217797\",\"redirect_uri\":\"https://mobile.example.com/cb\",\"scope\":\"openid\",\"state\":\"af0ifjsldkj\",\"code_challenge\":\"0e7Vv0nQLZhCBjJdl7lLa3I4_Qp3xX00dLRMWnq-rh8\",\"code_challenge_method\":\"S256\",\"iat\":1628124632,\"nbf\":1628124632,\"exp\":1628125237,\"jti\":\"KOQrfnS3vsZdIZo-lNYaf\"}",
...
"ticket": "fphPe4GEmYVlrKv6IzCwsPoUQyn9ooE1Yk7ctrTMp-E"
}
```
## Pushed Authorization Request
The exactly same JAR can be pushed to authorization server and /api/pushed\_auth\_req counterpart is available for you. The PAR request to Authlete from authorization server will take the form as below, again packing the `client_id` and `request` query parameters into the `parameters` value
```shell highlight={7-21} theme={null}
curl --request POST \
"https://us.authlete.com/api/${SERVICE_API_KEY}/pushed_auth_req" \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--header 'Content-Type: application/json' \
--data '{
"parameters": "client_id=3991284690217797
&request=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.
eyJpc3MiOiIzOTkxMjg0NjkwMjE3Nzk3IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb
20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjM5OTEyODQ2OTAyMT
c3OTciLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiI
sInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxl
bmdlIjoiMnZDaGRleDZkcmRtZnpKSmFDVXBiSHpDVl9GdG9dp0_sh7N1QJcE0xBWVQ0ZG
JlZmdGY2xlaG5mRzNWJHJt6YyG0FDI2rDBoDESdiaRN0bnh_jyaDNrhKLaTBU6TDBv0Q9
LkN-KNHoXY-ynQxcnW0_jrTq7VQZWfDqV0GRkFBPC093cVpDaU5uQl9jS0RWbuyLCJpYXQ
iOjE2MjgxMjQ5MjcsIm5iZiI6MTYyODEyNDkyNywiZXhwIjoxNjI4MTI1NTMyLCJqdGkiO
iJBX2VyeGNNNjl5TkZaMjJqcVFYbTIifQ.IRvOu10QHcvi1BgYCS43N7034KxZOMBRL07Hu
pKn4d5cvkah-tnNr8X8bmgE2SKSAGmK2jp8az7bgKb4g2bBxHd1Crrehxg9tvHswjQCdO7G
--MP2M8ey55u5iL-76S8HU_QiNDXHjuWAmS0godCD2XHxmjUiXjYX2H-cK8vEJy1Pl6-up
_ugdJBPYMOtB3AX3wWYjHGBlabB6S1lUkmksVtJ473zzExW97RVJuU2uFFHdi0XkKFgRRlv
qGeZOA8VuKM4te3qn-XOdV_HCGRPFuPLN1HK5CtM-Rd-iOxjwh3r9xF6cuXPhtxNn_LY7mk
K-AEWrqW2jLqRxZJRxgiXA",
"clientId": "3991284690217797"
}'
```
Authlete will process the JAR, provision a request\_uri as a regular PAR (check the note [Pushed Authorization Request](/configuration-reference/endpoints/pushed-authorization-requests-par)
) and return the responseContent for the authorization server so that it can be respond to the client accordingly.
```json highlight={4,6} theme={null}
{
"resultCode": "A245001",
"resultMessage": "[A245001] Successfully registered a request object for client (3991284690217797), URI is urn:ietf:params:oauth:request_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA.",
"action": "CREATED",
"requestUri": "urn:ietf:params:oauth:request_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA",
"responseContent": "{\"expires_in\":600,\"request_uri\":\"urn:ietf:params:oauth:request_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA\"}"
}
```
When the client invokes the authorization using the *request\_uri*, the authorization kicks in and Authlete will process it, creating the ticket and returning the action to be taken by the authorization server, as a regular [PAR request](/configuration-reference/endpoints/pushed-authorization-requests-par)
.
```shell highlight={7} theme={null}
curl --request POST \
"https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data-raw '{
"parameters": "client_id=3991284690217797&
request_uri=urn:ietf:params:oauth:request_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA"
}'
```
As the authorization request response shown on the previous section, the content of the JWT request, is returned to the authorization server for further inspection and processing.
```json highlight={4,6,8} theme={null}
{
"resultCode": "A004001",
"resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 943876981845) for the authorization request from the client (ID = 3991284690217797). [response_type=code, openid=true]",
"action": "INTERACTION",
....
"requestObjectPayload": "{\"iss\":\"3991284690217797\",\"aud\":\"https://authlete.com\",\"response_type\":\"code\",\"client_id\":\"3991284690217797\",\"redirect_uri\":\"https://mobile.example.com/cb\",\"scope\":\"openid\",\"state\":\"af0ifjsldkj\",\"code_challenge\":\"0e7Vv0nQLZhCBjJdl7lLa3I4_Qp3xX00dLRMWnq-rh8\",\"code_challenge_method\":\"S256\",\"iat\":1628124632,\"nbf\":1628124632,\"exp\":1628125237,\"jti\":\"KOQrfnS3vsZdIZo-lNYaf\"}",
...
"ticket": "ntv32-wAkXNSNP9f-11NCHaTQ9RyBSq_xbKoyjr0Tvk"
}
```
# Protected Resource
Source: https://developers.authlete.com/configuration-reference/endpoints/protected-resource
This document describes how to protect your Web APIs by OAuth access tokens.
# 1. Target Readers
The main purpose to implement [OAuth 2.0](http://tools.ietf.org/html/rfc6749) is to protect Web APIs by [access tokens](http://tools.ietf.org/html/rfc6749#section-5). Therefore, all of you will finally need to read this document.
What people want to do is to protect their Web APIs by access tokens, but they will soon notice that they have to implement [RFC 6749 (OAuth 2.0)](http://tools.ietf.org/html/rfc6749) as a prerequisite. Authlete exists to mitigate the burden to implement the specification and to enable you to focus on implementing your Web APIs.
# 2. What is "Protected Resource Endpoint"?
Web APIs protected by OAuth 2.0 access tokens are called **protected resource endpoints**. Such endpoints do the following before they return requested data to client applications.
1. Extract an access token from a request.
2. Get detailed information about the access token from the authorization server.
3. Validate the access token.
The following sections describe how to do these.
# 3. Extract Access Token
How should a protected resource endpoint accept an access token from a client application? It is possible to devise as many new ways to do it as you like. However, we don't have to reinvent the wheel. [RFC 6750](http://tools.ietf.org/html/rfc6750) (Bearer Token Usage) has already defined three ways to accept an access token as listed below.
1. Via `Authorization` header. ([2.1. Authorization Request Header Field](https://tools.ietf.org/html/rfc6750#section-2.1))
2. Via a form parameter `access_token`. ([2.2. Form-Encoded Body Parameter](https://tools.ietf.org/html/rfc6750#section-2.2))
3. Via a query parameter `access_token`. ([2.3. URI Query Parameter](https://tools.ietf.org/html/rfc6750#section-2.3))
The most desirable way is the first one and [RFC 6750](http://tools.ietf.org/html/rfc6750) says *"Resource servers MUST support this method".* Simply saying, when a client application uses the first way, a protected resource endpoint can find an HTTP header like below in the request.
```
Authorization: Bearer access-token
```
Below are examples to extract an access token.
```java Java (JAX-RS) theme={null}
private static final Pattern CHALLENGE_PATTERN
= Pattern.compile("^Bearer *([^ ]+) *$", Pattern.CASE_INSENSITIVE);
/**
* Entry point for GET method.
*/
@GET
public Response get(
@HeaderParam(HttpHeaders.AUTHORIZATION) String authorization,
@QueryParam("access_token") String accessToken)
{
// Extract an access token from Authorization header or use
// the value of query parameter 'access_token'.
accessToken = extractAccessToken(authorization, accessToken);
}
/**
* Entry point for POST method.
*/
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response post(
@HeaderParam(HttpHeaders.AUTHORIZATION) String authorization,
@FormParam("access_token") String accessToken)
{
// Extract an access token from Authorization header or use
// the value of form parameter 'access_token'.
accessToken = extractAccessToken(authorization, accessToken);
}
/**
* Extract an access token from Authorization header, or return
* the value of 'access_token' request parameter.
*/
private static String extractAccessToken(
String authorization, String accessToken)
{
// If Authorization header is not found in the request.
if (authorization == null)
{
// Return the value of 'access_token' request parameter.
return accessToken;
}
// Pattern matching on the value of Authorization header.
Matcher matcher = CHALLENGE_PATTERN.matcher(authorization);
// If the value of Authorization header is in the format of
// 'Bearer access-token'.
if (matcher.matches())
{
// Return the value extracted from Authorization header.
return matcher.group(1);
}
else
{
// Return the value of 'access_token' request parameter.
return accessToken;
}
}
```
```ruby Ruby (Sinatra) theme={null}
# Method to extract an access token from a request.
def extract_access_token(request)
# The value of Authorization header.
header = request.env["HTTP_AUTHORIZATION"]
# If the value is in the format of 'Bearer access-token'.
if /^Bearer[ ]+(.+)/i =~ header
# Return the value extracted from Authorization header.
return $1
end
# Return the value of 'access_token' request parameter.
return request["access_token"]
end
# Entry point for GET method.
get '/hello' do
# Extract an access token from the request.
access_token = extract_access_token(request)
end
# Entry point for POST method.
post '/hello' do
# Extract an access token from the request.
access_token = extract_access_token(request)
end
```
```php PHP theme={null}
/**
* Function to extract an access token from a request.
*/
function extract_access_token()
{
// The value of Authorization header.
$header = $_SERVER['HTTP_AUTHORIZATION'];
// If the value is in the format of 'Bearer access-token'.
if ($header != null && preg_match('/^Bearer[ ]+(.+)/i', $header, $captured))
{
// Return the value extracted from Authorization header.
return $captured;
}
if ($_SERVER['REQUEST_METHOD'] == 'GET')
{
// Return the value of 'access_token' query parameter.
return $_GET['access_token'];
}
else
{
// Return the value of 'access_token' form parameter.
return $_POST['access_token'];
}
}
```
# 4. Introspect Access Token
## RFC 7662
The process to get detailed information about an access token is called **introspection**. How to introspect an access token depends on each implementation of OAuth 2.0 authorization servers.
However, in Oct. 2015, [RFC 7662](http://tools.ietf.org/html/rfc7662) (OAuth 2.0 Token Introspection) was released as a standard specification for introspection. If an authorization server you are using supports RFC 7662, your protected resource endpoints can introspect access tokens without depending on a specific authorization server implementation. But still, how to protect the introspection endpoint itself depends on each implementation as mentioned in "[2.1. Introspection Request](https://tools.ietf.org/html/rfc7662#section-2.1)" of "[RFC 7662](http://tools.ietf.org/html/rfc7662)".
From RFC 7662, 2.1. Introspection Request;
> To prevent token scanning attacks, the endpoint MUST also require some form of authorization to access this endpoint, such as client authentication as described in OAuth 2.0 \[RFC 6749] or a separate OAuth 2.0 access token such as the bearer token described in OAuth 2.0 Bearer Token Usage \[RFC 6750]. The methods of managing and validating these authentication credentials are out of scope of this specification.
## Authlete Introspection API
Authlete provides an introspection API at `/auth/introspection`. The implementation of the API does not comply with RFC 7662, but from a viewpoint of those who develop protected resource endpoints, our API is better than RFC 7662 as listed below.
1. You can delegate validation to our introspection API.
2. Our introspection API generates an error message that complies with [RFC 6750](http://tools.ietf.org/html/rfc6750) when a given access token is invalid.
Simply saying, Authlete's introspection API does **introspection + validation + building an error message complying with RFC 6750**. You can find the detailed specification of Authlete's introspection API in [Authlete Web APIs document](http://docs.authlete.com/#introspection).
# 5. Validate Access Token
The next step after getting detailed information about an access token is to validate the access token. Validation involves the following steps.
1. Has it expired or not?
2. Has it been revoked or not?
3. Does it cover necessary permissions?
4. Is it associated with the expected end-user?
As mentioned, you can delegate validation to Authlete's introspection API if you want. `scopes` and `subject` are optional parameters for the introspection API to specify the scopes (= permissions) that your protected resource endpoint requires and the subject (= unique identifier) of an end-user that your protected resource endpoint expects.
If you utilize `scopes` parameter and `subject` parameter accordingly based on your needs, you don't have to write separate validation code.
# 6. Error Response
If you want to comply with [RFC 6750](http://tools.ietf.org/html/rfc6750), responses from your protected resource endpoints must contain WWW-Authenticate header on errors. The detailed format of the value of the header is described in "[3. The WWW-Authenticate Response Header Field](https://tools.ietf.org/html/rfc6750#section-3)". The format is not so simple as you may guess. Below is an example with parameters mentioned in [RFC 6750](http://tools.ietf.org/html/rfc6750) (some are optional, though).
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
scope="read write",
error="invalid_token",
error_description="The access token expired",
error_uri="http://example.com/error/123"
```
You might be seduced into ignoring this specification because you may doubt it's worth making an effort just to tell error details to client applications. However, you don't have to make a compromise if you use Authlete's introspection API. It's because a response from the API contains `responseContent` property whose value can be used as the value of `WWW-Authenticate` header. As a result, you can easily build an error response like below.
```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Just embed responseContent here.
```
# 7. Dispatch According to Validation Result
If an error is detected on validation, a protected resource endpoint should return `400 Bad Request`, `401 Unauthorized` or `403 Forbidden` (or `500 Internal Server Error`) based on the cause of the error.
On the other hand, when an access token is valid, a protected resource endpoint can generate any response it likes. In a typical case, it will return the requested resource in JSON format with `200 OK` to the client application.
To make it easy to dispatch based on validation result, a response from Authlete's introspection API contains `action` property. It is a string that represents the next `action` you should take. Values of action and their meanings are as follows.
| Value | Meaning |
| - | - |
| `INTERNAL_SERVER_ERROR` | An error occurred in Authlete. 500 Internal Server Error should be returned to the client application. |
| `BAD_REQUEST` | The request from the client application does not contain an access token. 400 Bad Request should be returned to the client application. |
| `UNAUTHORIZED` | The presented access token does not exist or has expired. 401 Unauthorized should be returned to the client application. |
| `FORBIDDEN` | The access token does not cover the required scopes or the subject associated with the access token is different from the expected one. 403 Forbidden should be returned to the client application. |
| `OK` | The access token is valid. |
Below are example implementations to dispatch according to validation result. Each method/function implementation (1) accepts an access token \[required], a string array of scopes \[optional] and a subject \[optional], (2) calls the introspection API, and (3) dispatches the flow according to the value of `action` property in the response from the introspection API. When `action` is not `OK`, `responseContent` property in the response from the introspection API is used as the value of `WWW-Authenticate` header in the response to a client application.
```java Java (JAX-RS) theme={null}
/**
* Call Authlete's /api/auth/introspection API.
* A response from the API is returned when the
* access token is valid. Otherwise, a
* WebApplicationException is raised.
*/
private static IntrospectionResponse doIntrospection(
String accessToken, String[] scopes, String subject)
{
// Call Authlete's /api/auth/introspection API.
IntrospectionResponse response = callIntrospectionApi(accessToken, scopes, subject);
// The value of 'WWW-Authenticate' header on error.
String content = response.getResponseContent();
int status;
// Check the next action.
switch (response.getAction())
{
case INTERNAL_SERVER_ERROR:
// 500 Internal Server Error.
// The API request from this implementation was wrong
// or an error occurred in Authlete.
status = 500;
break;
case BAD_REQUEST:
// 400 Bad Request.
// The request from the client application does not
// contain an access token.
status = 400;
break;
case UNAUTHORIZED:
// 401 Unauthorized.
// The presented access token does not exist or has expired.
status = 401;
break;
case FORBIDDEN:
// 403 Forbidden.
// The access token does not cover the required scopes
// or the subject associated with the access token is
// different.
status = 403;
break;
case OK:
// The access token is valid (= exists and has not expired).
return response;
default:
// This never happens.
throw new WebApplicationException("Unknown action", 500);
}
Response res = Response
.status(status)
.header(HttpHeaders.WWW_AUTHENTICATE, content)
.build();
throw new WebApplicationException(res);
}
```
```ruby Ruby (Sinatra) theme={null}
#--------------------------------------------------
# Call Authlete's /api/auth/introspection API.
# A response from the API is returned when the
# access token is valid. Otherwise, a WebException
# is raised.
#--------------------------------------------------
def do_introspection(token, scopes, subject)
# Call Authlete's /api/auth/introspection API.
response = call_introspection_api(token, scopes, subject)
# The value of 'WWW-Authenticate' header on error.
content = response["responseContent"]
# "action" denotes the next action.
case response["action"]
when "INTERNAL_SERVER_ERROR"
# 500 Internal Server Error
# The API request from this implementation was wrong
# or an error occurred in Authlete.
raise WebResponse.new(500).wwwAuthenticate(content).to_exception
when "BAD_REQUEST"
# 400 Bad Request
# The request from the client application does not
# contain an access token.
raise WebResponse.new(400).wwwAuthenticate(content).to_exception
when "UNAUTHORIZED"
# 401 Unauthorized
# The presented access token does not exist or has expired.
raise WebResponse.new(401).wwwAuthenticate(content).to_exception
when "FORBIDDEN"
# 403 Forbidden
# The access token does not cover the required scopes
# or the subject associated with the access token is
# different.
raise WebResponse.new(403).wwwAuthenticate(content).to_exception
when "OK"
# The access token is valid (= exists and has not expired).
return response
else
# This never happens.
raise WebResponse.new(500, "Unknown action").plain.to_exception
end
end
```
```php PHP theme={null}
/**
* Call Authlete's /api/auth/introspection API.
* A response from the API is returned when the
* access token is valid. Otherwise, a WebException
* is raised.
*/
function do_introspection($token, $scopes, $subject)
{
// Call Authlete's /api/auth/introspection API.
$response = call_introspection_api($token, $scopes, $subject);
// he value of 'WWW-Authenticate' header on error.
$content = $response['responseContent'];
// "action" denotes the next action.
switch ($response['action'])
{
case 'INTERNAL_SERVER_ERROR':
// 500 Internal Server Error
// The API request from this implementation was wrong
// or an error occurred in Authlete.
(new WebResponse(500))->wwwAuthenticate($content)->finish();
return null; // Not reach here.
case 'BAD_REQUEST':
// 400 Bad Request
// The request from the client application does not
// contain an access token.
(new WebResponse(400))->wwwAuthenticate($content)->finish();
return null; // Not reach here.
case 'UNAUTHORIZED':
// 401 Unauthorized
// The presented access token does not exist or has expired.
(new WebResponse(401))->wwwAuthenticate($content)->finish();
return null; // Not reach here.
case 'FORBIDDEN':
// 403 Forbidden
// The access token does not cover the required scopes
// or the subject associated with the access token is
// different.
(new WebResponse(403))->wwwAuthenticate($content)->finish();
return null; // Not reach here.
case 'OK':
// The access token is valid (= exists and has not expired).
return $response;
default:
// This never happens.
(new WebResponse(500, "Unknown action"))->plain()->finish();
return null; // Not reach here.
}
}
```
# 8. Endpoint Sample
This section shows example implementations to (1) extract an access token and (2) validate the access token in a protected resource endpoint (so-called Web API). Each implementation requires two scopes, `read` and `write`.
```java Java (JAX-RS) theme={null}
private static final String[] SCOPES = { "read", "write" };
@GET
public Response hello(
@HeaderParam(HttpHeaders.AUTHORIZATION) String authorization,
@QueryParam("access_token") String accessToken)
{
// Extract an access token from the request.
accessToken = extractAccessToken(authorization, accessToken);
// Introspect and validate the access token.
IntrospectionResponse response = doIntrospection(accessToken, SCOPES, null);
// The access token is valid. Write the main code after here.
......
}
```
```ruby Ruby (Sinatra) theme={null}
get '/hello' do
# Extract an access token from the request.
token = extract_access_token(request)
begin
# Introspect and validate the access token.
response = do_introspection(token, ["read", "write"], nil)
rescue WebException => e
# The access token is invalid.
return e.to_response
end
# The access token is valid. Write the main code after here.
......
end
```
```php PHP theme={null}
```
# 9. Scope
## How Are Scopes Used?
In OAuth context, **scope** is a technical term and it represents a permission. When a client application makes an authorization request to an [authorization endpoint](http://tools.ietf.org/html/rfc6749#section-3.1) of a service, the application may list scopes it wants using `scope` parameter. The following is an example authorization request specifying two scopes: `read` and `write`.
```
https://host/authorize?response_type=token&client_id=your-client-id&scope=read+write
```
After the end-user authorizes the authorization request, the authorization server issues an access token which are associated with the scopes. The client application uses the access token when it accesses protected resource endpoints of the service. The following is an example protected resource request that uses access\_token query parameter to pass an `access token` to a protected resource endpoint.
```
https://host/profile?access_token=access-token
```
A protected resource endpoint extracts an access token from the protected resource request and validates the access token. In a typical case, an implementation of protected resource endpoint checks whether the presented access token is associated with scopes which the endpoint requires. The following is a conceptual code to extract and validate an access token.
```
// Extract an access token from the protected resource request.
access_token = extract_access_token_from_request(request);
// If the access token has expired.
if (access_token.has_expired) {
return unauthorized;
}
// Scopes associated with the access token.
scopes = access_token.scopes;
// If the access token covers "read" and "write".
if (scopes.contains("read") && scopes.contains("write")) {
return ok;
} else {
return forbidden;
}
```
## Scope Name Definition
A scope name is a case-sensitive ASCII characters. The exact specification is described in "[RFC 6749, 3.3. Access Token Scope](https://tools.ietf.org/html/rfc6749#section-3.3)" as shown below. `%x20` (space), `%x22` (double quotation mark) and `%x5C` (back slash) are not allowed to use.
```
scope-token = 1*( %x21 / %x23-5B / %x5D-7E )
```
According to the specification, `%x2C` (comma) can be used for scope names, but Facebook uses commas as the delimiter to list scope names. It is a violation against the specification.
## Standard Scopes
[OpenID Connect Core 1.0](http://openid.net/specs/openid-connect-core-1_0.html) has defined some standard scopes. The table below is the list.
| Name | Section |
| - | - |
| address | [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) |
| email | [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) |
| openid | [3.1.2.1. Authentication Request](http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) |
| offline\_access | [11. Offline Access](http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess) |
| phone | [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) |
| profile | [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) |
## Scope Registration
Scopes need to be registered in advance before use. How to register scopes depends on each authorization server implementation.
Authlete provides GUI for you to edit the list of scopes supported by your service. Login [Service Owner Console](https://so.authlete.com/), click "Edit" button of a service whose scopes you want to edit, and click "Token" tab. You can see "supported scopes" at the bottom like below.
After editing the list, don't forget to press "Update" to apply changes.
# 10. Summary
Authlete provides an introspection API (`/api/auth/introspection`) for implementations of protected resource endpoints. The API is better than [RFC 7662](http://tools.ietf.org/html/rfc7662) because it does not only introspection but also validation and building an error message complying with [RFC 6750](http://tools.ietf.org/html/rfc6750).
# Pushed Authorization Requests (PAR)
Source: https://developers.authlete.com/configuration-reference/endpoints/pushed-authorization-requests-par
Overview of the technical support for Pushed Authorization Requests (PAR) in OAuth 2.0 framework with a focus on implementing PAR EP in an authorization server and configuring PAR settings in Authlete.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/protocol-extensions/pushed-authorization-requests).
## Introduction
[RFC 9126: OAuth 2.0 Pushed Authorization Requests (PAR)](https://www.rfc-editor.org/rfc/rfc9126.html)
is one the most impactful security enhancements to the OAuth2 framework. It allows clients to "push" (submit directly) content of an authorization request to the authorization server in advance, instead of sending it conventionally through a user agent.
The PAR specification defines a new endpoint (PAR EP) that accepts the authorization request content. The PAR EP sends back an identifier (request\_uri) so that the client can include it in the following authorization request.
The segregation of the provisioning of the authorization request content from the authorization request creates one more option to enhance the security, for instance: SPAs (single page applications) can rely on server side to create the authorization request content without disclosing any detail of the authorization request to the browser, or mobile apps can create and send the authorization request content before forwarding it to browser.
This article describes an overview of PAR support in Authlete, and instructions to enable it.
***
## Implementing PAR EP
In order to support PAR, you have to implement a PAR EP in an authorization server (or an OP on OIDC parlance), and configure PAR settings in Authlete.
The PAR EP of the authorization server can employ [/pushed\_auth\_req](/api-reference/pushed-authorization-endpoint/process-pushed-authorization-request) API of Authlete as a backend. This API has the same design principle as the other endpoints: it allows your authorization server to simply forward the pushed authorization request from a client to Authlete.
*Authorization code flow using Pushed Authorization Requests*
Authlete's [/pushed\_auth\_req](/api-reference/pushed-authorization-endpoint/process-pushed-authorization-request) API expects the same payload of [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API, plus the client authentication. The [/pushed\_auth\_req](/api-reference/pushed-authorization-endpoint/process-pushed-authorization-request) API will provide content of a pushed authorization response (responseContent) to be sent back to the client. The content includes a `request_uri` so that the client can use it on making an authorization request.
### Sample request and response
The authorization request content pushed from the client to the PAR EP is similar to ones in a usual request to an authorization endpoint, except that the client sends the authorization request using POST method and application/x-www-form-urlencoded media type.
```
POST /as/par HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
response_type=code&
client_id=3280859750204&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_challenge=W78hCS0q72DfIHa...kgZkEJuAFaT4&
code_challenge_method=S256
```
The sample request below, shows an interaction between an authorization server and Authlete’s [/pushed\_auth\_req](/api-reference/pushed-authorization-endpoint/process-pushed-authorization-request) API. The authorization request content, which initiates an authorization code flow with PKCE in this example, should be provisioned by a client.
The response from Authlete is the action "CREATED" and a requestUri, meaning that the authorization request was provisioned with an identifier as the requestUri value. Authlete also creates responseContent, whose value is intended to be used as a pushed authorization response from the PAR EP to the client.
* Request
```
curl --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/pushed_auth_req" \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--header 'Content-Type: application/json' \
--data '{
"parameters": "response_type=code&
client_id=3280859750204&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_challenge=W78hCS0q72DfIHa...kgZkEJuAFaT4&
code_challenge_method=S256",
"clientId": "3280859750204"}'
```
* Response
```json highlight={4-6} theme={null}
{
"resultCode": "A245001",
"resultMessage": "[A245001] Successfully registered a request object for client (3280859750204), URI is urn:ietf:params:oauth:request_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4.",
"action": "CREATED",
"requestUri": "urn:ietf:params:oauth:request_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4",
"responseContent": "{\"expires_in\":600,\"request_uri\":\"urn:ietf:params:oauth:request_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4\"}"
}
```
Using the `request_uri` returned from the PAR EP, the client sends an authorization request to the authorization server via a user agent. On receiving the request, the authorization server makes a request to Authlete’s /auth/authorization API as follows.
```shell highlight={7} theme={null}
curl --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data '{
"parameters":
"client_id=3280859750204&
request_uri=urn:ietf:params:oauth:request_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4"
}'
```
A response from Authlete is the same as the regular authorization request. A ticket will be created - check the details under the [note describing Authlete ticket](/configuration-reference/endpoints/ticket-parameter-in-authorization-endpoint).
***
## PAR configuration on Authlete
Log in to the Authlete Management Console and open **Service Settings** for your Authlete service to configure it as the backend for the PAR endpoint on the authorization server.
In the **Pushed Authorization Request (PAR)** tab, you can:
* Specify whether the usage of PAR is mandatory for every client by toggling the **Require PAR** option.
* Configure how long a pushed authorization request remains valid (set the duration in seconds).
Make the necessary changes and click **Save Changes** to apply the configuration.

*Pushed Authorization Request config on service level*
You can make PAR mandatory for a specific client, even if the Authlete service is not configured to require it. To enable this:
1. Open **Client Settings** for your client.
2. Navigate to **Endpoints > General > Pushed Authorization Request**.
3. Toggle the **Require PAR** switch to enable it.
This setting ensures that the client must use PAR, overriding the service-level configuration.

*Flag to force a specific client to use pushed authorization request*
***
## Client authentication
An authorization server may authenticate clients on a PAR EP. Authlete applies authentication method settings for a token endpoint to the PAR EP as well. The mechanism is described in the following article.
* [Configuring Client Authentication](/configuration-reference/endpoints/configuring-client-authentication)
In the **Service Settings** under the **Token** tab, you can configure the supported **Client Authentication Methods** for the token endpoint. Select methods like `PRIVATE_KEY_JWT` or `TLS_CLIENT_AUTH` based on your requirements.
To configure:
1. Navigate to **Endpoints > Token** in **Service Settings**.
2. Select the desired authentication methods under **Supported Client Authentication Methods**.
3. Click **Save Changes**.
These settings define how clients authenticate when accessing the token endpoint.

*Client authentication method on token and PAR endpoint for each client*
For the authentication method CLIENT\_SECRET\_BASIC, the client will be authenticated on the PAR EP using basic HTTP authentication. The authorization server will make a request to Authlete’s `/pushed_auth_req` API with the credentials submitted from the client, by using the "clientId" and "clientSecret" attributes (check snipped below).
```
curl --location --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/pushed_auth_req" \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--header 'Content-Type: application/json' \
--data-raw '{
"parameters": "response_type=code%20id_token&client_id=3280859750204&redirect_uri=https%3A%2F%2Fserver.example.com%2Fcb&state=SOME_VALUE_ABLE_TO_PREVENT_CSRF&scope=openid&nonce=SOME_VALUE_ABLE_TO_PREVENT_REPLAY_ATTACK&code_challenge=GyeodZxSpq0iyjNbEQE6N96MxomMXYpYUkfuEpvQ3Js&code_challenge_method=S256",
"clientId": "3280859750204",
"clientSecret": "qfd0ScLHhD**************YDg"
}'
```
If the client is configured for a client certificate based authentication method, the request will be similar to the request below.
```
curl --location --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/pushed_auth_req" \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--header 'Content-Type: application/json' \
--data-raw '{
"parameters":"response_type=code&client_id=....",
"clientId":3282602314604,
"clientCertificate":"\n-----BEGIN CERTIFICATE-----\nMIICnDCCAYSgAwIBAgIGAXqsMta0MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMM\nBGtleTEwHhcNMjEwNzE1MjIwNDEwWhcNMjIwNTExMjIwNDEwWjAPMQ0wCwYDVQQD\nDARrZXkxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh517rALrL+I9\npvOpCVr9wuJ/TE3l3CndvE9oRrU2BpBYSn0LVnIT6anKgrYSJNP/YOkgHqUQQIoq\n0j7Uv7fiYL02OuAuVouOP3pxC1QiRGNInZkmYVJ0EsNz8Gft3JW7A9pUHc/Sx0P1\nTbN1hL9J5auasCNjUhd3GCB7bEJeIlez066qkUeZR/Jtpqdh9TVJrnBjEiihrcwL\nlixo4G5Y2Tg9vpjOCKgoL1tni6wbxY64BzksF2y10OEfvcwacmLBsMxHhN3l0qU2\nnbKbYKb2R0xRq8DU5woG0Rkbi5z6FRF4DLzpjig6vk6ENjwenHFYt8XMhulmSdnX\nGvDe2/BWYQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQABRsoLK5hn5DesBpnDCBfq\nZnyMiWyUbh8qmIhO5Ta6Hq/AeUSM16gqJqBsLQm6UllfsW30Qn9EwkCMG1Fb4g8t\n5TVigtvtVcTkn3H2Ib6EhtsB5Evs1U273W5Z/y7QUDrS2TahraKNKK2k81UbHhZf\nY1qyDMTK1+a+EAcuUaFOPsOzZo3Yxa2GDXQ8ZjHwk4E7tIri953P66gGHC3GNTy\n92hrXw8KgoIXJXKyZ5WeyziTIfAypnlI6EzUU\n-----END CERTIFICATE-----"
}'
```
In case of TLS\_CLIENT\_AUTH using PKI authentication - where Authlete can validate the client certificate chain against a list of root CAs - the authentication will be similar to the request below.
```
curl --location --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/pushed_auth_req" \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--header 'Content-Type: application/json' \
--data '{
"parameters":"response_type=code&client_id=3289644915401&...",
"clientId":3289644915401,
"clientCertificate":"\n-----BEGIN CERTIFICATE-----\nMIIDmzCCAoOgAwIBAgI.....ftMPIhU1ocI0Uh9ObkPq5atK0lx\n39OTMXLj1kHxlf3RnoRo\n-----END CERTIFICATE-----",
"clientCertificatePath":["\n-----BEGIN CERTIFICATE-----\nMIIE3TCCAsWgAwI.....9HEtxsOeIDWmILz453xtSBdorV7rN7QcEK6Hd62czruZtk/ItPjQMnB1moBT3d\n5g==\n-----END CERTIFICATE-----",
"\n-----BEGIN CERTIFICATE-----\nMIIFuDCCA6CgAwI.....FRVZqvemtV0gZM0C3tkDBQzGsb/KW\nnFWbOABBQequSMJN0MjWd+fkiDZAJq/X0Gw==\n-----END CERTIFICATE-----"]}'
```
# Requiring clients to specify S256 when using PKCE for their authorization requests
Source: https://developers.authlete.com/configuration-reference/endpoints/requiring-clients-to-specify-s256-when-using-pkce
Require OAuth 2.0 clients to specify S256 for code_challenge_method parameter when using PKCE for authorization requests with Authlete's feature.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/authorization-requests/requiring-s256).
# Requiring Clients to Specify "S256" When using PKCE for their Authorization Requests
Authlete has a feature to require OAuth 2.0 clients to specify a value of "**S256**" for "**code\_challenge\_method**" parameter when using [PKCE (RFC 7636)](/protocols-and-flows/protocol-extensions/proof-key-for-code-exchange-pkce) for their authorization requests.
To enable `S256` for your service client using `PKCE`:
1. Navigate to **Client Settings > Endpoints > Authorization > General**
2. Under `Proof Key for Code Exchange (PKCE)`, enable the `"Require S256 for Code Challenge Method"` option. By default, the `S256 for Code Challenge Method` is disabled.
3. Click Save Changes to apply the updates.

*"S256 for Code Challenge Method" setting*
Once enabled, the [POST /auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API of the configured Authlete service will deny any authorization requests without `code_challenge_method=S256`.
The following example shows how an authorization request uses `PKCE` but includes "`code_challenge_method=plain`." Thus, Authlete denies processing. (folded for readability):
**Authorization Request**
```text highlight={8-9} theme={null}
% curl -s -X POST .../auth/authorization
-H "Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "redirect_uri=...
&response_type=code
&client_id=...
&scope=...
&code_challenge=...
&code_challenge_method=plain" }'
```
**Authorization Response**
```json theme={null}
{
"resultCode": "A124308",
"resultMessage": "[A124308] The value of the 'code_challenge_method' request parameter must be 'S256'.",
...
}
```
# Requiring clients to use PKCE for their authorization requests
Source: https://developers.authlete.com/configuration-reference/endpoints/requiring-clients-to-use-pkce-for-their-authorization-requests
Technical information about how to require OAuth 2.0 clients to use PKCE for their authorization requests with Authlete's feature.
# Requiring PKCE for Client Authorization Requests
Authlete has a feature to require OAuth 2.0 clients to use **[PKCE (RFC 7636)](/protocols-and-flows/protocol-extensions/proof-key-for-code-exchange-pkce)** for their authorization requests.
To enable `PKCE` for your service client:
1. Navigate to **Client Settings > Endpoints > Authorization > General**
2. Under `Proof Key for Code Exchange (PKCE)`, enable the `Require PKCE` option. By default, the `Require PKCE` option is disabled.
3. Click Save Changes to apply the updates.

*"Proof Key for Code Exchange (RFC 7636)" setting*
Once enabled, the [/auth/authorization API](/api-reference/authorization-endpoint/process-authorization-request)
of the configured Authlete service will deny any authorization requests without the `code_challenge` parameter.
The following example shows how an authorization requests without the `code_challenge` parameter is handled (folded for readability):
\*\* Authorization request without the code\_challenge "parameters"\*\*
```
curl -v -X POST .../auth/authorization \
-H "Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{"parameters": "redirect_uri=...&response_type=code&client_id=...&scope=..."}'
```
***
**Response (stating that code\_challenge is missing)**
```
{
"resultCode": "A124301",
"resultMessage": "[A124301] The authorization request does not contain 'code_challenge' parameter. See RFC 7636 for details.",
...
}
```
# responseContent of a Valid Introspection Response
Source: https://developers.authlete.com/configuration-reference/endpoints/responsecontent-of-a-valid-introspection-response
Why Authlete returns Bearer error="invalid_request" as responseContent even when the introspected access token is valid.
When a valid access token is sent to Authlete's [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request)
API, the response says the token is valid — and still carries
`Bearer error="invalid_request"` as `responseContent`.
```json highlight={4,10} theme={null}
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
"clientId": 1048476397,
"subject": "user123",
"existent": true,
"usable": true,
"refreshable": true,
"responseContent": "Bearer error=\"invalid_request\"",
"expiresAt": 1789564514000
}
```
This is not a sign that anything went wrong. `action: OK` is the field that
reports the outcome of the introspection: the access token exists and has not
expired, and the resource server should serve the protected resource.
`responseContent` is not a result — it is a prepared value for the
`WWW-Authenticate` header, for the case where the resource server decides to
reject the request anyway. `Bearer error="invalid_request"` is the simplest
string usable with `400 Bad Request`, and it is what Authlete returns when
`action` is `OK`, because at that point the token is fine and the only thing
left to complain about is the request itself.
```http theme={null}
HTTP/1.1 400 Bad Request
WWW-Authenticate: Bearer error="invalid_request"
Cache-Control: no-store
Pragma: no-cache
```
In practice, tell the client what is actually wrong rather than sending this
bare string — the developer of the client application cannot act on
`invalid_request` alone.
Note also that `responseContent` is formatted for the `Bearer` token type
([RFC 6750](https://www.rfc-editor.org/rfc/rfc6750)). A service that uses a
different token type has to build the error response itself.
See also:
* [JavaDoc for Class IntrospectionResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/IntrospectionResponse.html)
# Strict checking on client authentication parameters
Source: https://developers.authlete.com/configuration-reference/endpoints/strict-checking-on-client-authentication-parameters
Strict checking on client authentication parameters in Authlete version 2.0 requires specific configurations and values in token requests with differences from the previous version to note during migration.
## Overview
Authlete version 2.0 and later strictly check client type and client authentication method settings, and values specified in token requests. Some requests which are valid for Authlete 1.1 may be refused.
## Difference between 1.1 and 2.0 (and later)
| | Authlete 1.1 | Authlete 2.0 and later |
| - | - | - |
| Verification policy | When client secret is included in a token request, Authlete always verifies its value regardless of configuration (it ignores settings for client type and client authentication method). Authlete accepts a token request with no `client_id` because it can derive the client identifier from other parameters such as authorization code or refresh token. | Verification behavior varies with configured values of client type and client authentication method. A) Client type = `public`: client authentication method must be `none`; other values cause an error. B) Client type = `confidential`: client authentication method must not be `none`; if method is `client_secret_basic`, client secret must be sent in the `Authorization` header; if method is `client_secret_post`, client secret must be sent in request body. |
| Default settings | Client type: `public`; client authentication method: `client_secret_basic` | Client type: `public`; client authentication method: `none` |
## Notes on migrating from Authlete 1.1 to 2.0 and later
In version 1.1, if you have specified client\_secret\_basic as client authentication method and some client sends a request with client secret in request body, Authlete verifies the value.
In version 2.0 and later, clients must include client secret in Authorization header if you have specified client\_secret\_basic as client authentication method. Thus Authlete 2.0 responds error against such request that is valid for Authlete 1.1.
# Ticket Parameter in Authorization Endpoint
Source: https://developers.authlete.com/configuration-reference/endpoints/ticket-parameter-in-authorization-endpoint
Backend APIs for implementing an authorization endpoint with a ticket parameter that links two types of APIs and expires within 24 hours.
Authlete's [Authorization Endpoint APIs](/api-reference/authorization-endpoint)
are backend APIs to implement an authorization endpoint. The APIs are comprised of two types of APIs:
1. API that understands authorization requests and provides information that will be required in the next step, such as end-user authentication, and
* [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request)
2. API that issues tokens or codes, or returns errors
* [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request)
, [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)
These two APIs are linked using "ticket."
First, Authlete /auth/authorization API returns ticket in its response to authorization requests. Then, auth/authorization/issue or auth/authorization/fail API receives the ticket and process the authorization requests to issue tokens or codes or return errors.\\
Here is a couple of nature of the ticket.
* A ticket will be expired in 24 hours. Expired tickets will be deleted from Authlete's database.
* A ticket can only be used once. It will be removed from the database right after /auth/authorization/issue or /auth/authorization/fail API successfully processed a request including the ticket.
* When you use a ticket that has already been used or expired, you will get an error code like below:
```
[A041202] There is no entity having the ticket specified
in the /api/auth/authorization/issue request (ticket = {Ticket}).
```
* Please note that tickets are designed to be used only between an authorization server and Authlete server; It must thus not be used between an authorization server and user agent, such as web browser, to manage sessions, for example.
# Use cases for two introspection APIs
Source: https://developers.authlete.com/configuration-reference/endpoints/use-cases-for-two-introspection-apis
Various use cases for Authlete's two introspection APIs including the /auth/introspection and /auth/introspection/standard APIs are discussed in this article.
## Preface
This article describes Authlete's two kinds of introspection APIs and their use cases.
## Introspection APIs provided by Authlete
Authlete provides two APIs to fulfill token introspection.
* [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API
* [/auth/introspection/standard](/api-reference/introspection-endpoint/process-oauth-20-introspection-request) API
### /auth/introspection API
/auth/introspection API is a proprietary API of Authlete. It is designed to be used by implementation of protected resource endpoint (e.g. Web API) in a resource server (RS).
The API can accept request parameters including an access token and other values expected to be bound to the token such as a [scope](/configuration-reference/endpoints/checking-if-an-access-token-has-particular-scopes), a subject, [a client certificate](/configuration-reference/tokens-and-claims/issuing-mutual-tls-certificate-bound-access-tokens), and a [DPoP Proof JWT](/configuration-reference/tokens-and-claims/using-dpop) to allow the RS to offload verification of the binding, to Authlete. Thus implementation of the RS would be simplified.
### /auth/introspection/standard API
/auth/introspection/standard API is designed to be used by implementation of an RFC 7662 compliant introspection endpoint in an authorization server (AS).
Upon receiving an introspection request from the RS, the AS forwards the request parameters to Authlete. Authlete processes the request in accordance with RFC 7662, generates the introspection response content, and returns it to the AS.
## Choosing an introspection API based on architecture
The choice between the two types of introspection APIs depends on the desired level of coupling between the AS, RS, and Authlete. Common use cases and the most suitable APIs for each are outlined below.
### RS to talk directly to Authlete
If the RS is able to possess an Authlete service access token, the simplest approach is for the RS to call Authlete's /auth/introspection API directly. As of Authlete version 3.0.31 or later, the [Custom Service Token](/release-notes/server-v3/server-v3-april-2026#flexible-service-tokens) feature allows you to restrict the service access token's permissions solely to introspection.
### API gateway to accept all incoming API requests from clients
If an API gateway handles both AS endpoints (such as the token endpoint) and RS API endpoints, it will inherently possess an Authlete service access token. Consequently, as with the previous case, using the /auth/introspection API provides the simplest architecture.
### Exposing RFC 7662 introspection API for the RS
If you want to avoid direct communication between the RS and Authlete, or completely decouple the AS and RS, it is recommended that the AS expose an RFC 7662-compliant introspection API to the RS and call Authlete’s /auth/introspection/standard API to process the request.
# Using Request Object
Source: https://developers.authlete.com/configuration-reference/endpoints/using-request-objects
Instructions on configuring Authlete to support authorization requests with request objects for enhanced security.
## Preface
[Request Object](https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests)
is a JWT that contains a set of request parameters as its [Claims](https://tools.ietf.org/html/rfc7519#section-4).
The request object is capable to be signed as well as encrypted to protect against tampering and/or unintended data exposure. It can be passed by reference instead of value, to make a size of an authorization request (via browser redirect) smaller.
This article describes instructions for Authlete to support authorization requests with request objects.
## Configuration
In order for an Authlete service to process authorization requests that employ a request object, you have to configure these two properties so that the service can verify a signature of the object.
1. A signing algorithm used for request objects
2. A public key that corresponds to the specified signing algorithm
Optionally, you can configure Authlete to allow clients to:
* Encrypt requests objects, and/or
* Use “request\_uri.”
These settings are specified for each client through [Authlete Management Console](https://console.authlete.com/)
.
*Processing request objects*
## 1. Specifying a signing algorithm
Enter a value representing a signing algorithm, for Request Object Signature Algorithm section in Authorization tab. The following example shows a result when “ES256” has been registered.
*Request Object Signature Algorithm*
## 2. Registering a public key
Register a JWK formatted public key for “JWK Set Content” section, or specify a URL for “JWK Set URL” in JWK Set tab. The following example shows a result when an ES256 public key has been registered.
*JWK Set Content*
With the settings above, when the configured client makes a authorization request that contains a request object, the Authlete service verifies a signature of the object using the public key determined by the signing algorithm, and proceeds further.
## Option: Specifying an encryption algorithm
Specify appropriate values for “Encryption Algorithm” and “Encryption Encoding Algorithm” in Authorization tab, if you want to use encrypted request objects.\\
## Option: Registering values for request\_uri
Enter appropriate values for “Request URIs” in Authorization tab so that the client make an authorization request with request\_uri (pass-by-reference) instead of request (pass-by-value).
***
## Examples
API request / response examples are as follows. (folded for readability)
## Request
### Client: Creating a request object
Assume a client creates a signed JWT (a request object) that contains the following payload:
```
{
"exp": 1597130002,
"iss": "17201083166161",
"aud": "https://as.example.com",
"scope": "openid profile",
"response_type": "code",
"client_id": "17201083166161",
"redirect_uri": "https://client.example.org/cb/example.com",
"nonce": "lvzxcjkb78643ki5kigsysigyb",
"foo": "bar"
}
```
The resulting JWT would be as follows:
```
eyJhbGciOiJFUzI1NiJ9.
ewogICJleHAiOiAxNTk3MTMwMDAyLAogICJpc3MiOiAiMTcyMDE
wODMxNjYxNjEiLAogICJhdWQiOiAiaHR0cHM6Ly9hcy5leGFtcG
xlLmNvbSIsCiAgInNjb3BlIjogIm9wZW5pZCBwcm9maWxlIiwKI
CAicmVzcG9uc2VfdHlwZSI6ICJjb2RlIiwKICAiY2xpZW50X2lk
IjogIjE3MjAxMDgzMTY2MTYxIiwKICAicmVkaXJlY3RfdXJpIjo
gImh0dHBzOi8vY2xpZW50LmV4YW1wbGUub3JnL2NiL2V4YW1wbG
UuY29tIiwKICAibm9uY2UiOiAibHZ6eGNqa2I3ODY0M2tpNWtpZ
3N5c2lneWIiLAogICJmb28iOiAiYmFyIgp9.
JnuCpEL1mRKPBUfXIO5mPtusY2lnz1w6oS6QQdYai7VUXUxfPA7
elMIWdsExc7tsSovTTAMK8mSxz9COAciY-Q
```
### Client: Making an authorization request
Then the client crafts an authorization request with the request object above. The request will include it either as a value (request) or a reference (request\_uri).
* Passing the object by value
```http highlight={5-15} theme={null}
GET /cb/example.com
&client_id=...
&scope=openid
&response_type=code
&request=eyJhbGciOiJFUzI1NiJ9.
ewogICJleHAiOiAxNTk3MTMwMDAyLAogICJpc3MiOiAiMTcyMDE
wODMxNjYxNjEiLAogICJhdWQiOiAiaHR0cHM6Ly9hcy5leGFtcG
xlLmNvbSIsCiAgInNjb3BlIjogIm9wZW5pZCBwcm9maWxlIiwKI
CAicmVzcG9uc2VfdHlwZSI6ICJjb2RlIiwKICAiY2xpZW50X2lk
IjogIjE3MjAxMDgzMTY2MTYxIiwKICAicmVkaXJlY3RfdXJpIjo
gImh0dHBzOi8vY2xpZW50LmV4YW1wbGUub3JnL2NiL2V4YW1wbG
UuY29tIiwKICAibm9uY2UiOiAibHZ6eGNqa2I3ODY0M2tpNWtpZ
3N5c2lneWIiLAogICJmb28iOiAiYmFyIgp9.
JnuCpEL1mRKPBUfXIO5mPtusY2lnz1w6oS6QQdYai7VUXUxfPA7
elMIWdsExc7tsSovTTAMK8mSxz9COAciY-Q HTTP/1.1
Host: as.example.com
...
```
* Passing the object by reference
The client have to put the request object to the request\_uri location e.g. [https://client.example.org/request.jwt](https://client.example.org/request.jwt) and make it accessible to Authlete.
```http highlight={5} theme={null}
GET /cb/example.com
&client_id=...
&scope=openid
&response_type=code
&request_uri=https://client.example.org/request.jwt
HTTP/1.1
Host: as.example.com
...
```
### Authorization Server: Making a request to Authlete API
On receiving the authorization request, the authorization server makes a request to Authlete's [/auth/authorization API](/api-reference/authorization-endpoint/process-authorization-request)
. The request contains the authorization request from the client as a value of `parameters` — the same `request` JWT shown above.
```shell highlight={9-19} theme={null}
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{"parameters":
"redirect_uri=https://client.example.org/cb/example.com
&client_id=...
&scope=openid
&response_type=code
&request=eyJhbGciOiJFUzI1NiJ9.
ewogICJleHAiOiAxNTk3MTMwMDAyLAogICJpc3MiOiAiMTcyMDE
wODMxNjYxNjEiLAogICJhdWQiOiAiaHR0cHM6Ly9hcy5leGFtcG
xlLmNvbSIsCiAgInNjb3BlIjogIm9wZW5pZCBwcm9maWxlIiwKI
CAicmVzcG9uc2VfdHlwZSI6ICJjb2RlIiwKICAiY2xpZW50X2lk
IjogIjE3MjAxMDgzMTY2MTYxIiwKICAicmVkaXJlY3RfdXJpIjo
gImh0dHBzOi8vY2xpZW50LmV4YW1wbGUub3JnL2NiL2V4YW1wbG
UuY29tIiwKICAibm9uY2UiOiAibHZ6eGNqa2I3ODY0M2tpNWtpZ
3N5c2lneWIiLAogICJmb28iOiAiYmFyIgp9.
JnuCpEL1mRKPBUfXIO5mPtusY2lnz1w6oS6QQdYai7VUXUxfPA7
elMIWdsExc7tsSovTTAMK8mSxz9COAciY-Q"}'
```
## Response
If Authlete has been properly configured, it sends back a response like this.
```json highlight={8-18} theme={null}
{
"resultCode": "A004001",
"resultMessage":
"[A004001] Authlete has successfully issued a ticket
to the service (API Key = 14156727624613) for the
authorization request from the client (ID = ...).
[response_type=code, openid=true]",
"requestObjectPayload":
"{\"exp\":1597130002,
\"iss\":\"17201083166161\",
\"aud\":\"https://as.example.com\",
\"scope\":\"openid profile\",
\"response_type\":\"code\",
\"client_id\":\"17201083166161\",
\"redirect_uri\":
\"https://client.example.org/cb/example.com\",
\"nonce\":\"lvzxcjkb78643ki5kigsysigyb\",
\"foo\":\"bar\"}",
"ticket": "rja...GiE"
[...]
```
Note that there is a key "requestObjectPayload" that includes content of the payload. The authorization server can utilize them for further authorization process.
# Required Scope When response_type Contains id_token
Source: https://developers.authlete.com/configuration-reference/endpoints/when-a-response-type-parameter-contains-id-token
Technical information about the necessity of including openid in the value of the scope parameter when the response_type parameter contains id_token in an authorization request.
## Overview
When the **response\_type** parameter of an authorization request contains\*\* id\_token **in its value, the** scope\*\* parameter of the request must contains **openid** in its value.
## Detail
When calling the Authlete [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request)
API, if the **response\_type** in the parameters contains **id\_token**, please make the **scope** parameter contains **openid** in its value.
Otherwise, the API will return the response like below.
```
{
"resultCode": "A004302",
"resultMessage": "[A004302] The value of 'response_type' (id_token) is not allowed when 'scope' does not contain 'openid'.",
"action": "BAD_REQUEST",
"client": ...
}
```
# There Is No Entity Having the Ticket Error
Source: https://developers.authlete.com/configuration-reference/endpoints/when-seeing-the-error-of-there-is-no-entity-having-the-ticket-specified
The article covers the conditions under which the error There is no entity having the ticket specified may occur in the Authlete API server.
## When seeing the error of "There is no entity having the ticket specified..."
Authlete API server sends ticket-related errors like below:
> \[A041201] There is no entity having the ticket specified in the /api/auth/authorization/issue request (ticket = ).
You will see this error in the following conditions:
1. The ticket used in a request is already expired, or
2. The ticket used in a request is already used before.
As to the detail of the ticket parameter, please check the following article.\
[Ticket parameter in Authorization Endpoint](/configuration-reference/endpoints/ticket-parameter-in-authorization-endpoint)
# Service Flags
Source: https://developers.authlete.com/configuration-reference/error-handling-debugging/flags-supported-in-authlete
Configure various flags in Authlete to enforce stricter requirements to meet OAuth/OIDC specifications.
## Overview
Over the years, Authlete has implemented a large number of standard specifications. Among them, there have been several occasions where changes to Authlete’s implementation were necessary due to overlooked details, misinterpretations, or breaking changes in the specifications themselves. This article shares various flags created from these challenges.
Service flags in Authlete provide essential configuration flexibility that allows developers to fine-tune the behavior of their OAuth 2.0 and OpenID Connect implementations. These boolean switches and configuration parameters enable organizations to balance strict specification compliance with practical deployment considerations.
Service flags serve multiple crucial purposes:
* Allow gradual migration to stricter compliance requirements without breaking existing integrations
* Provide options for handling edge cases or ambiguities in the specifications
* Enable customization of security policies to meet specific organizational requirements
* Facilitate interoperability with a wide range of client applications, including those that may not fully comply with the latest standards
## Flags for Specification Compliance
The table below shows an overview of the Authlete service flags:
| Flag | Type | Description | Default Value |
| - | - | - | - |
| Service.missingClientIdAllowed | Boolean | When set to `true` or `enabled` in the console, the client is identified from the authorization code when the `client_id` request parameter is missing. | False |
| Service.claimShortcutRestrictive | Boolean | Ensures strict adherence to the OpenID Connect specification regarding claim embedding. When set to `true` or enabled in the console, it ensures that claims requested by the profile, email, address, and phone scope values are only included in the ID Token if no Access Token is issued. | True |
| Service.dcrDuplicateSoftwareIdBlocked | Boolean | When set to `True`, Authlete rejects dynamic client registration requests containing a registered `software_id` | False |
| Service.dcrScopeUsedAsRequestable | Boolean | Limits the range of scopes that the client application can request. When set to `True`, follows RFC 7591 standard by treating scopes specified during dynamic client registration as requestable scopes. | False |
| Service.frontChannelRequestObjectEncryptionRequired | Boolean | When sending a request object via the front channel, it must be encrypted. | False |
| Service.idTokenAudType | Array, String, or Null | Addresses the JWT specification flexibility where the `aud` claim can be either an `array` or a `single string`, causing inconsistency in how receiving systems must process tokens. It can also be overridden at runtime using the `idTokenAudType` parameter in Authlete APIs that issue ID tokens (such as `/auth/authorization/issue`). | Null |
| Service.idTokenReissuable | Boolean | controls whether the `/auth/token` API returns `ID_TOKEN_REISSUABLE`. When set to `True`, the /`auth/token` API returns `"action": "ID_TOKEN_REISSUABLE"` if the conditions are all met. | False |
| Service.issSuppressed | Boolean | When set to `True`, does not include iss response parameter in the authorization response. | False |
| Service.loopbackRedirectionUriVariable | Boolean | Controls how the authorization server handles port numbers in loopback IP address redirection URIs during OAuth 2.0 flows. When set to `True`, Treats the port number as variable when the host part of the redirection URI is a loopback IP address, following RFC 8252 Section 7.3 | False |
| Service.nbfOptional | Boolean | When set to `True`, the authorization server does not enforce the request object lifespan check, even in scenarios where FAPI 1.0 Part 2 compliance would normally require it. In other words, the `nbf` claim is treated as optional rather than mandatory. | False |
| Service.refreshTokenIdempotent | Boolean | When set to `True` or enabled in the console, ensures consistent results for duplicate token refresh requests within a 60-second window, preventing issues when the same refresh token is used multiple times. | False |
| Service.refreshTokenKept | Boolean | Controls the behavior of refresh token rotation. When set to `True`, refresh token rotation will not occur. | True |
| Service.requestObjectEncryptionAlgMatchRequired | Boolean | The algorithm specified in the alg parameter of the encrypted request object must be a specific value. | False |
| Service.requestObjectEncryptionEncMatchRequired | Boolean | The algorithm specified in the `enc` parameter of the encrypted request object must be a specific value. | False |
| Service.scopeRequired | Boolean | Controls how authorization requests without a scope parameter are handled. When set to `True`, Authorization requests without a scope parameter will either use configured default scopes or be rejected with an invalid scope error if no defaults exist. | False |
| Service.traditionalRequestObjectProcessingApplied | Boolean | Allows switching between processing request objects using the OIDC Core method (for backward compatibility) or the RFC 9101 method. | False |
| Service.unauthorizedOnClientConfigSupported | Boolean | When this flag is set to true, Authlete will return UNAUTHORIZED where applicable. When false, for backward compatibility, Authlete will return `BAD_REQUEST` instead (even though this does not strictly follow RFC 7592). | False |
## Configure Flags in the Management Console
Starting with Authlete 3.0, flags have been introduced in the management console to enforce strict requirements to meet OAuth/OIDC specifications.
The following sections demonstrate configuring service setting flags in the [Authlete Management Console](https://console.authlete.com/):
### Client ID Omission
To Configure the `Service.missingClientIdAllowed` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Token > General**
2. To enable the flag, toggle the `“Client ID Omission"` option.
3. Click Save Changes to apply the updates.

*Client ID Omission*
### Restrict Shortcut
To Configure the `Service.claimShortcutRestrictive` flag in the management console:
1. Navigate to **Service Settings > Tokens and Claims > Claims > Supported Claims**
2. To turn on, toggle the `“Restrict Shortcut”` option.
3. Click Save Changes to apply the updates.

*Restrict Shortcut*
### DCR with Duplicate Software ID
To Configure the `Service.dcrDuplicateSoftwareIdBlocked` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Advanced > Dynamic Client Registration**
2. Go to`“DCR with Duplicate Software ID”` and toggle the `Accept` option.
3. Click Save Changes to apply the updates.

*DCR with Duplicate Software ID*
### DCR's Scope Parameter
To Configure the `Service.dcrScopeUsedAsRequestable` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Advanced > Dynamic Client Registration**
2. Go to `"DCR's Scope Parameter"` and toggle the `Enable` option.
3. Click Save Changes to apply the updates.

*DCR's Scope Parameter*
### Encryption in Front Channel
To Configure the `Service.frontChannelRequestObjectEncryptionRequired` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Authorization > Request Object**
2. Go to `"Encryption in Front Channel"` and toggle the `Require` option.
3. Click Save Changes to apply the updates.

*Encryption in Front Channel*
### Choose Audience Claim Format
To Configure the `Service.idTokenAudType` flag in the management console:
1. Navigate to **Service Settings > Tokens and Claims > ID Token**
2. Open the `“Choose Audience Claim Format”` menu and select an option.
3. Click Save Changes to apply the updates.

*Choose Audience Claim Format*
### Enable Reissuable
To Configure the `Service.idTokenReissuable` flag in the management console:
1. Navigate to **Service Settings > Tokens and Claims > ID Token**
2. Under `ID Tokens` section, toggle the `"Enable Reissuable”` option.
3. Click Save Changes to apply the updates.

*Enable Reissuable*
### Issuer Identification Response Parameter
To Configure the `Service.issSuppressed` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Authorization**
2. Under `"Issuer Identification Response Parameter"`, toggle the `Suppress iss Response Parameter` option.
3. Click Save Changes to apply the updates.

*Issuer Identification Response Parameter*
### Loopback Redirection URI
To configure the `Service.loopbackRedirectionUriVariable` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Authorization > General**
2. Go to `"Loopback Redirection URI"` and toggle the `Loopback Redirection URI` option.
3. Click Save Changes to apply the updates.

*Loopback Redirection URI*
### nbf claim
To configure the `Service.nbfOptional` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Authorization > Request Object**
2. Go to `"nbf claim""` and toggle the `"Require"` option.
3. Click Save Changes to apply the updates.

*nbf claim*
### Enable Idempotency
To configure the `Service.refreshTokenIdempotent` flag in the management console:
1. Navigate to **Service Settings > Tokens and Claims > Refresh Tokens > General**
2. Under `Refresh Token Rotation`, toggle the `Enable Idempotency` option.
3. Click Save Changes to apply the updates.

*Enable Idempotency*
### Enable Token Rotation
To configure the `Service.refreshTokenKept` flag in the management console:
1. Navigate to **Service Settings > Token and Claims > Refresh Tokens**
2. Under `Refresh Token Rotation`, toggle the `"Enable Token Rotation"` option. By default `Enable Token Rotation` is enabled.
3. Click Save Changes to apply the updates.

*Enable Token Rotation*
### Encryption Algorithm Match
To configure the `Service.requestObjectEncryptionAlgMatchRequired` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Authorization > Request Object**
2. Go to the `"Encryption Algorithm Match"` option, toggle `Require`.
3. Click Save Changes to apply the updates.

*Encryption Algorithm Match*
### Encryption Encoding Algorithm Match
To configure the `Service.requestObjectEncryptionEncMatchRequired` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Authorization > Request Object**
2. Go to the `"Encryption Encoding Algorithm Match"` and toggle `Require`.
3. Click Save Changes to apply the updates.

*Encryption Encoding Algorithm Match*
### Requests Without Scope Parameter
To configure the `Service.scopeRequired` flag in the management console:
1. Navigate to **Service Settings > Tokens and Claims > Advanced > Scopes**
2. Go to `"Requests Without Scope Parameter"` and toggle `Reject`.
3. Click Save Changes to apply the updates.

*Requests Without Scope Parameter*
### Request Object Processing
To configure the `Service.traditionalRequestObjectProcessingApplied` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Authorization > Request Object**
2. Go to `"Request Object Processing"` and toggle the `Enable JAR Compatibility` option.
3. Click Save Changes to apply the updates.

*Request Object Processing*
### Return UNAUTHORIZED
To configure the `Service.unauthorizedOnClientConfigSupported` flag in the management console:
1. Navigate to **Service Settings > Endpoints > Advanced > Dynamic Client Registration**
2. Go to `"Client Configuration Error Behavior"` option and toggle `Return UNAUTHORIZED`.
3. Click Save Changes to apply the updates.

*Return UNAUTHORIZED*
## Example Request
Service Configurations for `Service.setUnauthorizedOnClientConfigSupported` flag. The following example uses a POST request [`/api/{serviceId}/service/update`](/api-reference/service-management/update-service).
* Authlete API Request that shows how to update the `unauthorizedOnClientConfigSupported` flag to `true`.
```
curl -v -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/service/update" \
-H 'Content-Type:application/json' \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-d '{
"unauthorizedOnClientConfigSupported": true
}'
```
Set the following before running the request:
* `${SERVICE_API_KEY}` — your service ID
* `${SERVICE_ACCESS_TOKEN}` — a service access token issued from the console
* the API cluster hostname — `us`, `jp`, `eu` or `br`
## Further Reading
* For more in-depth information, see [OAuth/OIDC Implementation Mistakes! How We Overcame Overlooked Details and Breaking Changes in the Specifications](https://darutk.medium.com/oauth-oidc-mistakes-7f3bb909518b)
# Generating Error Response Using Fail API
Source: https://developers.authlete.com/configuration-reference/error-handling-debugging/generating-error-response-using-fail-api
Generating OAuth 2.0 compliant error responses using Authlete's fail APIs to support authorization server in responding to clients with standard error messages.
# Generating error response using "fail" API
*/auth/authorization/fail API*
## Overview
Authlete provides two "fail" APIs; [/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) and [/auth/token/fail](/api-reference/token-endpoint/fail-token-request) to support generating error response from authorization server to client.
These APIs generate OAuth 2.0 compliant error response so that your authorization server can easily answer to clients with error messages in line with the standard.
## How "fail" API works
After user authentication and access authorization process, authorization server may decide not to issue any tokens to a client. In such case the server makes a request to Authlete's "fail" API with reason as a "reason" parameter in the request.
Authlete determines which error message is appropriate and how the message is transferred, and then the API responds authorization server with its result which contains "action" and "responseContent" respectively.
## /auth/authorization/fail API
[/auth/authorization/fail](/api-reference/authorization-endpoint/fail-authorization-request) API is used in the following case:
* Authorization server receives authorization request from client
* The server invokes Authlete's /auth/authorization API to check if the request is valid and gets successful response
* The server does some interaction with user to authenticate and get consent from him/her
* If conditions for granting access are not satisfied, the server is to provide error response instead of tokens
The following table describes overview of "action and "responseContent." In most cases Authlete responds the server with "LOCAITON" as redirect\_uri is determined.
| action | responseContent |
| - | - |
| INTERNAL\_SERVER\_ERROR | Authlete's internal error. It responds authorization server with JSON content which contains description of the error. Authorization server doesn't have to respond its clients with the content as is |
| BAD\_REQUEST | Authorization server is unable to report error to its clients as redirect\_uri is not determined. How the server handles this situation depends on requirement of the server. For example the server may generate error content as HTML and send it back with HTTP status code 400. |
| LOCATION | Authlete generates redirection URI and responds authorization server with it. The server is expected to include it as a value of Location header in HTTP response with status code 302. |
| FORM | Authlete generates HTML which contains JavaScript to do POST some content to a callback location, and responds authorization server with it. The server is expected to include it as body of HTTP response with status code 200. |
## /auth/token/fail API
[/auth/token/fail](/api-reference/authorization-endpoint/fail-authorization-request) API is used for ROPC (Resource Owner Password Credentials) grant tyoe. Authorization server without ROPC capability doesn't have to call this API.
Structure of this API is the same as /auth/authorization/fail API.
## Developing your own error handling without "fail" API
You don't have to use "fail" API if your authorization server can completely determine transport method and content of error. There are no side effects in terms of Authlete's operation.
However you will owe to implement complicated functions in the server such as:
* Keeping redirect\_uri value form authorization request
* Keeping response method like query parameters, fragments etc. and Developing some logic for special cases such as response\_mode=fragment, which designates fragment as response format even if response\_type=code is specified
* Generating HTML form for response\_type=form\_post
* Keeping state value
Using Authlete's "fail" API is always recommended to avoid considering these matters.
## Resources
* OAuth 2.0 and OpenID Connect Implementation in Laravel (Authlete)
* [3.9.3. Delegate authorization request processing to Authlete](https://medium.com/@darutk/oauth-2-0-and-openid-connect-implementation-in-laravel-authlete-4d32802ab335#1f0d)
* > For example, if the client\_id request parameter is not included in an authorization request, /api/auth/authorization API returns a response like “Return ‘400 Bad Request’ to the client application. Use ‘…’ as the error message of the response.” If an authorization request is valid, it returns a response like “The authorization request has no problem. Get the user’s consent to the authorization request interactively. Then, call either /api/auth/authorization/issue API or /api/auth/authorization/fail API according to the user’s decision.”
* [3.11.3. Delegate token request processing to Authlete](https://medium.com/@darutk/oauth-2-0-and-openid-connect-implementation-in-laravel-authlete-4d32802ab335#8774)
* > The resource owner password credentials flow is only an exception. A call of /api/auth/token API does not complete the flow. The implementation of token endpoint has to call either /api/auth/token/issue API or /api/auth/token/fail API additionally.
# Interpreting Authlete's Result Codes
Source: https://developers.authlete.com/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes
Interpreting the meaning behind Authlete's API result codes starting with A.
Authlete APIs return result codes starting with "A".
The third digit from the right has a meaning as is listed below:
0: Success\
1: Error in Authlete.\
2: Error in Service.\
3: Error in Client.\
4: Just logging.\
5: Error in Authlete Front-End.\
6: Error in user operation (Service Owner, Developer, End-User).\
7: Error in external SNS.
Please refer this when building your authorization server(s) with Authlete.
# Suppressing Error Details in responseContent
Source: https://developers.authlete.com/configuration-reference/error-handling-debugging/suppressing-error-details-in-responsecontent
Suppressing error details in responseContent by utilizing the responseContent parameter in Authlete APIs for authorization servers.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/service-setup/suppressing-error-details-in-responsecontent).
# Suppressing Error Details
Some Authlete APIs include a `responseContent` parameter in their responses. This parameter is designed to be used by an authorization server as the response content sent back to a client.
By default, the `responseContent` parameter contains error details. For instance, if you send a request to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API with an empty value for the `parameters` parameter (which is invalid for an authorization request), the API will return the following response:
## Sample Request
```bash theme={null}
curl -s -X POST $apiUrl/auth/authorization \
-H 'Authorization: Bearer ${SERVICE_ACCES_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"parameters":""}'
```
## Sample Response
```bash theme={null}
[...]
"action": "BAD_REQUEST",
"responseContent":
"{\"error\":
\"invalid_request\",
\"error_description\":
\"[A166301] The request does not contain any request parameters.\",
\"error_uri\":
\"https://docs.authlete.com/#A166301\"}",
[...]
```
The `error_description` and `error_uri` parameters in `responseContent` are Authlete's own error details. They are useful for client applications and user agents to resolve problems during development and testing, but may be unnecessary in production.
For production deployment, Authlete recommends disabling both **Include Error Description** and **Include Error URI** settings to suppress detailed error outputs.
## Authlete Configuration
To suppress error details, update the following settings in the **"Error Details"** section of the target Authlete service:
1. Navigate to **Service Settings > Endpoints > Global Settings**.
2. Scroll down to the **Error Details** section.
3. Toggle **Include Error Description** and **Include Error URI** to the "off" position.

If both settings are "off", the response to the request mentioned above will appear as follows:
```bash theme={null}
[...]
"action": "BAD_REQUEST",
"responseContent": "{\"error\":\"invalid_request\"}",
[...]
```
# JWK Set Settings for Authlete Service
Source: https://developers.authlete.com/configuration-reference/key-management/jwk-set-settings-for-an-authlete-service
Setting up a JWK set for an Authlete service.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/discovery-metadata/jwk-set-for-service).
## Preface
This article explains how to register a JWK set for an Authlete service. First, refer to the steps below in order to prepare a JWK set.
***
This article assumes that you have prepared a JWK set in some way. The following example illustrates usage of [mkjwk.org](https://mkjwk.org/) service to generate an ES256 key pair, and parameters specified for it.
* **Key Type:** EC (Elliptic Curve)
* **Curve:** P-256
* **Key Use:** Signing
* **Algorithm:** ES256
* **Key ID:** 1

*Generating a JWK Set using mkjwk*
## Registering the JWK set via the Authlete Console
To register the JWK set you prepared, go to your service settings, inside `Key Management > JWK Set`. There, you will be able to copy the generated "Public and Private Keypair Set" into the `JWK Set Content` field of the settings. Make sure to save your changes by hitting the `Save Changes` button at the bottom of the page.

*JWK Set Content*
Now the JWK set has been registered in the service.
In addition, by adding the authorization server’s endpoint as the JWK set endpoint URI, the `jwks_uri` value is reflected in the authorization server’s metadata document.
## Registering the JWK set via Authlete API
You can also use Authlete's service management APIs to register the JWK set instead of using the Web console. The following example illustrates how to make a request to the [PUT /service/update](/api-reference/service-management/update-service) API to specify the JWK set as a value of "jwks" key. Updating service settings requires an organization access token.
### 1. Get configuration data about the target service
Use the [GET /service/get](/api-reference/service-management/get-service) API to retrieve the configuration data of the service.
```sh theme={null}
curl -s {Authlete API}/service/get/{Service API Key} \
-H Authorization: Bearer {OrganizationAccessToken} \
-H 'Content-type: application/json' \
> service.json
```
### 2. Add a new JWK set to the data
Add the following key/value as a JWK set to the JSON formatted configuration data retrieved above and save as updated-service.json.
```json theme={null}
{
"jwks": {
"keys": [
{
"kty": "EC",
"d": "eb4BggIO87SUjzP1M56MeXj0NQajWBwpwiDq8yoL5n4",
"use": "sig",
"crv": "P-256",
"kid": "2019-07-25_02",
"x": "f8a6jovcRTNLDWi3_c62YcW_3ZN-GH1RkiVOZgSgIYI",
"y": "EB3R8W12a3tgZfNer1RP0DizT3qpRybGw_krfsE0JzY",
"alg": "ES256"
}
]
}
}
```
If you would like to register multiple JWKs, specify which one of the JWKs for signing. For example, if you want a JWK identified by key ID `"kid":"2019-07-25_02"` to be used for signing ID tokens, you should add the following key/value:
```json theme={null}
"idTokenSignatureKeyId": "2019-07-25_02"
```
(see [Service Management API Reference](/api-reference/service-management/get-service) for details on `idTokenSignatureKeyId`)
### 3. Update the service with the new configuration data
Use [PUT /service/update](/api-reference/service-management/update-service) API to put the new configuration data that includes the JWK set.
```sh theme={null}
cat updated-service.json | \
curl -s -X PUT {Authlete API}/service/update/{Service API Key} \
-H 'Authorization: Bearer {OrganizationAccessToken}' \
-H 'Content-type: application/json' \
-d @-
```
The new "jwks" should be included in the output as follows.
```json theme={null}
{
// ... other service properties ...
"jwks": "{\"keys\":[
{
\"kty\":\"EC\",
\"d\":\"eb4BggIO87SUjzP1M56MeXj0NQajWBwpwiDq8yoL5n4\",
\"use\":\"sig\",
\"crv\":\"P-256\",
\"kid\":\"2019-07-25_02\",
\"x\":\"f8a6jovcRTNLDWi3_c62YcW_3ZN-GH1RkiVOZgSgIYI\",
\"y\":\"EB3R8W12a3tgZfNer1RP0DizT3qpRybGw_krfsE0JzY\",
\"alg\":\"ES256\"
}
]}",
// ... other service properties ...
}
```
## Key Rotation
To perform key rotation without downtime, it is common to update keys using the following procedure.
1. Add a new signing key to `jwks_uri` (resulting in a state where multiple keys are included)
2. Wait until the `jwks_uri` cache on clients and RPs is invalidated and multiple keys are recognized
3. Switch the signing key to the new key
4. Remove the old signing key
For authorization servers using Authlete, steps 1 and 4 correspond to updating the contents of the service’s JWK set (`jwks`), and step 3 corresponds to updating settings such as the service’s ID token signature key ID (`idTokenSignatureKeyId`).
# JWK Set Settings for OAuth / OIDC client
Source: https://developers.authlete.com/configuration-reference/key-management/jwk-set-settings-for-an-oauth-oidc-client
Technical information for registering a JWK set to settings of an OAuth / OIDC client in Authlete for signature verification and message encryption.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/discovery-metadata/jwk-set-for-client).
# Configuring JWK Set for an OAuth/OIDC Client
## Preface
In order to verify a signature of request objects coming from a client, or to provide encrypted ID tokens to a client, you have to register the client's public key to Authlete for signature verification and/or message encryption.
This article explains how to register a JWK set in the settings of an OAuth/OIDC client registered with an Authlete service.
## Preparing a JWK set
> Instruction in this section is for illustrative purposes. Do not apply the following JWK set to your production environment.
An administrator of the OAuth / OIDC client is to prepare a JWK set including a public key. **Note that the key set must not include a private key.**
The following example illustrates usage of [mkjwk.org](https://mkjwk.org/)
tool to generate a key pair for encryption, and parameters specified for it.
| Item | Value |
| - | - |
| Key Type | **EC (Elliptic Curve)** |
| Curve | **P-256** |
| Key Use | **Encryption** |
| Algorithm | **(none)** |
| Key ID | **SHA-256** |
By clicking “Generate” button, the tool shows the generated keys in various formats. We will use “Public and Private Keypair Set” in this example.
*Generating "Public and Private Keypair Set"*
The original JWK set is as follows.
```json theme={null}
{
"keys": [
{
"kty": "EC",
"d": "4AEnTq3H8gcIutIoJCCZuv9GgWdKRaoJIXQkdM8r0UA",
"use": "enc",
"crv": "P-256",
"kid": "_agec7UaYVN4c3RZQJQhUuR6nFSnqEXywv3QaIfFRFk",
"x": "ilLNQ-Lcp_t5DBs9puJVI3JhwqlMndTILjkBrNd3Dsc",
"y": "3Uy7NIHilkOWviGXMRIl2ZUE4L7Mc8ub4VhosE3l8t8"
}
]
}
```
Remove the private key below.
```plaintext theme={null}
"d": "4AEnTq3H8gcIutIoJCCZuv9GgWdKRaoJIXQkdM8r0UA",
```
The result\*\* \*\*is as follows.
```json theme={null}
{
"keys": [
{
"kty": "EC",
"use": "enc",
"crv": "P-256",
"kid": "_agec7UaYVN4c3RZQJQhUuR6nFSnqEXywv3QaIfFRFk",
"x": "ilLNQ-Lcp_t5DBs9puJVI3JhwqlMndTILjkBrNd3Dsc",
"y": "3Uy7NIHilkOWviGXMRIl2ZUE4L7Mc8ub4VhosE3l8t8"
}
]
}
```
This JSON document is to be used as a JWK set of the client's public key.
## Registering a JWK set
To add a JWK set for a client in the Authlete Management Console, follow these steps:
1. Log in to the [Authlete Management Console](https://console.authlete.com).
2. Navigate to **Client Settings** for the desired client.
3. Select the **Key Management > JWK Set** tab from the left-hand menu.
4. Under the **JWK Set Content** section, paste your JSON Web Key Set (JWK Set) into the input field.
5. Click **Save Changes** to apply the configuration.

Now the JWK set has been registered for the OAuth / OIDC client.
# Publishing JWK Set
Source: https://developers.authlete.com/configuration-reference/metadata/publishing-a-jwk-set
Procedures for publishing public keys used for ID token signature verification and Request Object encryption via jwks_uri
## Introduction
This article explains the procedure for publishing a JWK Set using the Authlete API by an OpenID Provider (OP) or an Authorization Server (AS).
## Publishing a JWK Set
A Relying Party (RP) requires the OP’s public keys in order to verify the signature of ID tokens obtained from the OP and to encrypt Request Objects presented to the OP. One way for an OP to provide these public keys to an RP in the form of JWKs is defined in [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html).\
Similarly, in OAuth 2.0, the method by which an AS provides such information to clients is specified in [RFC 8414 - OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414).
For OPs/ASs that support these specifications, the following implementations are required:
* Configuration of `jwks_uri` (JWK Set Endpoint URI) in the server metadata
* Publication of a JWK Set (a set of multiple JWKs) via the JWK Set EP
## Implementation Using Authlete
### Configuring `jwks_uri` in Server Metadata
When the `JWK Set Content` and the `JWK Set Endpoint URI`are registered with an Authlete service, Authlete automatically generates server metadata that includes `jwks_uri`.\
For details on the registration procedure, refer to [Publishing JWK Set](/configuration-reference/metadata/publishing-a-jwk-set).
### Publishing a JWK Set via the JWK Set Endpoint
When implementing a JWK Set endpoint in an OP/AS, Authlete’s `/service/jwks/get` API can be used as the backend.
## Example Implementation
In this example, assume that a JWK Set containing an ES256 key has been configured in advance in the Authlete service as the `JWK Set Content` and that `https://as.example.com/jwks` has been configured as the `JWK Set Endpoint URI.`
An RP that implements OpenID Connect Discovery 1.0, or a client that implements RFC 8414, first discovers and retrieves the server metadata of the OP/AS based on these specifications. Next, it sends a request to the JWK Set endpoint indicated by the `jwks_uri` included in the metadata.
Upon receiving this request, the OP/AS invokes Authlete’s `/service/jwks/get` API on the backend as part of the JWK Set endpoint processing.\
Below is an example cURL command used by the OP/AS to call the Authlete API.
```bash theme={null}
curl -v {Authlete API}/api/{Service ID}/service/jwks/get?pretty=true \
-H 'Authorization: Bearer {Service Access Token}'
```
If a JWK Set has been properly registered, Authlete returns the JWK Set registered with the service in JSON format.
```json theme={null}
{
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "1",
"x": "DQ5ADe7viHfsrnrTy4oXrguWgSbuk-qLGR6bhWSg-gw",
"y": "HeW4HeETjzyJbmf3FgoLWUtCHzgfRB8-gVYn4lt6XbA",
"alg": "ES256"
}
]
}
```
The OP/AS returns this JSON to the RP or client as the response from the JWK Set endpoint.
## Using Caching
Depending on the update interval and frequency of the JWK Set, implementing a mechanism to cache the JWK Set on the RP/client side or the OP/AS side can indirectly or directly reduce the number of calls to the Authlete API. This can help reduce latency.
Taking into account the key rotation policy and rotation period, consider implementing an appropriate caching strategy.
## Related Articles
* [Publishing OpenID Connect Discovery Metadata](/configuration-reference/metadata/publishing-metadata)
# Publishing OpenID Provider Metadata and OAuth 2.0 Authorization Server Metadata
Source: https://developers.authlete.com/configuration-reference/metadata/publishing-metadata
## Introduction
This article explains how an OpenID Provider (OP) or an Authorization Server (AS) can publish server metadata using the Authlete API.
## Generating Authorization Server Metadata with Authlete
In OAuth and OpenID Connect, metadata is defined so that clients and relying parties (RPs) can obtain information about an authorization server.\
Specifically, the relevant specifications are [OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) and [RFC 8414 OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414).
By using the [/service/configuration](/api-reference/service-management/get-service-configuration) API provided by Authlete, you can generate metadata that conforms to these two specifications.
### OpenID Connect Discovery
An authorization server that supports OpenID Connect Discovery accepts GET requests at `https://as.example.com/.well-known/openid-configuration`, assuming `https://as.example.com` is used as the `issuer`.
In your authorization server implementation, you call Authlete’s [/service/configuration](/api-reference/service-management/get-service-configuration) API and return its response (optionally filtered or transformed) as JSON. An example call to [/service/configuration](/api-reference/service-management/get-service-configuration) is shown below.
```sh theme={null}
curl --request GET \
--url 'https://jp.authlete.com/api//service/configuration?pretty=true' \
--header 'authorization: Bearer ' \
--header 'content-type: application/json'
```
The response is returned directly as a JSON string representing the metadata.
```json theme={null}
{
"issuer": "https://as.example.com",
"authorization_endpoint": "https://as.example.com/api/authorization",
"prompt_values_supported": [
"none",
"login",
"consent",
"select_account",
"create"
],
"token_endpoint": "https://as.example.com/api/token",
"userinfo_endpoint": "https://as.example.com/api/userinfo",
"jwks_uri": "https://as.example.com/api/jwks",
"scopes_supported": [
"address",
"email",
"openid",
"offline_access",
"phone",
"profile",
"edit_payment_methods",
"grant_management_query",
"grant_management_revoke"
],
...
}
```
### OAuth 2.0 Authorization Server Metadata
For OAuth 2.0 clients, you can publish **OAuth Server Metadata** at `https://as.example.com/.well-known/oauth-authorization-server` in accordance with RFC 8414.
As with OIDC Discovery, the response from the [/service/configuration](/api-reference/service-management/get-service-configuration) API can also be used for this endpoint.
## Metadata Caching and Operational Considerations
For both the OIDC Discovery document and OAuth Server Metadata, it is acceptable to implement caching on the authorization server or client side. When doing so, consider factors such as key rotation intervals and design an appropriate caching strategy.
## Metadata Items Configurable via the Management Console
Some items configured in the Authlete Management Console are reflected in the response from the [/service/configuration](/api-reference/service-management/get-service-configuration) API. For example, the issuer identifier, various endpoint URLs, and supported authorization types configured in the console are automatically included in the API response.
For details on the specific configuration items included in the metadata, please refer to [Service Configuration](https://kb.authlete.com/ja/s/operations/a/service-settings).
## Selecting Which Metadata to Publish
Information returned by the [/service/configuration](/api-reference/service-management/get-service-configuration) API can be selectively included or excluded by the authorization server. For example, `id_token_signing_alg_values_supported` may include multiple algorithms supported by Authlete, but you may overwrite this value to publish only the algorithms intended for your authorization server.
### Customizing Authlete Responses with JSON Patch
Authlete allows customization of the [/service/configuration](/api-reference/service-management/get-service-configuration) API response based on [RFC 6902 JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902).
\* **`Example: Restrict id_token_signing_alg_values_supported to ["RS256"] only`**
```bash theme={null}
curl --request GET \
--url 'https://jp.authlete.com/api//service/configuration?patch=%5B%7B%22op%22%3A%20%22replace%22%2C%20%22path%22%3A%20%22%2Fid_token_signing_alg_values_supported%22%2C%20%22value%22%3A%20%5B%22RS256%22%5D%7D%5D' \
--header 'authorization: Bearer ' \
--header 'content-type: application/json'
```
You can also apply your own custom logic to further modify the JSON after receiving it in your authorization server.
# How to Provide User Attributes
Source: https://developers.authlete.com/configuration-reference/security/how-to-provide-user-attributes
Technical Information about providing user attributes in Authlete in accordance with OpenID Connect.
## Preface
In order to provide user attributes with Authlete, in accordance with OpenID Connect, you have to consider requirements on how identity provider (OpenID Provider; OP) should handle the information and choose the appropriate method.
***
## Methods for providing user attributes
Authlete supports the following three methods for providing user attributes:\
\*EP: Endpoint, RP: Relying Party
| Method | Providing ID Token from Token EP | Providing ID Token from Authorization EP | Providing response from Userinfo EP |
| - | - | - | - |
| Authentication request parameters | response\_type includes "code" and scope does "openid" | response\_type includes "id\_token" | N/A |
| Authlete API | [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) | [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) | [/auth/userinfo/issue](/api-reference/userinfo-endpoint/issue-userinfo-response) |
| How it works | Once receiving an authentication request from an RP, an OP authenticates a user, prepares a request including user attributes and ask Authlete to create a response to the client by sending the request data.Authlete generates both an ID token and a code, responds to the server with the code and stores the token to Authlete's database. The token is to be provided from token EP on receiving the valid code | Once receiving an authentication request from an RP, an OP authenticates a user, prepares a request including user attributes and ask Authlete to create a response to the client by sending the request data.Authlete generates an ID token and responds to the server with the token | Once receiving a request at UserInfo EP from an RP, an OP prepares a request including user attributes and ask Authlete to create a response to the client by sending the request data. Authlete generates UserInfo response including user attributes |
| Does Authlete keep user attributes? | Yes. Authlete encrypts the ID Token which contains user attributes and stores it temporarily. The token data will be kept until cleanup process for expired authorization codes is completed | No. Authlete doesn't keep any user attributes | No. Authlete doesn't keep any user attributes |
| Authorization server's tasks | Implement both authorization EP and Token EP and use Authlete as a backend for each EP | Implement authorization EP and use Authlete as a backend for the EP | Implement Userinfo EP as well as other EPs as described on the left columns, and use Authlete as a backend for the EPs |
*Providing ID Token from Token EP*
*Providing ID Token from Authorization EP*
*Providing response from Userinfo EP*
***
## How to choose the right method
* **Authlete never keeps user attributes for a long time.**
* In the case where Authlete provides the data from authorization EP or UserInfo EP, the data is removed on memory right after responded.
* Even if Authlete provides the data via token EP (i.e. using authorization code), the data is removed after completion of cleanup process for expired authorization codes.
* **If you have some security requirement which prohibits Authlete from keeping user attributes** even if they are encrypted and to be removed in a short period of time, using authorization EP or Userinfo EP is the solution.
* **If sending user attributes to Authlete API is not permitted** due to strict security reasons, consider the following options:
* **Create a UserInfo response by OP itself**. On providing user attributes from UserInfo EP, an OP asks Authlete to analyze an access token and then creates a UserInfo response. The actual sequence is as follows:
* Send the access token to [/auth/userinfo](/api-reference/userinfo-endpoint/process-userinfo-request) API to obtain a list of user claims that the RP wants.
* Create a UserInfo response defined in [the specification](https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse) , without using [/auth/userinfo/issue](/api-reference/userinfo-endpoint/issue-userinfo-response) API.
* **Deploying Authlete's on-premises package** so that you can manage the Authlete service.
* The other thing to be considered is operational requirement. If it recommends **not to support implicit flows or Userinfo EP**, the appropriate method would be going to issue authorization code from authorization EP and provide ID Token from token EP.
# Adding Claims to ID Token
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/adding-claims-to-an-id-token
Adds arbitrary claims to an ID token using Authlete's API with specific claim values such as name and email.
## Preface
This article explains how to add arbitrary claims to an ID token.
## How to add claims to an ID token
Authlete's [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)
API is the method for adding claims. In this article, the following claims are to be added.
| Item | Value |
| - | - |
| "name" | "Test User" |
| "email" | "[testuser01@example.com](mailto:testuser01@example.com)" |
| "email\_verified" | true |
claims is the paramater to add claims. The request will be constructed as follows. (folded for readability)
```shell highlight={5-7} theme={null}
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization/issue" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{ "ticket": "", "subject": "testuser01",
"claims": "{\"name\": \"Test User\",
\"email\": \"testuser01@example.com\",
\"email_verified\": true}"
}'
```
Authlete will issue an ID token including the claims in accordance with the request above. The payload part of the ID token is as follows:
```json highlight={2-4} theme={null}
{
"name": "Test User",
"email": "testuser01@example.com",
"email_verified": true,
"iss": "https://as.example.com",
"sub": "testuser01",
"aud": [
"12898884596863"
],
"exp": 1559137301,
"iat": 1559050901,
"nonce": "n-0S6_WzA2Mj"
}
```
## How to specify a custom value to "sub" claim
By default, Authlete uses the value of the "subject" parameter for "sub" claim in an ID token. If you want to specify your own value for this claim, use the special "sub" parameter instead of the "claims" parameter when making an API request. An example is shown below:
```shell highlight={8} theme={null}
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization/issue" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{ "ticket": "", "subject": "testuser01",
"claims": "{\"name\": \"Test User\",
\"email\": \"testuser01@example.com\",
\"email_verified\": true}",
"sub": "1234567890" }'
```
The resulted ID token would include the custom "sub" value as follows:
```json highlight={6} theme={null}
{
"name": "Test User",
"email": "testuser01@example.com",
"email_verified": true,
"iss": "https://as.example.com",
"sub": "1234567890",
"aud": [
"126863743267133"
],
"exp": 1688804473,
"iat": 1688718073,
"nonce": "n-0S6_WzA2Mj"
}
```
# Authlete's Policy on Sweeping Unused Tokens
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/authletes-policy-on-sweeping-unused-tokens
Authlete removes unused access tokens and refresh tokens after a certain period regardless of their expiration time set by the service owner console.
Authlete removes access tokens and refresh tokens that have been unused in a certain period, 90 days. "Use" in this context is read/update operations to the tokens by Authlete to process requests such as token introspection, updating scopes.
It is not affected by the token’s expiration time that is defined in the service owner console. For example, a refresh token with a very long expiration time is subject to be removed from the token database after 90 days from the last usage.
Additionally, expired access tokens and refresh tokens are also deleted, regardless of their usage status.
If a token that has not been used for 90 days is used, Authlete will send a response with "\[A057302] The access token does not exist" because the token is already deleted from the database.
# Changing Signing Key for ID Tokens
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/changing-signing-key-for-id-tokens
Guide on changing a signing key for ID tokens by configuring both a service of Authlete and a client registered to the service.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/openid-connect/changing-signing-key-for-id-token).
## Overview
This article explains an example on changing a signing key for ID tokens. In order to have Authlete to sign an ID token with the new key, you may have to configure both a service of Authlete and a client registered to the service.
## Service settings
In the Authlete console, go to your service settings, under `Key Management > JWK Set`. You will then need to register a JWK set document in the `JWK Set Content` field.
You can refer to the following article for instructions on how to generate a JWK :
* [JWK set settings for an Authlete service](/configuration-reference/key-management/jwk-set-settings-for-an-authlete-service)
After the registration, select the value of "kid" of this keypair set ("1" in this example) for the `ID Token Signature Key ID` field.

*ID Token Signature Key ID*
## Client settings
In the Authlete console, go to your client settings, under `Tokens and Claims > ID Token`. You will then need to select the `ES256` parameter in the `ID Token Signature Algorithm` field, in order for the Authlete service to issue an ID token signed with the new key.

*ID Token Signature Algorithm*
With the settings above, Authlete will be using the ES256 key (identified by "kid=1") to sign ID tokens for the client.
# Changing Token Duration
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/changing-token-duration
Explanation on how changing token duration settings in Authlete affects access tokens and refresh tokens.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/token-lifecycle-policies/changing-token-durations).
## Overview
When you update the "token duration" settings on an Authlete service that has already been issuing tokens, Authlete will:
* Retain the original duration settings for tokens issued before the change.
* Apply the new duration settings to tokens issued after the update.
This article explains how the "token duration" settings affect access tokens and refresh tokens.
## Access Tokens
The new duration settings are to be applied on (re)issuing access tokens on token requests with various grant types including refresh token grant.\\
## Refresh Tokens
The duration change may affect refresh tokens based on "\*\*Refresh Token Rotation
\*\*" settings.
To configure Refresh Tokens settings:
1. Navigate to **Service Settings > Tokens and Claims > Refresh Tokens**
2. Enable your desired Refresh Token Rotation options.
3. Click `Save Changes` to apply the updates.

### Enable Token Rotation
***
The `Enable Token Rotation `Configuration item controls whether to keep a refresh token valid after its use or invalidate the used refresh token and issue a new one.
* If "**Enable Token Rotation**" is `enabled`
* The new duration won't be effective until the existing refresh token is expired and reissued.\\
* If "**Enable Token Rotation**" is `disabled`
* The new duration is effective for a new refresh token that is to be issued along with a new access token on refresh token grant (using the old refresh token).
### Enable Duration Takeover
* If "**Enable Duration Takeover**" is `enabled`
* The remaining duration of a used refresh token is transferred to the newly issued one. This setting has no effect when Enable Token Rotation is on.
### See Also
[Refresh tokens after being used](/configuration-reference/tokens-and-claims/refresh-tokens-after-being-used)
# Custom Header Claims in ID Tokens
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/custom-header-claims-in-id-tokens
Adding custom claims to ID tokens by configuring Authlete to include them in the JWS header available in version 2.2 and later.
## Preface
This article describes on how to configure Authlete to add custom claims to a JWS header of ID tokens.
> This feature is available in Authlete version 2.2 and later.
## Specifying custom claims
An authorization server can specify a set of custom claims to be included in a header of ID tokens on making requests to the following Authlete APIs. **idtHeaderParams** is the request parameter to pass the claims in JSON format.
* [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)
* [/backchannel/authentication/complete](/api-reference/ciba/complete-backchannel-authentication)
* [/device/complete](/api-reference/device-flow/complete-device-authorization)
Authlete creates an ID token including the custom claims in its header and sends it back to the authorization server.
***
## Examples
This section shows example instructions on how to create ID tokens that includes custom claims in authorization code flow.
### Preparing custom claims
Let's assume that the following custom claims are to be included in a header of ID tokens.
| Key | Value |
| - | - |
| typ | JWT |
| extra\_key | extra\_value |
A JSON formatted value to be specified to an idtHeaderParams parameter would be as follows.
```
{
"typ": "JWT",
"extra_key": "extra_value"
}
```
### Processing an authentication request
A client (OIDC relying party) makes an authentication request that includes scope=openid and response\_type=code. The request is sent to an authorization server (OIDC identity provider) via a user agent.
The authorizations server then forwards content of the authentication request to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API and receives a response including a value of "ticket" to be used in the next step. (details omitted)
API and receives a value of ticket to be used in the next request to Authlete. (details omitted)
The authorization server authenticates an end user and asks consent as needed, and make a request to Authlete's [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)
API. In this request, the server uses the idtHeaderParams parameter to specify the custom claims.
The following is an example using curl. (folded for readability)
* Request
```shell highlight={6-8} theme={null}
curl -s -X POST .../auth/authorization/issue
-u $apiKey:$apiSecret
-H 'Content-type: application/json'
-d '{"subject":"...",
"ticket": "...",
"idtHeaderParams":
"{\"typ\":\"JWT\",
\"extra_key\":\"extra_value\"}" }'
```
* Response
```
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"accessTokenDuration": 0,
"accessTokenExpiresAt": 0,
"action": "LOCATION",
"authorizationCode": "eOEh...3eSc",
"responseContent": "https://...?code=eOEh...3eSc"
}
```
On receiving the response above, the authorizations server sends back an authorization code (eOEh...3eSc) to the client via the user agent (Web browser).
### Processing a token request
On receiving the authorization code, the client sends a token request including the code to the authorization server. The server forwards content of the token request to Authlete's [/auth/token](/api-reference/token-endpoint/process-token-request)
API and receives a token response.
The following is an example using curl. (folded for readability)
* Request
```
curl -s -X POST .../auth/token \
-u $apiKey:$apiSecret \
-H 'Content-type: application/json' \
-d '{"clientId":"...",
"clientSecret":"...",
"parameters":
"redirect_uri=https://...
&grant_type=authorization_code
&code=eOEh...3eSc"}'
```
* Response
```json highlight={9-16,22-29} theme={null}
{
"resultCode": "A050001",
"resultMessage":
"[A050001] The token request (grant_type=authorization_code) was
processed successfully.",
"accessToken": "9Myh...sYMs",
"action": "OK",
"grantType": "AUTHORIZATION_CODE",
"idToken":
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImV4dHJ
hX2tleSI6ImV4dHJhX3ZhbHVlIn0.
eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjI4MzA
0NTE3OTYwODU5NDAiXSwiaXNzIjoiaHR0cHM6Ly9hcy
5leGFtcGxlLmNvbSIsImV4cCI6MTU5OTcyNjA0MCwia
WF0IjoxNTk5NjM5NjQwfQ.
-jo-A-SXjP4JlBSc24vKkO6FVf3MQJ6eN-XzPAraiZ4",
"refreshToken": "SlEl...cWoY",
"responseContent":
"{\"access_token\":\"9Myh...sYMs\",
\"refresh_token\":\"lEl...cWoY\",
\"scope\":\"openid\",
\"id_token\":
\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImV4dHJ
hX2tleSI6ImV4dHJhX3ZhbHVlIn0.
eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjI4MzA
0NTE3OTYwODU5NDAiXSwiaXNzIjoiaHR0cHM6Ly9hcy
5leGFtcGxlLmNvbSIsImV4cCI6MTU5OTcyNjA0MCwia
WF0IjoxNTk5NjM5NjQwfQ.
-jo-A-SXjP4JlBSc24vKkO6FVf3MQJ6eN-XzPAraiZ4\",
\"token_type\":\"Bearer\",
\"expires_in\":86400}",
"scopes": [
"openid"
],
...
}
```
Authlete has added the custom claims in a JWS header of the ID token (eyJh...aiZ4) in this response. You can find them by decoding the header part as follows.
```json highlight={3-4} theme={null}
{
"alg": "HS256",
"typ": "JWT",
"extra_key": "extra_value"
}
```
# Enabling Single Access Token
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/enabling-single-access-token-per-subject
Technical information on enabling a single access token per subject in Authlete to ensure that only the newest access token is valid for each user.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/token-issuance-formats/single-access-token-per-subject).
# Single Access Token Per Subject
By default, Authlete issues a new access token each time the same user grants an authorization request from a client. This happens when the authorization server calls Authlete's [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API with the same `subject` value.
However, by default, Authlete does not invalidate previously issued access tokens associated with the same user. As a result, the client may hold multiple valid access tokens for the same user at the same time.
## Runtime Behavior
In some environment, authorization server is required not to issue multiple valid access tokens in such a way. For Authlete to achieve this, it provides **Single Access Token Per Subject** option.
## Authlete Configuration
To enable **Single Access Token Per Subject**, follow these steps:
1. Log in to the [Authlete Management Console](https://console.authlete.com).
2. Navigate to **Service Settings > Tokens and Claims > Access Tokens**.
3. Locate the **Enable Single Token Per Subject** option under the **General** section.
4. Toggle the switch to enable this option (as shown in the screenshot below).
5. Click **Save Changes** to apply the configuration.

Once enabled, Authlete will automatically invalidate any previously issued access tokens and their associated refresh tokens for the same subject when issuing a new access token.
# Generating Encrypted ID Tokens
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/generating-encrypted-id-tokens
Generating encrypted ID tokens for a particular client using Authlete and registering a JWK set for encryption.
## Preface
This article describes an example on how to configure Authlete to generate encrypted ID tokens for a particular client.
***
## Preparing and registering a JWK set
Prepare and register a JWK set to be used for encrypting ID tokens. See the following article for instructions.
* [JWK Set Settings for OAuth / OIDC Client](/configuration-reference/key-management/jwk-set-settings-for-an-oauth-oidc-client)
In this example, the following JWK set is registered.
```
{
"keys": [
{
"kty": "EC",
"use": "enc",
"crv": "P-256",
"kid": "_agec7UaYVN4c3RZQJQhUuR6nFSnqEXywv3QaIfFRFk",
"x": "ilLNQ-Lcp_t5DBs9puJVI3JhwqlMndTILjkBrNd3Dsc",
"y": "3Uy7NIHilkOWviGXMRIl2ZUE4L7Mc8ub4VhosE3l8t8"
}
]
}
```

***
## Configuring ID token encryption
Choose encryption algorithm of ID token for the client. See [Client Settings - JWK Set](/configuration-reference/key-management/jwk-set-settings-for-an-oauth-oidc-client)
for details.
In this example, the following settings in accordance with the registered public key are specified:
| Item | Value |
| - | - |
| ID Token Encryption Algorithm | **ECDH\_ES** |
| ID Token Encryption Encoding Algorithm | **A128CBC\_HS256** |
*Settings for ID token encryption*
***
## Generating an encrypted ID token
With the settings above, Authlete will be encrypting ID tokens for the client, like the following value:
```
eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJDeVU3XzdMNlRaeHdmZzhBNkRUNUxMSz
1ZWlBSE1yTzJyYWt5WEY0TUFZIiwieSI6InZ6MkJZd1k1WmJFUkV2OGxTcWNuREpQVkdQMExyWXpUS2NiZUR2aTVKQlUifSw
ia2lkIjoiX2FnZWM3VWFZVk40YzNSWlFKUWhVdVI2bkZTbnFFWHl3djNRYUlmRlJGayIsImVuYyI6IkExMjhDQkMtSFMyNTY
iLCJhbGciOiJFQ0RILUVTIn0..e9rmLbmTkuaUL7AdUB9umw.-o4aia0O6FUarqN3Owssu6ko4v9YqjMJXm00Sj-w7oH7YjmgZDR
j7omoXSnmU19wYFhC70yzv8U2B0pXNxLqVZOv4vQchwslP6Ms55cmgvenCHcIbsXFirb8we-6Z0EfKiKmLCy0NU8kFE0fQ-6y9
n91WI9hGPVbw968FtA2y7x2JXGv93nPryshCp4HJz2HXQeb5F18RSsCAkY5nE7-GqD81DnmG6-rdVoOBAGM7LK_lc8gIhSozmNn7
8cFtfQrNn6S0VFWM2e8SyukQtdGk3um3mZAjRdDNtsACkztQJo.5S7YimrpLKuWnRM0hwdJzweyJlcGsiOnsia3R5IjoiRUMiLCJj
cnYiOiJQLTI1NiIsIngiOiIzUHNVT0lj
```
***
## Decrypting the ID token
On receiving the ID token above, the client will be decrypting it using the client's private key. The following is a sample JWK set for the decryption:
```
{
"Keys": [
{
"kty": "EC",
"d": "4AEnTq3H8gcIutIoJCCZuv9GgWdKRaoJIXQkdM8r0UA",
"use": "enc",
"crv": "P-256",
"kid": "_agec7UaYVN4c3RZQJQhUuR6nFSnqEXywv3QaIfFRFk",
"x": "ilLNQ-Lcp_t5DBs9puJVI3JhwqlMndTILjkBrNd3Dsc",
"y": "3Uy7NIHilkOWviGXMRIl2ZUE4L7Mc8ub4VhosE3l8t8",
"typ": "ECDH_ES"
}
]
}
```
Here is an example using [José](https://github.com/latchset/jose) to decrypt and other commands to extract a payload part:
```
jose jwe dec -i -k \
| cut -d"." -f2 \
| base64 --decode \
| jq
{
"sub": "testuser01",
"aud": [
"17201083166161"
],
"iss": "https://authlete.com",
"exp": 1595921860,
"iat": 1595835460
}
```
# Getting a List of Issued Access Tokens
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/getting-a-list-of-issued-access-tokens
Getting a list of access tokens issued by Authlete's API.
## Overview
This is about how to get a list of access tokens issued.
Authlete provides an API that enables to get a list of client applications that an end-user (resource owner) has authorized. This functionality is available at Authlete's [/auth/token/get/list](/api-reference/token-operations/list-issued-tokens) API.
This function is available only for Enterprise Plan customers.
### Definition
```
GET /auth/token/get/list?
clientIdentifier={clientIdentifier}
&subject={subject}
&start={start}
&end={end}
```
### Request Parameters
| parameter | required | description | default |
| - | - | - | - |
| clientIdentifier | NO | Client Identifier (client ID or client ID alias). Use this parameter when you want to get a list of tokens that have been issued to a specific client. | null |
| subject | NO | Unique user ID.Use this parameter when you want to get a list of tokens that have been issued to a specific user. | null |
| start | NO | Start index of search results (inclusive). | 0 |
| end | NO | End index of search results (exclusive). | 20 |
> Currently, the maximum number of tokens in a list is 20. Authlete server will return an error when you request more than 20 (= "end" - "start" > 20).
### Response Parameters
| parameter | description |
| - | - |
| accessTokens | An array of access tokens. |
| start | Start index of search results (inclusive). |
| end | End index of search results (exclusive). |
| totalCount | The total number of access tokens that meet the conditions. |
\*\*\
Access Token Infromation
\*\*
| parameter | description |
| - | - |
| accessTokenExpiresAt | The timestamp at which the access token will expire. |
| accessTokenHash | The hash of the access token. |
| clientId | The ID of the client associated with the access token. |
| createdAt | The timestamp at which the access token was first created. |
| grantType | The grant type of the access token when the access token was created. |
| lastRefreshedAt | The timestamp at which the access token was last refreshed using the refresh token. |
| refreshTokenExpiresAt | The timestamp at which the refresh token will expire. |
| refreshTokenHash | The hash of the refresh token. |
| scopes | The scopes associated with the access token. |
| subject | The subject (= unique user ID) associated with the access token. |
\*\*
Response Example\
\*\*
```
{
"accessTokens": [
{
"accessTokenExpiresAt": 1527583747000,
"accessTokenHash": "kjjduowWXMPiHt1AjxbgkqWANOsVWQfKYCyFO76q1MU",
"clientId": 11867130158,
"createdAt": 1527497347000,
"grantType": "AUTHORIZATION_CODE",
"lastRefreshedAt": 0,
"refreshTokenExpiresAt": 1528361347000,
"refreshTokenHash": "hCkIkuS6py0drpe5XOybw0MxkgsYNOk2q3dtilLiI64",
"scopes": ["openid"],
"subject": "john"
},
{...},
{...},
{...},
{...}
],
"end": 5,
"start": 0,
"totalCount": 15
}
```
# How Authlete Determines Token Duration
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/how-authlete-determines-token-duration
Technical information on how Authlete determines token duration based on Authlete 3.0.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/token-lifecycle-policies/how-to-calculate-token-duration).
## Overview
Authlete 3.0 calculates the effective token duration by evaluating configurations at multiple levels. The final duration is determined based on the following priority order:
1. **Service Level**: If no other duration is specified, the service-level token duration is used.
2. **Scope Level**: If the token duration is configured for any of the requested scopes, it overrides the service-level duration. Authlete uses the shortest duration among all requested scopes.
3. **Client Level**: If a token duration is set for the requesting client, it overrides both the service-level and scope-level durations.
This ensures that the token's lifetime reflects the most granular and specific configuration available.
***
## Token Duration Calculation Process
1. **Service Level**:
* Authlete starts with the token duration configured at the service level.
2. **Scope Level**:
* If the request includes scopes with configured durations, Authlete selects the shortest duration among those scopes.
3. **Client Level**:
* If the requesting client has a configured token duration, it takes precedence over both service-level and scope-level durations.
***
### Example
| Level | Token Duration (seconds) |
| - | - |
| Service | 86,400 |
| Scope (`read`) | 3,600 |
| Scope (`write`) | 600 |
| Client | 300 |
**Scenario 1**: Request without scopes → Token duration is `86,400` (service level).\
**Scenario 2**: Request with `read` scope → Token duration is `3,600` (scope level).\
**Scenario 3**: Request with `write` scope → Token duration is `600` (scope level).\
**Scenario 4**: Request with `read` and `write` scopes → Token duration is `600` (shortest scope-level duration).\
**Scenario 5**: Request by a client with configured duration → Token duration is `300` (client level overrides all).
***
## See Also
* [Token Duration Per Scope](/configuration-reference/tokens-and-claims/token-duration-per-scope)
* [Token Duration Per Client](/configuration-reference/tokens-and-claims/token-duration-per-client)
# Adding Extra Properties to Access Token
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/how-to-add-extra-properties-to-an-access-token
Adding additional properties to access tokens for enhanced control and security in API requests.
## Preface
On receiving API requests (with access tokens) from clients, resource servers determine if the requests are allowed, and how to process them, by referring information associated with the tokens. Typical kinds of information include the following: status of the token (active / expired), who (resource owner) granted what access (scope) to whom (client). In some cases, the resource servers need to obtain other related information from an authorization server.
Authlete enables an authorization server to associate arbitrary properties with either an access token or authorization code. The authorization server can easily share the properties with resource servers so that they can consume such information for its authorization enforcement as well as making a response.
You can prevent unnecessary information disclosure as the sharing of properties can be achieved by not using clients as an intermediary.
## Use cases
## 1. Controlling a money transfer API
Let's assume you would like to develop a money transfer API that can process specific transaction like "send \$50 to ABC shop."
You could implement such function by creating a "send-50dollar-to-abcshop" scope, but it hardly works as you would have to prepare a lot of scopes that are multiplied with recipients and amounts.
With the properties feature of Authlete, The authorization server can associate the money transfer information (or its handle, if database manages the actual information) with an access token to be issued to a client. The resource server, which hosts the money transfer API, receives an API request with the access token from the client, asks Authlete's introspection API to provide the properties along with details of the token, and then determines if the money transfer request is allowed to proceed.
## 2. Role based access control
When a resource server receives an API request with an access token, it would like to know details of the token, not only user identifier but also groups and roles of the user.
A simple solution is, that the resource server makes an introspection request to Authlete, gets the user identifier ("subject") and makes a query to another database to find such groups/roles information.
Again, Authlete's properties feature makes the implementation simpler. The authorization server can associate the groups/roles with the access token to be provided to the client, and the resource server can find the values from the access token included in an API request from the client. The resource server doesn't need to communicate with the database.
## How it works
This section describes examples on how to set and get properties.
*Extra Properties*
## “Property” data type
Authlete defines [Property](/api-reference/token-endpoint/process-token-request#body-properties) data type that represents an arbitrary set of attributes associated with either an access token or an authorization code. The Property data type is a JSON object that contains the following entries.
| Name | Type | Description |
| - | - | - |
| key | string | A key of the property |
| value | string | A value of the property |
| hidden | boolean | A flag that indicates if the property is hidden from clients |
* true: hidden from clients
* false: not hidden from clients i.e. they can see the content |
For example, a property whose key and value are example\_key and example\_value respectively and that is not hidden from clients would be represented as a JSON object as follows.
```
{
"key": "example_key",
"value": "example_value",
"hidden": false
}
```
## Associating properties
Associating properties with an access token and/or an authorization code will be done by an authorization server at the following steps.
### Providing an authorization code and/or an access token through its authorization endpoint
An authorization server sets properties to a request to Authlete's [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API. Authlete associates the properties with the new authorization code / the access token and provide content of a token response including the code / the token.
The following is an example using curl. (folded for readability)
* Request
```shell highlight={6-8} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization/issue \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"subject":"",
"ticket":"",
"properties":[
{"key":"example_key", "value":"example_value", "hidden":true}
]
}'
```
* Response
```
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"accessTokenDuration": 0,
"accessTokenExpiresAt": 0,
"action": "LOCATION",
"authorizationCode": "...",
"responseContent": "https://client.example.org/cb/example.com?code=..."
}
```
You can implement this operation with [authlete-java-jaxrs](https://github.com/authlete/authlete-java-jaxrs) library. Specifically there is a [getProperties](https://authlete.github.io/authlete-java-jaxrs/com/authlete/jaxrs/spi/AuthorizationDecisionHandlerSpi.html#getProperties--) method in [AuthorizationDecisionHandlerSpiAdapter](https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/spi/AuthorizationDecisionHandlerSpiAdapter.java) class, that handles association of properties (returns null by default).
### Providing an access token through a token endpoint
An authorization server sets properties to a request to Authlete's [/auth/token](/api-reference/token-endpoint/process-token-request) API. Authlete associates the properties with the new access token and provide content of a token response including the token.
The following is an example using curl. (folded for readability)
* Request
```shell highlight={8-10} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "",
"clientSecret": "",
"parameters": "redirect_uri=https://client.example.org/cb/example.com&grant_type=authorization_code&code=...",
"properties": [
{"key": "example_key", "value": "example_value", "hidden": false}
]
}'
```
* Response
```json highlight={9-15,23} theme={null}
{
"resultCode": "A050001",
"resultMessage":
"[A050001] The token request (grant_type=authorization_code) was processed
successfully.",
"accessToken": "...",
"action": "OK",
...
"properties": [
{
"hidden": false,
"key": "example_key",
"value": "example_value"
}
],
"refreshToken": "...",
"responseContent":
"{\"access_token\":\"...\",
\"refresh_token\":\"...\",
\"scope\":\"payment\",
\"token_type\":\"Bearer\",
\"expires_in\":300,
\"example_key\":\"example_value\" }",
"subject": ""
}
```
In this case, the property is exposed to the client as\*\* "hidden":false\*\* is set. Client can see the property as a part of a token response and/or a part of payload of [JWT-formatted access token](/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens) (if used). This value must be set to **true** if the property is only for resource servers i.e. clients must not see it.
## Obtaining properties
By sending an access token (extracted from an API request from a client) to Authlete's [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API, an resource server receives a response that would contain properties.
The following is an example using curl. (folded for readability)
* Request
```
curl -s -X POST \
https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"token":""}'
```
* Response
```json highlight={3-9} theme={null}
{
"clientId": ,
"properties": [
{
"key": "example_key",
"value": "example_value",
"hidden": false
}
],
"action": "OK",
"resultCode": "A056001",
"expiresAt": 1565847795000,
"subject": "",
"refreshable": true,
"usable": true,
"existent": true,
"resultMessage": "[A056001] The access token is valid.",
"responseContent": "Bearer error=\"invalid_request\""
...
}
```
## Examples by flow
Where properties are set depends on the grant flow, because different flows issue the token from different Authlete APIs. The walkthroughs below show the API call that takes the `properties` parameter in each flow, and what the client and the resource server see as a result.
The examples use `example_parameter=example_value` as the property, `${SERVICE_API_KEY}` as the service ID, and a service access token as the bearer credential.
### Authorization code flow
1. Pass the authorization request from the client to the [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API. The response carries `action: INTERACTION` and a `ticket`, which the next call consumes.
```bash theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "client_id=1048476397&response_type=code&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=timeline.read" }'
```
2. Issue the authorization code with the [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API, passing the properties to associate with it. They end up on the access token that the authorization code is later exchanged for.
```bash highlight={6-8} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization/issue \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "ticket": "XhPCGRCuKw4QYShCB59yQ-5jq258hZaZ-2QNhdmsUZg",
"subject": "user123",
"properties": [
{"key": "example_parameter", "value": "example_value"}
]
}'
```
```json theme={null}
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"action": "LOCATION",
"authorizationCode": "HSwoz1hLb-qaEo5oUFhGHZdpdTevHNIrhJotOpOg0_o",
"responseContent": "https://client.example.org/cb?code=HSwoz1hLb-qaEo5oUFhGHZdpdTevHNIrhJotOpOg0_o&iss=https%3A%2F%2Fauthlete.com"
}
```
`action: LOCATION` means the authorization server should answer the client with `302 Found` and use `responseContent` as the `Location` header.
3. Exchange the authorization code at the [/auth/token](/api-reference/token-endpoint/process-token-request) API. Properties can be added here as well; they are merged with the ones given at step 2, and a key given in both places takes the value passed to `/auth/token`.
```bash highlight={7-9} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "grant_type=authorization_code&code=HSwoz1hLb-qaEo5oUFhGHZdpdTevHNIrhJotOpOg0_o&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb",
"clientId": "1048476397",
"clientSecret": "${CLIENT_SECRET}",
"properties": [
{"key": "additional_parameter", "value": "additional_value"}
]
}'
```
```json highlight={5-16,19} theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"action": "OK",
"properties": [
{
"key": "additional_parameter",
"value": "additional_value",
"hidden": false
},
{
"key": "example_parameter",
"value": "example_value",
"hidden": false
}
],
"accessToken": "LN-42WCA9hGmTbiKVfnyG8zFssh1S99YdLZ-uZ7Dg04",
"refreshToken": "pbUei8l4-2cFL31zfBsaX9MO83SvNYmLfIJQxxYXAwU",
"responseContent": "{\"access_token\":\"LN-42WCA9hGmTbiKVfnyG8zFssh1S99YdLZ-uZ7Dg04\",\"token_type\":\"Bearer\",\"expires_in\":86400,\"scope\":\"timeline.read\",\"refresh_token\":\"pbUei8l4-2cFL31zfBsaX9MO83SvNYmLfIJQxxYXAwU\",\"additional_parameter\":\"additional_value\",\"example_parameter\":\"example_value\"}"
}
```
`action: OK` means the authorization server should answer the client with `200 OK` and use `responseContent` as the response body. Both properties reach the client, because neither was marked `hidden`.
### Implicit flow
The access token is issued from the authorization endpoint, so `/auth/authorization/issue` is the only place that takes properties.
1. Pass the authorization request, this time with `response_type=token`, to the `/auth/authorization` API and take the `ticket` from the response.
```bash theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "client_id=1048476397&response_type=token&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=timeline.read" }'
```
2. Issue the access token with `/auth/authorization/issue`, passing the properties.
```bash highlight={6-8} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization/issue \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "ticket": "j3OkWV4r8TEKeCTLPY8c_BfrYAbKYEy2FXZUaiYKAYU",
"subject": "user123",
"properties": [
{"key": "example_parameter", "value": "example_value"}
]
}'
```
```json highlight={5} theme={null}
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"action": "LOCATION",
"responseContent": "https://client.example.org/cb#access_token=VHnmJTwVdiS69oY_RLT9kYhdMUWqNdUEVHITdy_81ak&token_type=Bearer&expires_in=86400&scope=timeline.read&example_parameter=example_value&iss=https%3A%2F%2Fauthlete.com",
"accessToken": "VHnmJTwVdiS69oY_RLT9kYhdMUWqNdUEVHITdy_81ak"
}
```
The property travels to the client as a fragment parameter of the redirect URI, next to `access_token`.
### Client credentials flow
There is no authorization endpoint step, so the properties go to `/auth/token` together with the token request.
```bash highlight={7-9} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "grant_type=client_credentials&scope=timeline.read",
"clientId": "1048476397",
"clientSecret": "${CLIENT_SECRET}",
"properties": [
{"key": "example_parameter", "value": "example_value"}
]
}'
```
```json theme={null}
{
"resultCode": "A052001",
"resultMessage": "[A052001] The token request (grant_type=client_credentials) was processed successfully.",
"action": "OK",
"properties": [
{
"key": "example_parameter",
"value": "example_value",
"hidden": false
}
],
"responseContent": "{\"access_token\":\"jC9TsQVcb0tE30pZ2jGu9S_fUB_r7AFACqcJOkl4HFA\",\"token_type\":\"Bearer\",\"expires_in\":86400,\"scope\":\"timeline.read\",\"example_parameter\":\"example_value\"}"
}
```
The client must be confidential to use this flow, and no refresh token is issued ([RFC 6749, 4.4.3](https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3)).
### Resource owner password credentials flow
The token is issued in two steps, and only the second one takes properties.
1. Pass the token request to `/auth/token`. The response carries `action: PASSWORD` together with the `username` and `password` the client sent, which the authorization server authenticates the user with.
Properties passed to `/auth/token` are discarded when `grant_type` is `password`. Pass them to `/auth/token/issue` instead.
```bash theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "grant_type=password&username=john&password=secret",
"clientId": "1048476397",
"clientSecret": "${CLIENT_SECRET}"
}'
```
```json theme={null}
{
"resultCode": "A051001",
"resultMessage": "[A051001] Authlete has successfully issued a ticket to the service (API Key = 4239612149) for the token request from the client (ID = 1048476397). [grant_type=password]",
"action": "PASSWORD",
"username": "john",
"password": "secret",
"ticket": "gS1B19zO0c8DzN5jpac2PIWa9M1h2bFX9u6MzRi0X58"
}
```
2. Once the user is authenticated, issue the access token with the [/auth/token/issue](/api-reference/token-endpoint/issue-token-response) API, passing the identified user as `subject` and the properties to associate.
```bash highlight={6-8} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token/issue \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "ticket": "gS1B19zO0c8DzN5jpac2PIWa9M1h2bFX9u6MzRi0X58",
"subject": "user123",
"properties": [
{"key": "example_parameter", "value": "example_value"}
]
}'
```
```json theme={null}
{
"resultCode": "A054001",
"resultMessage": "[A054001] The token request (grant_type=password) was processed successfully.",
"action": "OK",
"properties": [
{
"key": "example_parameter",
"value": "example_value",
"hidden": false
}
],
"responseContent": "{\"access_token\":\"jRdB_IwMkIyjvFkZTE5PBm1rbzz96x8Y0oF2MGagsVo\",\"token_type\":\"Bearer\",\"expires_in\":86400,\"scope\":null,\"refresh_token\":\"a3NqLMT87HKgLblRkg51ga-xCY3OI_voPvC5uzLxQDg\",\"example_parameter\":\"example_value\"}"
}
```
### Refresh token flow
Properties passed to `/auth/token` with `grant_type=refresh_token` are **merged into** the properties already carried by the access token that the refresh token belongs to — new keys are added, and a key that already exists takes the value passed here. The refresh token below is the one issued by the authorization code flow above, whose access token already has `example_parameter` and `additional_parameter`.
```bash highlight={7-9} theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "parameters": "grant_type=refresh_token&refresh_token=pbUei8l4-2cFL31zfBsaX9MO83SvNYmLfIJQxxYXAwU",
"clientId": "1048476397",
"clientSecret": "${CLIENT_SECRET}",
"properties": [
{"key": "extra_parameter", "value": "extra_value"}
]
}'
```
The newly issued access token therefore carries three properties, which the resource server reads back from the introspection API.
```bash theme={null}
curl -s -X POST https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection \
-H 'Authorization: Bearer ${SERVICE_ACCESS_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{ "token": "SJKrkYzT16OInwg0gKjC6bhPo9rUNRGRDMD1c8YUHNE" }'
```
```json highlight={7-23} theme={null}
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
"clientId": 1048476397,
"subject": "user123",
"properties": [
{
"key": "additional_parameter",
"value": "additional_value",
"hidden": false
},
{
"key": "example_parameter",
"value": "example_value",
"hidden": false
},
{
"key": "extra_parameter",
"value": "extra_value",
"hidden": false
}
],
"refreshable": true,
"usable": true,
"existent": true,
"expiresAt": 1789564519772,
"responseContent": "Bearer error=\"invalid_request\""
}
```
## Notes
## Reserved values for "key"
The following values are not applicable for "key" of properties, as [RFC 6749](https://tools.ietf.org/html/rfc6749) and [OpenID Connect Core 1.0](http://openid.net/specs/openid-connect-core-1_0.html) have reserved the names for parameters which may be included in a response from an authorization server. Authlete discards them if they are specified.
* access\_token
* token\_type
* expires\_in
* refresh\_token
* scope
* error
* error\_description
* error\_uri
* id\_token
## Type of "value"
String is the only allowed type of "value." Neither boolean nor array can be specified.
## Size of properties
Size of properties is limited. Properties are saved into the database on the server side after going through the steps described below.
1. Converted to a two-dimensional array (e.g. \[\["example\_parameter","example\_value",null]]). The value of hidden is converted to either null or an empty string. They mean false and true, respectively.
2. Converted to JSON (e.g. "\[\["example\_parameter":"example\_value",null]]")
3. Encrypted by AES/CBC/PKCS5Padding
4. Encoded by base64url
5. If the length of the resultant base64url string generated by the above steps exceeds 65535, an error occurs.
# Enabling Refresh Token Issuance
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/how-to-enable-issuing-of-a-refresh-token
Configuring Authlete service and clients to enable issuing a refresh token including adding REFRESH_TOKEN to Supported Grant Types and Grant Types settings in the Authlete Management Console.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/token-lifecycle-policies/how-to-enable-refresh-token-grant-type).
In order to include a refresh token in a response from [/auth/token](/api-reference/token-endpoint/process-token-request), you have to configure both Authlete service and its clients.
If not configured, Refresh Token Grant Type (grant\_type=refresh\_token) is disabled. Issuing of a refresh token for other grant types such as Authorization Code Grant Type (grant\_type=authorization\_code) is also omitted.
## Service settings
In the Authlete console, go to your service settings, under `Endpoints > Global Settings`. You will then need to enable the "REFRESH\_TOKEN" value under the `Supporter Grant Types` setting.

*Supported Grant Types*
## Client settings
**Requiring such settings for clients is applicable for Authlete 2.1 and later i.e. not applicable for Authlete 2.0 and its previous versions.**
In the Authlete console, go to your client settings, under `Endpoints > Global Settings`. You will then need to enable the "REFRESH\_TOKEN" value under the `Supporter Grant Types` setting.

*Grant Types*
# Identifying Claims Expected to Be Included in ID Token
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/identifying-claims-expected-to-be-included-into-an-id-token
How Authlete helps identity providers identify and fulfill requested claims by relying parties in OpenID Connect authentication requests.
# Identifying "claims" expected to be included into an ID token
## Preface
OpenID Connect (OIDC) allows a relying party (RP) to specify what [claims](https://openid.net/specs/openid-connect-core-1_0.html#Claims)
it wants as an ID token, in its authentication requests.
Sometimes it would be complicated for an identity provider (OpenID provider; OP) to identify which claims are actually requested by the RP, due to methods to specify them.
This article describes how Authlete fulfills this process on behalf of the OP.
## Identifying requested claims
On receiving an OIDC authentication request from the RP through user agent (Web browser), the OP makes a request to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request)
API to parse query string of the authentication request.
Authlete parses the string and may identify claims that the RP expects to be included into an ID token. There are two types of claims; those indirectly specified by "scope" parameter and others directly specified by "claims" parameter. Authlete expands the former ones and aggregates together with the latter ones.
Then Authlete makes an API response to the OP, including "claims" whose values include the claims (e.g. email) that the OP is expected to set explicitly. Note that the "claims" in the response doesn't include ones which Authlete is responsible for setting by default.
The OP checks the response from Authlete, determines which claims must be provided to the RP and prepares their values to be set to an ID token. It is done using Authlete's [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)
API. (See also: [Adding Claims to ID Token](/configuration-reference/tokens-and-claims/adding-claims-to-an-id-token)
)\\
*Claims in an authentication request and how Authlete detects them*
## Expanding shortcut scopes
[OpenID Connect Core 1.0 Section 5.4](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)
restricts that an OP shall not include claims derived from shortcut scopes (e.g., profile) into an ID token, when an access token is also to be issued.
Authlete is to follow this clause by default, i.e. it doesn't include the derived claims into the "claims" of /auth/authorization API response, when response\_type is anything other than id\_token (e.g., code). As a result, the OP will not set those claims as an ID token.
This setting can be changed via the "Claims Shortcut" in the Authlete service. The options are as follows:
* **Restrictive**: In strict compliance with the clause, Authlete operates as described above. (default)
* **Nonrestrictive**: Authlete always include the derived claims as the "claims" value, regardless of whether or not an access token is issued. The OP will decide whether to set those claims as an ID token.
*Claim Shortcut*
## Example
Assume that the RP requests the following scope and claims:
* Scope (excluding the "openid" scope):
* **profile**
* Claims that the RP expects to include into an ID token:
* **[http://example.info/claims/category](http://example.info/claims/category)**
* **email\_verified**
Query string of the authentication request form the RP would be as follows. (folded for readability)
```
redirect_uri=...
&response_type=code
&client_id=...
&nonce=...
&scope=openid+profile&claims=%7b%22id_token%22%3a %7b%22http%3a%2f%2fexample%2einfo%2fclaims%2fcategory%22%3a null%2c
%22email_verified%22%3a null%7d%7d
```
When the "Claim Shortcut" setting above is "Nonrestrictive," once the OP receives this string as the authentication request and sends it (as a value of "parameters") to Authlete's /auth/authorization API, the OP would receive an API response including the following "claims." Note that there are other claims which are indirectly specified by the "profile" scope. (folded for readability)
```
"claims": [
"birthdate",
"email_verified",
"family_name",
"gender",
"given_name",
"http://example.info/claims/category",
"locale",
"middle_name",
"name",
"nickname",
"picture",
"preferred_username",
"profile",
"updated_at",
"website",
"zoneinfo"
]
```
> Proprietary claims such as "[http://example.info/claims/category](http://example.info/claims/category)" have to be added to "Supported Claims" of Authlete's service instance used as a backend of the OP.
\*> Supported Claims
> \*
The values included in "claims" here are only those that are expected to be explicitly specified in another "claims" parameter of a request to [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)
API.
For example, "sub" is a essential claim for an ID token. Authlete is to generate an appropriate value of the claim from"subject" parameter in a request to /auth/authorization/issue API, and set it to "sub" in the ID token. In other words, the OP must not specify it as "claims" in the request to /auth/authorization/issue API. Thus such claims don't be shown in "claims" of the response from /auth/authorizatoin API.
# Issuing mutual-TLS certificate-bound access tokens
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/issuing-mutual-tls-certificate-bound-access-tokens
Instructions for setting up Authlete to issue Mutual-TLS certificate-bound access tokens as defined in RFC 8705.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/protocol-extensions/certificate-bound-access-tokens).
## Preface
This article describes setup instructions for Authlete to use “Mutual-TLS certificate-bound access tokens,” defined in “RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens.”
> “Mutual-TLS certificate-bound access tokens” works independently of the "mutual-TLS client authentication." Check out the following article on the client authentication method.
>
> * [Client authentication using tls\_client\_auth method](/configuration-reference/endpoints/client-authentication-using-tls-client-auth-method)
## How it works

Authlete APIs work as follows, once the “Mutual-TLS certificate-bound access tokens” settings have been enabled:
* [/auth/token](/api-reference/token-endpoint/process-token-request) API
* It receives a client certificate (clientCertificate) as a request parameter and binds it with an access token to be issued.
* If using [JWT-formatted access tokens](/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens) is enabled, the API includes a thumbprint of the client certificate into an access token to be issued.
* [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API
* It receives an access token and a client certificate as request parameters and checks if both are bound to each other.
* [/auth/introspection/standard](/api-reference/introspection-endpoint/process-oauth-20-introspection-request) API
* It receives an access token (token) as a value included in one of request parameters (parameters) and provides a thumbprint of the client certificate bound to the token.
With these APIs, only the clients that can submit their client certificates through mutual TLS connection are able to prove their possession of access tokens and use the tokens for API requests. In other words, the tokens are no longer useful for other clients.
***
## Configuration
## Service settings for access token configuration
Log into the [Authlete Console](https://console.authlete.com/), and navigate to your service settings.
Under `Tokens and Claims > Access Token`, you will find the `Enable TLS Client Certificate Binding` setting, which needs to be turned on.

*TLS Client Certificate Bound Access Tokens in Authlete Management Console*
## Client settings for access token configuration
Log into the [Authlete Console](https://console.authlete.com/), and navigate to your client settings.
Under `Tokens and Claims > Access Token`, you will find the `Enable TLS Client Certificate Binding` setting, which needs to be turned on.

*TLS Client Certificate Bound Access Tokens in Authlete Management Console*
With these settings, “Mutual-TLS certificate-bound access tokens” are now ready for use.
***
## Examples
The following are example are requests and responses to/from the APIs (folded for readability).
## /auth/token API
### Request
An authorization server establishes a mutual TLS connection with a client, extracts the client's certificate from the connection, and make a request to Authlete using the certificate as a value of "clientCertificate."
```shell highlight={5-23} theme={null}
$ curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{'\
'"clientCertificate":"-----BEGIN CERTIFICATE-----\n
MIIDPDCCAiQCCQDfkemtGUyxAjANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJK\n
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\n
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTIwMDUxMTEx\n
MDcyM1oXDTIxMDUxMTExMDcyM1owYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\n
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\n
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n
AQoCggEBAMu7V2QyQk4iLPry0OsykR/8WO2aNOBNoVZYDexF1TsFv9s2S4PEDFEt\n
1BZrEmBe5HWpGb1iuDGG6wjAanEkea8AIUgslMsOOB0rQnbJA3nI5wktjCG2VzWo\n
zrmxAAaBM7ixaLPcJT1K0FSu4fzse3X9gtA2rtNGwk1JEMNIBpFghxw7zzUZBduS\n
lkVPPcQ3gPGVutiWfNPNPAqb4eMKwtuTPXbZSJ4RO9xGKMIuoaBWO9xPS6rNy+Kr\n
vOckNEsR+8PBYh9vzbmF0mDlk+BMd9sOGyylYBARFdqukMJnSbVcRvr+Gcaf+QSg\n
FRHkVGWXHXtMRI+MsCvOXrhmlJhxbdMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA\n
JfEyXTEGdjchre2dFCa/WLdH5PZBqB+E0Umm+/I+puA+aUpkRaOfmIUPNaCZ1Lgg\n
mOo8BGuEG0oXpOSBoRkxf46d1bxJk5YSe+TgSxS6YX/mE6Lt1Aj7HjojlCuNFGS6\n
vzyEugQSsOjuoIPS32PvqmZ+80jzVNUsMD76xGl32ZsVeJmDnogVPZ4NlolQcmH2\n
bYjK00uPXaUU30IVu+UTrEBhe30My/aW31NbT2I7Ct52wwu67pyP3QOKmuLxMd6N\n
+KkxfIY23zla4herq9vWZejgfBfkAwOapeIIHGYtY7IFxm6nqufb4h71vjeoouOt\n
31J7bEcAU9WRP1Pr/X4hLA==\n-----END CERTIFICATE-----\n",'\
'"parameters"':\
'"redirect_uri=https://client.example.org/cb/example.com'\
'&client_id=...'\
'&grant_type=authorization_code'\
'&code=smA...Gp4"'\
'}'
```
### Response
Authlete issues an access token bound with the client certificate.
```json highlight={4,13} theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "AHu...MMs", "accessTokenDuration": 86400,
"accessTokenExpiresAt": 1591935416474,
"action": "OK",
"clientId": 17566160603766,
"clientIdAliasUsed": false,
"grantType": "AUTHORIZATION_CODE",
"refreshToken": "_zkZeqy6pLdFerpwqJPh0S0gEZsSj9EbmwVsN92WNsE",
"refreshTokenDuration": 864000,
"refreshTokenExpiresAt": 1592713016474,
"responseContent": "{\"access_token\":\"AHu...MMs\",
\"refresh_token\":\"_zk...NsE\",
\"scope\":null,
\"token_type\":\"Bearer\",
\"expires_in\":86400}",
"subject": "testuser01"
}
```
If using JWT-formatted access tokens is enabled, an access token looks like this:
```
eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ.
eyJzdWIiOiJ0ZXN0dXNlcjAxIiwic2NvcGUiOm51bGwsImlzcyI6Imh0dHBzOi8v
YXMuZXhhbXBsZS5jb20iLCJjbmYiOnsieDV0I1MyNTYiOiJPSURfU2MyeVJlVER4
OVFTN2YxU01Vek54c2g3a2hKWW1hSXdxWHc4WXV3In0sImV4cCI6MTU5MTkzOTMy
NiwiaWF0IjoxNTkxODUyOTI2LCJjbGllbnRfaWQiOiIxNzU2NjE2MDYwMzc2NiIs
Imp0aSI6InN6dDVnV0ZTb3ZGSVU1Ti13amRMeUZELUVpUzNyZEcyS1IzMTFCMkY2
RmMifQ.
Pw3IntOrXDqW3Z5eoGSeeJaPG5n1aKEVokgbUh_BJpqzuZ6F6GDDLhj98XBvjii
x9oAmaTpy39ijl1vcq3r1BA
```
Payload part of the access token includes a thumbprint of the certificate as follows.
```json highlight={5-7} theme={null}
{
"sub": "testuser01",
"scope": null,
"iss": "https://as.example.com",
"cnf": {
"x5t#S256": "OID_Sc2yReTDx9QS7f1SMUzNxsh7khJYmaIwqXw8Yuw"
},
"exp": 1591939326,
"iat": 1591852926,
"client_id": "17566160603766",
"jti": "szt5gWFSovFIU5N-wjdLyFD-EiS3rdG2KR311B2F6Fc"
}
```
## /auth/introspection API
### Request
An authorization server establishes a mutual TLS connection with a client, extracts the client's certificate from the connection, and make a request to Authlete using the certificate as a value of "clientCertificate."
A resource server establishes a mutual TLS connection with a client, extracts the client's certificate from the connection, and make a request to Authlete using the certificate as a value of "clientCertificate."
```
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{
"token": "AHu...MMs",
"clientCertificate": "-----BEGIN CERTIFICATE-----\n
MIIDPDCCAiQCCQDfkemtGUyxAjANBgkqhkiG9w0BAQsFADBgMQswCQYDVQQGEwJK\n
UDEOMAwGA1UECAwFVG9reW8xEzARBgNVBAcMCkNoaXlvZGEta3UxDzANBgNVBAoM\n
BkNsaWVudDEbMBkGA1UEAwwSY2xpZW50LmV4YW1wbGUub3JnMB4XDTIwMDUxMTEx\n
MDcyM1oXDTIxMDUxMTExMDcyM1owYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRv\n
a3lvMRMwEQYDVQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNV\n
BAMMEmNsaWVudC5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n
AQoCggEBAMu7V2QyQk4iLPry0OsykR/8WO2aNOBNoVZYDexF1TsFv9s2S4PEDFEt\n
1BZrEmBe5HWpGb1iuDGG6wjAanEkea8AIUgslMsOOB0rQnbJA3nI5wktjCG2VzWo\n
zrmxAAaBM7ixaLPcJT1K0FSu4fzse3X9gtA2rtNGwk1JEMNIBpFghxw7zzUZBduS\n
lkVPPcQ3gPGVutiWfNPNPAqb4eMKwtuTPXbZSJ4RO9xGKMIuoaBWO9xPS6rNy+Kr\n
vOckNEsR+8PBYh9vzbmF0mDlk+BMd9sOGyylYBARFdqukMJnSbVcRvr+Gcaf+QSg\n
FRHkVGWXHXtMRI+MsCvOXrhmlJhxbdMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA\n
JfEyXTEGdjchre2dFCa/WLdH5PZBqB+E0Umm+/I+puA+aUpkRaOfmIUPNaCZ1Lgg\n
mOo8BGuEG0oXpOSBoRkxf46d1bxJk5YSe+TgSxS6YX/mE6Lt1Aj7HjojlCuNFGS6\n
vzyEugQSsOjuoIPS32PvqmZ+80jzVNUsMD76xGl32ZsVeJmDnogVPZ4NlolQcmH2\n
bYjK00uPXaUU30IVu+UTrEBhe30My/aW31NbT2I7Ct52wwu67pyP3QOKmuLxMd6N\n
+KkxfIY23zla4herq9vWZejgfBfkAwOapeIIHGYtY7IFxm6nqufb4h71vjeoouOt\n
31J7bEcAU9WRP1Pr/X4hLA==\n-----END CERTIFICATE-----"}'
```
### Response
If it is confirmed that the access token (token) is bound with the client certificate (clientCertificate), Authlete sends the following response. It includes a value of thumbprint of the client certificate (certificateThumbprint).
```json highlight={5} theme={null}
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
"certificateThumbprint": "OID_Sc2yReTDx9QS7f1SMUzNxsh7khJYmaIwqXw8Yuw",
"clientId": ...,
"clientIdAliasUsed": false,
"existent": true,
"expiresAt": 1591936228000,
"refreshable": true,
"responseContent": "Bearer error=\"invalid_request\"",
"subject": "testuser01",
"sufficient": true,
"usable": true
}
```
## /auth/introspection/standard API
### Request
A resource server send the access token in question, to an authorization server's token introspection endpoint. The authorization server forwards the request content to Authlete.
```
$ curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection/standard" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{ "parameters":"token=AHu...MMs" }'
```
### Response
If it is confirmed that the access token (token) is bound with the client certificate (clientCertificate), Authlete sends the following response. It includes a value of thumbprint of the client certificate (certificateThumbprint).
If it is confirmed that the access token is bound with a client certificate, Authlete sends responseContent including a thumbprint of the certificate. The authorization server sends the content back to the resource server as a response from its token introspection endpoint.
```json highlight={10-12} theme={null}
{
"resultCode": "A145001",
"resultMessage": "[A145001] Introspection was performed successfully (type=access_token, active=true).",
"action": "OK",
"responseContent": {
"sub": "testuser01",
"scope": null,
"iss": "https://as.example.com",
"active": true,
"cnf": {
"x5t#S256": "OID_Sc2yReTDx9QS7f1SMUzNxsh7khJYmaIwqXw8Yuw"
},
"token_type": "Bearer",
"exp": 1591936228,
"client_id": "17566160603766"
}
}
```
# Issuing Two Access Tokens Through Hybrid Flow
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/issuing-two-access-tokens-through-hybrid-flow
Issuing two access tokens with restricted scopes and shortened duration using OpenID Connect's Hybrid flows.
## Preface
You can use OpenID Connect's "Hybrid" flows to issue two access tokens for a client through a single authorization flow.
This article describes a use case of issuing access tokens with restricted scopes and/or a shortened duration, and how Authlete processes it.
## Use case
The hybrid flows are useful for authorization server to issue access tokens for a client that consists of a native application (e.g. mobile app) and an web server application (e.g. app backend), with a single authorization request.
A native application is often a public client, whose security risk is higher than a confidential client such as an web server application. In order to mitigate the risk, the hybrid flows can issue an access token for each side and restrict scopes (subsetting) of the particular token for the native application.
Furthermore, for an web server application that makes requests to high-stakes APIs, the flows allow authorization servers to issue access tokens with a shortened duration.
## How it works using Authlete
Authlete supports the hybrid flows. For example, its [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API can handle an authorization request with “response\_type=code token” and create an authorization response including an authorization code and an access token. Also, its [/auth/token](/api-reference/token-endpoint/process-token-request) API accepts a token request including the authorization code and creates a token response including another access token.
Authlete's token updating API ([/auth/token/update](/api-reference/token-operations/update-access-token) API) allows authorization servers to modify properties of access tokens, such as scopes, duration. These modifications are applied to access tokens stored in Authlete.
On receiving such access tokens from clients, resource servers will check status and details of the tokens, using Authlete's introspection API ([/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request)) and obtain the information modified in the previous step.
## Examples
This section describes examples where an authorization server issues two access tokens on receiving an authorization request with “response\_type=code token” and modifies scopes and duration for each of the tokens.
### Obtaining an access token to be sent back in an authorization response
On receiving an authorization request from a client (via a user agent), an authorization server typically does the following processes.
* Send content of the authorization request to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API (step #2, #3)
* Authenticate an end user and ask consent (step #4, #5)
* Make a request to [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API and obtain content of an authorization response (step #6, #7)
The following example is a response from [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API (step #7). It contains an access token as a value of "accessToken", to be provided from the authorization server's authorization endpoint to the client. Content of "responseContent" indicates that the token has scope=openid profile payment. (all examples below are folded for readability)
```json highlight={4,13} theme={null}
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"accessToken": "stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c",
"accessTokenDuration": 1800,
"accessTokenExpiresAt": 1596379999493,
"action": "LOCATION",
"authorizationCode": "iEATjO9V24Rubj1_PnATziCGh4ivVPRcTK_VZOsGplI",
"responseContent": "https://client.example.org/cb/example.com
#code=iEATjO9V24Rubj1_PnATziCGh4ivVPRcTK_VZOsGplI
&access_token=stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c &token_type=Bearer
&expires_in=1800
&scope=openid+profile+payment"}
```
### Narrowing (subsetting) scopes
The access token has three scopes: openid, profile and payment. The authorization server modifies the granted scopes (removes unnecessary scopes) using Authlete's [/auth/token/update](/api-reference/token-operations/update-access-token) API (step #6, #7). In this example, the server removes “payment” scope and preserves “openid” and “profile.”
> If [JWT-based access token](/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens) is enabled, the value to be specified as "accessToken" is a value of "accessToken" in the previous response, which is not a JWT-formatted one ("jwtAccessToken").
* Request (step #6 using curl command)
```shell highlight={5} theme={null}
curl -s -X POST $apiUrl/auth/token/update \
-u $apiKey:$apiSecret \
-H 'Content-type: application/json' \
-d '{"accessToken":"stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c", \
"scopes":["openid","profile"]}'
```
* Response (step #7)
```json highlight={7-10} theme={null}
{
"resultCode": "A135001",
"resultMessage": "[A135001] Updated the access token successfully.",
"accessToken": "stm3IfTHV_F1iVKEYQBJA58XMzA7OQ59-MQpdM3NH6c",
"accessTokenExpiresAt": 1596379999000,
"action": "OK",
"scopes": [
"openid",
"profile"
],
"tokenType": "Bearer"
}
```
The authorization server will send back the content of "responseContent", obtained from a response of /auth/authorization/issue API as shown above, to the client (step #8).
Note that values of scope parameter in the "responseContent" are the same as the original ones (“scope=openid+profile+payment”). Also note that, if [JWT-based access token](/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens) is enabled, the values of "scopes" in the access token are not updated i.e. "scope": "openid profile payment".
### Obtaining an access token to be sent back in a token response
On receiving a token request from a client (step #13), an authorization server typically does the following process.
* Send content of the token request to Authlete's [/auth/token]() API (step #14, #15)
The following example is a response from [/auth/token](/api-reference/token-endpoint/process-token-request) API (step #15). It contains an access token as a value of "accessToken", to be provided from the authorization server's token endpoint to the client. A value of "accessTokenExpiresAt" indicates that the token is to be expired on 1596380052227 (in Unix time).
```json highlight={4,6,17} theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg",
"accessTokenDuration": 1800,
"accessTokenExpiresAt": 1596380052227,
"action": "OK",
"clientId": 17201083166161,
"clientIdAlias": "clientapp01",
"clientIdAliasUsed": false,
"grantType": "AUTHORIZATION_CODE",
"idToken": "eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjE3MjAxMDgzMTY2MTYxIl0s ImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTk2NDY0NjUy LCJpYXQiOjE1OTYzNzgyNTJ9. uJiJxISW7d0HEgOyQsIFbqptOREeqs9zJYMs_ZhZ72w",
"refreshToken": "98YoC89H9CYcc56amo0bvNZ8pV02dYZmxCvB0Wz31jU",
"refreshTokenDuration": 9000,
"refreshTokenExpiresAt": 1596387252227,
"responseContent":
"{\"access_token\":\"zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg\",
\"refresh_token\":\"98YoC89H9CYcc56amo0bvNZ8pV02dYZmxCvB0Wz31jU\",
\"scope\":\"openid profile payment\",
\"id_token\":
\"eyJhbGciOiJIUzI1NiJ9.
eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjE3MjAxMDgzMTY2MTYxIl0s
ImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTk2NDY0NjUy
LCJpYXQiOjE1OTYzNzgyNTJ9.
uJiJxISW7d0HEgOyQsIFbqptOREeqs9zJYMs_ZhZ72w\",
\"token_type\":\"Bearer\",
\"expires_in\":1800}",
"scopes": [
"openid",
"profile",
"payment"
],
"subject": "testuser01"
}
```
### Shortening a duration
In the same way as the previous example, the authorization server modifies the duration of the access token using Authlete's [/auth/token/update](/api-reference/token-operations/update-access-token) API (step #16, #17). In this example, it specifies 1596379152000, that means 900 seconds (900,000 milliseconds) shorter than the original duration.
* Request (Step #16 using curl command)
```shell highlight={5} theme={null}
curl -s -X POST $apiUrl/auth/token/update \
-u $apiKey:$apiSecret \
-H 'Content-type: application/json' \
-d '{"accessToken":"zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg", \
"accessTokenExpiresAt":1596379152000}'
```
* Response (step #17)
```json highlight={5} theme={null}
{
"resultCode": "A135001",
"resultMessage": "[A135001] Updated the access token successfully.",
"accessToken": "zcxCRPL3P1n7gUnn1gnRAz3nndUdYeyilpU7txGE3gg",
"accessTokenExpiresAt": 1596379152000,
"action": "OK",
"scopes": [
"openid",
"profile",
"payment"
],
"tokenType": "Bearer"
}
```
# Letting Resource Owners Choose Scopes to Be Authorized
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/letting-resource-owners-choose-scopes-to-be-authorized
How to utilize Authlete APIs to enable resource owners to select scopes for authorization on an authorization page.
## Overview
Authlete enables developers to build an authorization page where end-users (or resource owners) can choose scopes. This article describes how to use Authlete APIs to do that.\\
## Obtaining a list of scopes from an authorization request
First you will need to know which scopes are requested by a client. They are included in an authorization request from the client with a parameter “scope.” Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API parses the request and tells your authorization server the requested scopes, as an array of "scopes". The following is an example request/response.
* Request
```shell highlight={8} theme={null}
$ curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{"parameters":
"redirect_uri=...
&client_id=...
&response_type=code
&scope=read+write"}'
```
* Response
```json highlight={9-22} theme={null}
{
"resultCode": "A004001",
"resultMessage":
"[A004001] Authlete has successfully issued a ticket
to the service (API Key = ...) for the authorization
request from the client (ID = ...).
[response_type=code, openid=false]",
...
"scopes": [
{
"defaultEntry": false, "description": "Read",
"descriptions": [
{ "tag": "en", "value": "Read" },
{ "tag": "ja", "value": "参照" } ],
"name": "read" },
{
"defaultEntry": false, "description": "Write",
"descriptions": [
{ "tag": "en", "value": "Write" },
{ "tag": "ja", "value": "更新" } ],
"name": "write" }
]
...
```
With the values in "scopes", you can craft an authorization page that allows end users to choose what kind of access can be granted to the requesting client.
## Specifying the end user's chosen scopes
Once the authorization server recognizes the end user's intent, it will ask Authlete to issue an access token (and/or an authorization code) with the scopes that are chosen by the user.
Authlete's [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API accepts a request including "scopes" that enables narrowing down scopes, which are initially requested in the authorization request from the client. By giving a non-empty string array as the value of the scopes parameter, Authlete replaces the scopes with it. The following is an example request/response for processing implicit grant flow.
* Request
```shell highlight={6} theme={null}
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization/issue" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{"subject":"...",
"ticket": "...",
"scopes":["read"]}'
```
* Response
```json highlight={14} theme={null}
{
"resultCode": "A040001",
"resultMessage":
"[A040001] The authorization request was processed successfully.",
"accessToken": "...",
"accessTokenDuration": ...,
"accessTokenExpiresAt": ...,
"action": "LOCATION",
"responseContent":
"https://client.example.org/cb/example.com
#access_token=...
&token_type=Bearer
&expires_in=...
&scope=read"}
```
As a result, the authorization server will give the client the access token with the narrowed scopes.
**Note that this function only narrows down the scopes originally requested at /auth/authorization API. The scopes parameter cannot include additional scopes that you did not request at the /auth/authorization API.**
# Managing Authorizations (Issued Tokens) Granted for Client by User
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/managing-authorizations-issued-tokens-granted-for-a-client-by-a-user
Technical information about Authlete APIs for managing authorizations (issued tokens) granted for a client by a user.
## Preface
This article describes some Authlete APIs for retrieving, changing and revoking authorization granted for a client by a user. They would be useful in some use cases, for example:
* A user logs in to an API provider (authorization server) website and checks what clients he or she has granted access
* An API provider deletes tokens for a particular client as per request from a user
## 1. Obtaining a list of clients that have tokens granted by a user
[/client/authorization/get/list](/api-reference/client-management/get-authorized-applications) API provides a list of clients which have been authorized a certain user (i.e. which have had tokens granted by the user).
### Request
A request will be made using either GET or POST.
```
GET /api/client/authorization/get/list/
```
```
GET /api/client/authorization/get/list?subject=
```
```
POST /api/client/authorization/get/list
application/x-www-form-urlencoded
```
```
POST /api/client/authorization/get/list
application/json
```
Request parameters are as follows.
| Item | Description |
| - | - |
| subject | Unique user ID **\*REQUIRED** |
| start | Start index of search results, inclusive (The default value is 0) |
| end | End index of search results, exclusive (The default value is 5) |
| developer | Unique Developer ID (The default value is null) |
### Response
### Successful response
JSON including the following parameters is provided with status code 200.
| Item | Description |
| - | - |
| start | Start index of search results (inclusive) |
| end | End index of search results (exclusive) |
| developer | Unique developer ID |
| totalCount | The total number of clients that meet the conditions |
| clients | An array of clients. Format of the client information is the same as ones in other responses of some APIs e.g. [/client/get](https://docs.authlete.com/#client-get-api) |
| subject | Unique user ID |
### Failed response
The following JSON object is provided with status code 400, 403, 500 etc.
```
application/json
{
"resultCode": ...,
"resultMessage": ...
}
```
### Example
* Request
The following example is a request to retrieve a list of clients granted authorization by user **"testuser01"**.
```shell highlight={4} theme={null}
curl -s -X POST $AL_API/client/authorization/get/list \
-u ...:... \
-H 'Content-type: application/json' \
-d '{"subject":"testuser01"}'
```
* Response
Authlete sends back a response including a list of clients in **"clients"**.
```json highlight={2-9} theme={null}
{
"clients": [
{
"clientId": 17566160603766,
"clientIdAliasEnabled": false,
"clientName": "FAPI Client",
"developer": "authlete_14500880170338"
}
],
"end": 5,
"start": 0,
"totalCount": 1,
"subject": "testuser01"
}
```
## 2. Updating scopes of authorizations (tokens) for a client by a user
[/client/authorization/update](/api-reference/client-management/update-client-tokens) API allows an authorization server to update scopes of tokens for a single client, which have been granted by a certain user.
### Request
A request will be made using POST. Its URL includes clientId.
```
POST /api/client/authorization/update/
application/x-www-form-urlencoded
```
```
POST /api/client/authorization/update/
application/json
```
Request parameters are as follows.
| Item | Description |
| - | - |
| subject | Unique user ID **\*REQUIRED** |
| scopes | An array of new scopes |
* If a non-null value is given, the new scopes are set to all existing access tokens
* If an API call is made using "Content-Type: application/x-www-form-urlencoded", scope names listed in this request parameter should be delimited by spaces (after form encoding, spaces are converted to '+') |
### Response
JSON including the following parameters is provided with status code 200, 400, 403, 500 etc.
```
application/json
{
"resultCode": ...,
"resultMessage": ...
}
```
### Example
* Request
The following example is a request to update tokens issued to a client **"17566160603766"** as per granted by a user **"testuser01"**. A new value of "scopes" for the tokens will be **"payment"**.
```shell highlight={1,5} theme={null}
curl -s -X POST $AL_API/client/authorization/update/17566160603766 \
-u ...:... \
-H 'Content-type: application/json' \
-d '{
"subject":"testuser01", "scopes":"payment" }'
```
* Response
Authlete send back a response stating that the access tokens have been updated.
```json highlight={3} theme={null}
{
"resultCode": "A138001",
"resultMessage": "[A138001] Updated 4 access token(s) issued to the client (ID = 17566160603766) of the service (API Key = ...)."
}
```
## Revoking authorization for one of clients which have been authorized by a user
[/client/authorization/delete](/api-reference/client-management/delete-client-tokens) API allows authorization server to revoke tokens by specifying both a client and a user.
### Request
A request will be made using either DELETE or POST. Its URL includes clientId.
```
DELETE /api/client/authorization/delete//
```
```
DELETE /api/client/authorization/delete/?subject=
```
```
POST /api/client/authorization/delete/
application/x-www-form-urlencoded
```
```
POST /api/client/authorization/delete/
application/json
```
Request parameters are as follows.
| Item | Description |
| - | - |
| subject | Unique user ID **\*REQUIRED** |
### Response
JSON including the following parameters is provided with status code 200, 400, 403, 500 etc.
```
application/json
{
"resultCode": ...,
"resultMessage": ...
}
```
### Example
* Request
The following example is a request to delete tokens issued to a client **"17566160603766"** as per granted by a user **"testuser01"**.
```shell highlight={1,5} theme={null}
curl -s -X POST $AL_API/client/authorization/delete/17566160603766 \
-u ...:... \
-H 'Content-type: application/json' \
-d '{
"subject":"testuser01" }'
```
* Response
Authlete send back a response stating that the access tokens have been deleted.
```json highlight={3} theme={null}
{
"resultCode": "A137001",
"resultMessage": "[A137001] Deleted 4 access token(s) issued to the client (ID = 17566160603766) of the service (API Key = ...)."
}
```
# Refresh tokens after being used
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/refresh-tokens-after-being-used
Technical information on handling refresh tokens after they have been used in OAuth 2.0.
## Overview
[“Refresh token grant” in OAuth 2.0](https://tools.ietf.org/html/rfc6749#section-6)
defines a mechanism for a client to request an authorization server to issue an access token (AT) by sending a previously issued refresh token (RT). The authorization server checks if the RT is valid, and then issues the new AT and sends it back to the client.
There are four choices for handling the used RT:
* 1-1: Keeping the RT valid for the next token refresh and not resetting the duration of the RT,
* 1-2: Keeping the RT valid for the next token refresh and resetting the duration of the RT,
* 2-1: Making the RT invalid and creating a new RT with a predefined duration, or
* 2-2: Making the RT invalid and creating a new RT with a remaining duration of the old RT.
In addition to the options above, Authlete provides a Token Expiration Link feature that limits duration of access tokens within the remaining duration of its corresponding refresh token.
You can configure Authlete to support either one of four choices and the link feature. This article describes these characteristics.
> The feature of “2-2: Making the RT invalid and creating a new RT with a remaining duration of the old RT” is available in Authlete version 2.1 and later.
> The feature of “1-2: Keeping the RT valid for the next token refresh and resetting the duration of the RT is available in Authlete version 2.2 and later.
> The Token Expiration Link feature is available in Authlete version 2.2 and later.
## 1-1: Keeping the RT valid for the next token refresh and not resetting the duration of the RT
On a token refresh request from a client with an RT (rt1), an authorization server sends a new AT (at2) and the submitted RT (rt1) back to the client. The RT (rt1) will remain valid so that the client can reuse the RT (rt1) in the next token refresh request as long as its duration remains. \
The validity period of the RT (rt1) won’t be changed on each token refresh.\\
## 1-2: Keeping the RT valid for the next token refresh and resetting the duration of the RT
On a token refresh request from a client with an RT (rt1), an authorization server sends a new AT (at2) and the submitted RT (rt1) back to the client. The RT (rt1) will remain valid so that the client can reuse the RT (rt1) in the next token refresh request as long as its duration remains.
Available duration of RT (rt1) is reset to the initial value on each token refresh. As a result, the available duration of RT (rt1) will be extended over the multiple requests.
## 2-1: Making the RT invalid and creating a new RT with a predefined duration
On a token refresh request from a client with an RT (rt1), an authorization server sends a new AT (at2) and a new RT (rt2) back to the client. The submitted RT (rt1) will be invalidated so that the client can no longer reuse the RT (rt1) in the next token refresh request.\
Available duration of RT (rt2, rt3, …) will be set to the initial value. As a result, the total available duration of the RT (rt2, rt3, …) gets extended more than the initial value of the RT (rt1).
## 2-2: Making the RT invalid and creating a new RT with a remaining duration of the old RT
On a token refresh request from a client with an RT (rt1), an authorization server sends a new AT (at2) and a new RT (rt2) back to the client. The submitted RT (rt1) will be invalidated so that the client can no longer reuse the RT (rt1) in the next token refresh request.\
Available duration of RT (rt2, rt3, …) is set to the remaining duration of the first RT (rt1). As a result, the total available duration of the RT (rt2, rt3, ...) is limited to the duration of the first RT (rt1)\\
## Token Expiration Link
On a token refresh request from a client with an RT (rt), an authorization server checks if the remaining duration of the RT is shorter than the default duration of the AT. If it is true, Authlete issues a new AT whose duration is linked to the remaining duration of the RT.\\
## Configuration
You can configure this setting through the Authlete Management Console. Choose “**Kept**” (i.e. keeping the used RT valid) or “**Not kept**” (i.e. invalidating the used RT) at “**Refresh Token Continuous Use**” section in “**Token**” tab. There are additional settings for each choice.
* When specifying “**Kept**,” you can further choose “**Enable**” or “Disable” at “**Refresh Token Duration Reset**” section.
* When specifying “**Not kept**,” you can further choose “**Enabled**” or “**Disabled**” at “**Refresh Token Duration Takeover**” section.
You can choose “**Linked**” or “**Not Linked**” at “**Token Expiration Link**” section regardless of your choices above.
## Examples
The following examples are a sample request and responses when a client makes a token request using a RT (**\< refresh\_token\_1 >**).
* Request
An authorization server makes a request to [Authlete's /auth/token API](/api-reference/token-endpoint/process-token-request)
to process the token request including the RT. Here is an example using curl. (folded for readability)
```shell highlight={8} theme={null}
curl -s -X POST .../auth/token
-u $apiKey:$apiSecret
-H 'Content-type: application/json'
-d '{"clientId":"...",
"clientSecret":"...",
"parameters":
"grant_type=refresh_token
&refresh_token=< refresh_token_1 >"}'
```
* Response if “**Kept**” (i.e. keeping the used RT valid) and “**Disabled**” (i.e. reset the duration of the RT) are specified
Authlete sends back the same RT as being used (**\< refresh\_token\_1 >**) to the authorization server. Its duration is not initialized (in the example, **332** seconds left).
```json highlight={8} theme={null}
{
"resultCode": "A053001",
"resultMessage": "[A053001] The token request (grant_type=refresh_token) was processed successfully.",
"accessToken": "...",
"action": "OK",
"grantType": "REFRESH_TOKEN",
"refreshToken": "< refresh_token_1 >",
"refreshTokenDuration": 332,
"refreshTokenExpiresAt": ...,
"responseContent":
"{\"access_token\":\"...\",
\"refresh_token\":\"< refresh_token_1 >\",
\"scope\":\"payment\",
\"token_type\":\"Bearer\",
\"expires_in\":300}",
"scopes": [
"payment"
],
"subject": "testuser01",
...
}
```
* Response if “**Kept**” (i.e. keeping the used RT valid) and “**Enabled**” (i.e. reset the duration of the RT) are specified
Authlete sends back the same RT as being used (**\< refresh\_token\_1 >**) to the authorization server. Its duration is initialized. (an example omitted)
* Response if “**Not kept**” (i.e. invalidating the used RT) and “**Disabled**” (i.e. creating a new RT with a predefined duration) are specified
Authlete issues a new RT (**\< refresh\_token\_2 >**) and sends it back to the authorization server. Its duration is set to **900** seconds.
```json highlight={10} theme={null}
{
"resultCode": "A053001",
"resultMessage":
"[A053001] The token request (grant_type=refresh_token)
was processed successfully.",
"accessToken": "...",
"action": "OK",
"grantType": "REFRESH_TOKEN",
"refreshToken": "< refresh_token_2 >",
"refreshTokenDuration": 900,
"refreshTokenExpiresAt": ...,
"responseContent":
"{\"access_token\":\"...\",
\"refresh_token\":\"< refresh_token_2 >\",
\"scope\":\"payment\",
\"token_type\":\"Bearer\",
\"expires_in\":300}",
"scopes": [
"payment"
],
"subject": "testuser01",
...
}
```
* Response if “**Not kept**” (i.e. invalidating the used RT) and “**Enabled**” (i.e. creating a new RT with a remaining duration of the old RT) are specified
Authlete issues a new RT (**\< refresh\_token\_2 >**) and sends it back to the authorization server. Its duration is not initialized. (an example omitted)
# Registering Localized Descriptions for Custom Scopes
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/registering-localized-descriptions-for-custom-scopes
Registering localized descriptions for custom scopes in Authlete's API.
## Overview
You can specify localized descriptions for your custom scopes by making a request to Authlete's API.
## Examples
Standard scopes have been configured to include Japanese text in descriptions as follows:
```
{
"name": "openid",
"defaultEntry": false,
"description": "A permission to request an OpenID Provider to issue an ID Token. See OpenID Connect Core 1.0, 3.1.2.1. for details.",
"descriptions": [
{
"tag": "en",
"value": "A permission to request an OpenID Provider to issue an ID Token. See OpenID Connect Core 1.0, 3.1.2.1. for details."
},
{
"tag": "ja",
"value": "OpenID プロバイダーに ID トークンの発行を要求するための権限です。詳細は OpenID Connect Core 1.0, 3.1.2.1. を参照してください。"
}
]
}
```
On custom scopes, you can register your own text for each language to Authlete by updating descriptions with [/service/update](/api-reference/service-management/update-service) API.
## Notes
The descriptions registered via the API will be removed if you update service settings via GUI.
# Resource Indicators
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/resource-indicators
Technical information about the Resource Identifier specification in OAuth 2 framework and its support by Authlete.
## Introduction
the original mechanism on OAuth 2 framework for expressing authorizations is the scopes presented on the token by the subject to the token holder. That allowed many enterprises and adopters to properly secured their environments when there is no ambiguity of the resource that the token is about.
Today with the micro service architecture and data partition due to data residency requirements or data sharding, the resource that the grant is about needs to be increasingly precise identified on the request, token and introspection.
There are 2 specifications addressing this: the [Resource Identifier (RFC 8707)](https://www.rfc-editor.org/info/rfc8707)
and the [Rich Authorization Requests (RFC 9396)](https://www.rfc-editor.org/info/rfc9396/). This note is about the Resource Identifier specification and the support that Authlete provides.
The resource identifier specification extends the authorization requests by introducing the "resource" parameter. This parameter can be included on authorization request parameters, Request Objects or the body of the push authorization requests or token requests.
## Basic flow
### Authorization Request
The authorization requests, pushed or not and wrapped or not on a Request Object can single or multi valued.
The request below is an example of API request to ask Authlete to process an authorization request that includes two resources specified by using the parameter **resource**.
```
curl --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data '{
"parameters":
"client_id=4015029369818770&
scope=openid&
response_type=code&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_challenge=OJeFyctrNbrZKVR60DQhAMqN6X_q00x7koQWAMZivnI&
code_challenge_method=S256&
resource=https%3A%2F%2Fapi.example.com%2Fapp%2F&
resource=https%3A%2F%2Fcontacts.example.com%2F" }'
```
The response from Authlete includes the **resources** attribute that will be used by the AS to decide if the authorization request is valid and if the user can grant access to those resources.
```json highlight={6-9} theme={null}
{
"resultCode": "A004001",
"resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 974410533989) for the authorization request from the client (ID = 4015029369818770). [response_type=code, openid=true]",
"action": "INTERACTION",
...
"resources": [
"https://api.example.com/app/",
"https://contacts.example.com/"
], ...
"ticket": "GKXW-NWoXHEH8Xh2lOVXbImFKZF3NY-yfpxMdqvwsms"
}
```
### Token Request
The AS sends token request content with the authorization code to Authlete.
```
curl --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data '{
"parameters": "grant_type=authorization_code&
code=R9eLSo9quzYjjO8zdlqFMAMdqkBz0vthmZWUDRpQw2s&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_verifier=_OtZIlJuuFwniie_nb6A172G2576YD_NwppB-I2ezfY",
"clientId" : "4015029369818770"
}'
```
The response will extract the resources that are granted to the client and include them in **accessTokenResources**.
```json highlight={5-8} theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "FOMxkE5baqGjIAznqmhANgw1ITiwCK4CRdo0Pbi2p-A",
"accessTokenResources": [
"https://api.example.com/app/",
"https://contacts.example.com/"
], ...
"grantType": "AUTHORIZATION_CODE",
"idToken": "eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIwNDAxOTg3NTE1NCIsIm5hbWUiOiJEci4gQmV2ZXJseSBQYWNvY2hhIiwiZW1haWwiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiaXNzIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb20iLCJzdWIiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiYXVkIjpbIjQwMTUwMjkzNjk4MTg3NzAiXSwiZXhwIjoyNTM0MDIxMjgwMDAsImlhdCI6MTYyODI3ODE2MH0.MGIEUwzFOiySL1klpCrGgcP7u70J1lGuxM154WV8l6HuGRlDbul8RsjjqkM-cZrX-wohWnXr4keqoQ8uJAEJwIuSL3Szt8KNpFD6fDwNfYC6SL_cTO1va3sgLSmDH7xnvaLfDMEedidw5-DPCYXg9r6jBAkixTkeSPbjZXgMkk44a6kq17TA0nRO2HZ_KNB-ewhjTpQhwrTxOoPtH-ThwOqUSg7DQfSndlcm-yBWE0ZveGj3m5MbwQhzqt6qokQN8bgbAsPMVDF34gM613v_kEWCiiI3iWzIDjGfJ0nzPZTjm7q8MDGRaSKIEXI98XDhyv-Ah4dQiIMYLuP48tz-xg",
"responseContent": "{\"access_token\":\"FOMxkE5baqGjIAznqmhANgw1ITiwCK4CRdo0Pbi2p-A\",\"scope\":\"openid\",\"id_token\":\"eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIwNDAxOTg3NTE1NCIsIm5hbWUiOiJEci4gQmV2ZXJseSBQYWNvY2hhIiwiZW1haWwiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiaXNzIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb20iLCJzdWIiOiJCZXJuaWVjZTE1QGhvdG1haWwuY29tIiwiYXVkIjpbIjQwMTUwMjkzNjk4MTg3NzAiXSwiZXhwIjoyNTM0MDIxMjgwMDAsImlhdCI6MTYyODI3ODE2MH0.MGIEUwzFOiySL1klpCrGgcP7u70J1lGuxM154WV8l6HuGRlDbul8RsjjqkM-cZrX-wohWnXr4keqoQ8uJAEJwIuSL3Szt8KNpFD6fDwNfYC6SL_cTO1va3sgLSmDH7xnvaLfDMEedidw5-DPCYXg9r6jBAkixTkeSPbjZXgMkk44a6kq17TA0nRO2HZ_KNB-ewhjTpQhwrTxOoPtH-ThwOqUSg7DQfSndlcm-yBWE0ZveGj3m5MbwQhzqt6qokQN8bgbAsPMVDF34gM613v_kEWCiiI3iWzIDjGfJ0nzPZTjm7q8MDGRaSKIEXI98XDhyv-Ah4dQiIMYLuP48tz-xg\",\"token_type\":\"Bearer\",\"expires_in\":86400}"
}
```
Note that the resource list are encoded into the access token (when you are using JWT-Based Access Tokens) and are **not included on ID token**, as the intent of the ID token is not to be user as Bearer token when accessing the APIs.
### Introspection Request
When the access token is presented to a resource server (RS), the RS can use the introspection endpoint to query the resources list and therefore fulfill or not the request. The request below is an example of API request by the AS, asking Authlete API to process an introspection request from the RS to the AS.
```
curl --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection/standard" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data '{ "parameters": "token=FOMxkE5baqGjIAznqmhANgw1ITiwCK4CRdo0Pbi2p-A"
}'
```
A sample response is as follows, note the **aud** attribute that includes the list of resources that the access token is about.
```json highlight={7-8} theme={null}
{
"resultCode": "A145001",
"resultMessage": "[A145001] Introspection was performed successfully (type=access_token, active=true).",
"action": "OK",
"responseContent":
"{\"sub\":\"Berniece15@hotmail.com\",
\"aud\":[\"https://api.example.com/app/\",
\"https://contacts.example.com/\"],
\"scope\":\"openid\",
\"iss\":\"https://authlete.com\",
\"active\":true,
\"token_type\":\"Bearer\",
\"exp\":1628364560,
\"client_id\":\"4015029369818770\"}"
}
```
## Narrowing the resource during token request
The Resource Indicators specification creates the option for the client narrowing the list of resources that it is targeting. Let's say the client has requested the authorization to resources "[https://api.example.com/customers/](https://api.example.com/customers/)", "[https://api.example.com/orders/](https://api.example.com/orders/)" and "[https://api.example.com/inventory/](https://api.example.com/inventory/)", and the authorization for all those resources is granted. As long as the authorization retains, the client can request an access token restricted to "[https://api.example.com/orders/](https://api.example.com/orders/)" to run a specific use case and later request another access token to a different resource list.
The sequence below of requests shows an API client creating an access token for two resources.
```shell highlight={16-19} theme={null}
curl --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data '{
"parameters": "grant_type=authorization_code&
code=HEEutGTtpXDVD_3FcRATvshyqMgTsfhdcBFjy2TE3ro&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_verifier=XTzm4vHHPQXWUI45M1wJMiIO3oF5sZz6OhKWEvFdsG0",
"clientId" : "4015046106493768"
}'
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "4Ntb16h21_nFrc637_vXF-3HfR1g8or3646o4cAVyNE",
"accessTokenResources": [
"https://api.example.com/app/",
"https://contacts.example.com/"
],
...
"subject": "Bonita89@yahoo.com"
}
```
The second request creates an access token specific to the "*[https://contacts.example.com/](https://contacts.example.com/)*".
```shell highlight={8,17-22} theme={null}
curl --location --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data-raw '{
"parameters": "grant_type=refresh_token&
refresh_token=JWmEeN5BtBTL0um-pB9diYlJP5LRr7YREAJ8XrqY8CQ&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
resource=https%3A%2F%2Fcontacts.example.com%2F",
"clientId" : "4015046106493768"
}'
{
"resultCode": "A053001",
"resultMessage": "[A053001] The token request (grant_type=refresh_token) was processed successfully.",
"accessToken": "5XvZzUjduXQInKi5ywyj7hP0dScDmfhMmp_yl-fSKDI",
"accessTokenResources": [
"https://contacts.example.com/"
],
"resources": [
"https://contacts.example.com/"
],
"subject": "Bonita89@yahoo.com"
}
```
And later the same client changes the token to "*[https://api.example.com/app/](https://api.example.com/app/)"*.
```shell highlight={8,16-21} theme={null}
curl --location --request POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/token" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
--data-raw '{
"parameters": "grant_type=refresh_token&
refresh_token=JWmEeN5BtBTL0um-pB9diYlJP5LRr7YREAJ8XrqY8CQ&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
resource=https%3A%2F%2Fapi.example.com%2Fapp%2F",
"clientId" : "4015046106493768"
}'
{
"resultCode": "A053001",
"resultMessage": "[A053001] The token request (grant_type=refresh_token) was processed successfully.",
"accessToken": "W_g41P7zgGMW8TtYdb32sg9pBD7IthLQKFTP7hrB7ZY",
"accessTokenResources": [
"https://api.example.com/app/"
],
"resources": [
"https://api.example.com/app/"
],
...
"subject": "Bonita89@yahoo.com"
}
```
If you are planning to use this feature, check if the [single access token per subject is enabled](/configuration-reference/tokens-and-claims/enabling-single-access-token-per-subject). if that is the case, the access token will be invalidated when the refresh token is used. In your example, when the second request is done, the access token from the first became invalid. When the 3rd call is done, the access token of the 2nd call is invalidated.
# Rich Authorization Requests (RAR)
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/rich-authorization-requests-rar
Usage of OAuth 2.0 Rich Authorization Requests (RAR) in Authlete
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/protocol-extensions/rich-authorization-requests).
## Preface
This article explains Authlete's support for RAR and how to use this feature to handle fine-grained permissions. For detailed specifications, please refer to the [Overview of the RAR Specification
](/protocols-and-flows/protocol-extensions/rich-authorization-requests-rar-spec).
## Configuration
To utilize Authlete's RAR support, you must configure the `type` of authorization details (`authorization_details`) for both the service and the client.
* Service: Add the supported authorization details types
* Client: Specify the available authorization details types
### Service Level Configuration
To configure a list of supported authorization `types` at the service level:
1. Open the **Service Settings** in the **Management Console**.
2. Navigate to **Endpoints > Tokens and Claims > Advanced** and to **Scope**.
3. Under **Supported Authorization Details Types**, add the required `type` values (e.g., `payment_information`, `customer_information`).
Clients can request types from this list, but none are granted by default.

*List of supported authorization types by a service*
### Client Level Configuration
In the **Management Console**, you can configure the set of requestable authorization `types` for individual clients:
1. Open **Client Settings** for the specific client.
2. Navigate to **Endpoints > Advanced** and to **Scope**.
3. Under **Authorization Details Types**, add the `type` values that the client is allowed to request (e.g., `customer_information`).
4. Click **Add** to include more types as needed.
These settings control which `authorization_details` types the client can declare in its requests.

*List of authorization types that a client can request*
## Examples
### Authorization request
You can use RAR regardless of whether or not the request is pushed, or a request object mechanism is used. There are constraints in place for those mechanisms: if the RAR request is very large, it will require clients to use PAR, if you need to make tamper evident, the request object (or JAR) should be used.
#### Request
* URL-encoded
For instance, if an RAR is URL-encoded and sent in an authorization request by a client, a request from an AS (that receives the authorization request) to Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API would be like this:
```http theme={null}
curl --request POST 'https://us.authlete.com/api/{YOU_SERVICE_ID}/auth/authorization' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer gLmkGuAYT7V6LqH********kaWsk-QMvquoi1E' \
--data '{
"parameters": "client_id=4025660683512920&
scope=openid&
response_type=code&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_challenge=NcCW6zMwKWy5Mya8jopzE1SVeTBJBAHH1jU7TPpYK9A&
code_challenge_method=S256&
authorization_details=%5B%7B%22type%22%3A%20%22customer_information%22%2C%22locations%22%3A%20%5B%22https%3A%2F%2Fexample.com%2Fcustomers%22%2C%5D%2C%22actions%22%3A%5B%22read%22%2C%22write%22%5D%2C%22datatypes%22%3A%5B%22contacts%22%2C%22photos%22%5D%7D%5D"
}'
```
* JAR-wrapped
The same RAR content can be wrapped on a JAR as the sample below.
```http theme={null}
curl --request POST 'https://us.authlete.com/api/{YOU_SERVICE_ID}/auth/authorization' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer gLmkGuAYT7V6LqH********kaWsk-QMvquoi1E' \
--data-raw '{ "parameters": "client_id=4025658857453025&request=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.eyJpc3MiOiI0MDI1NjU4ODU3NDUzMDI1IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjQwMjU2NTg4NTc0NTMwMjUiLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiIsInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxlbmdlIjoiQW9OM29NcVFvUVF1RGhoQmc2VTF2Y0ljbXRtQ19QWm9UMGNoVEVSVktFZyIsImNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJhdXRob3JpemF0aW9uX2RldGFpbHMiOlt7InR5cGUiOiJjdXN0b21lcl9pbmZvcm1hdGlvbiIsImxvY2F0aW9ucyI6WyJodHRwczovL2V4YW1wbGUuY29tL2N1c3RvbWVycyJdLCJhY3Rpb25zIjpbInJlYWQiLCJ3cml0ZSJdLCJkYXRhdHlwZXMiOlsiY29udGFjdHMiLCJwaG90b3MiXX1dLCJpYXQiOjE2Mjg1MzE0NDQsIm5iZiI6MTYyODUzMTQ0NCwiZXhwIjoxNjI4NTMyMDQ5LCJqdGkiOiI4WjJEbGpCaUZVckpnTUt3bThiQ3EifQ.PIxVI2GFWi7B_frRfLg9r8AWEz7HGeopMeQo7MLYVEMGpOdoPkt5piBrLXI7PPI7ohrUmhxd-B4kZfm4WfkKH5qSub4A_mdd6pBpTWacBgfVQDIOvzE1yPrawCDEWQn2xgdYd1G-KM6pk8rDngOMEfaBbnoJ5C9krQtgYMHGbDIScgm8Y5AHf5aEF41FboZI67BlvbzXdxcJEPvB2zLGwV9twMrJ07OeRX0NVpIamhhEgfMQ87FyOsPVx9bqYUPN_VjwgB8lkKgrCdIkc9jPs2mQkpUbx0AIg8Pfmwyw0F5Vih7tgBbpb1LlwNgW36La3DPtTY9xSZ7SQGcyGxteIA" }'
```
#### Response
Authlete extracts and echoes back the authorization details to the AS so they can be used for collecting end user approval.
```json theme={null}
{
"resultCode": "A004001",
"resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 979748525706) for the authorization request from the client (ID = 4025658857453025). [response_type=code, openid=true]",
"action": "INTERACTION",
"authorizationDetails": {
"elements": [
{
"actions": ["read", "write"],
"dataTypes": ["contacts", "photos"],
"locations": ["https://example.com/customers"],
"type": "customer_information"
}
]
}, "requestObjectPayload": "{\"iss\":\"4025658857453025\",\"aud\":\"https://authlete.com\",\"response_type\":\"code\",\"client_id\":\"4025658857453025\",\"redirect_uri\":\"https://mobile.example.com/cb\",\"scope\":\"openid\",\"state\":\"af0ifjsldkj\",\"code_challenge\":\"AoN3oMqQoQQuDhhBg6U1vcIcmtmC_PZoT0chTERVKEg\",\"code_challenge_method\":\"S256\",\"authorization_details\":[{\"type\":\"customer_information\",\"locations\":[\"https://example.com/customers\"],\"actions\":[\"read\",\"write\"],\"datatypes\":[\"contacts\",\"photos\"]}],\"iat\":1628531444,\"nbf\":1628531444,\"exp\":1628532049,\"jti\":\"8Z2DljBiFUrJgMKwm8bCq\"}",
"ticket": "xTZCagNjVJUltVS-WD7CKZr1fp0zeFTAAva86Rmzuow"
}
```
### Token request
After the user has approved, an authorization code is generated and sent to the original client. The client will invoke the token endpoint authenticating itself (if required) to the AS, and Authlete's [/auth/token](/api-reference/token-endpoint/process-token-request) API behind the AS will generate an access token and optionally an ID token (if using openid).
Note that the authorization details are not encoded into the ID token.
#### Request
```http theme={null}
curl --request POST 'https://us.authlete.com/api/{YOU_SERVICE_ID}/auth/token' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer gLmkGuAYT7V6LqH********kaWsk-QMvquoi1E' \
--data '{ "parameters": "grant_type=authorization_code&code=wkPCpsu-HsMDeaNFBI78LydnmW7IyadBLloa3Mn7ZzM&redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&code_verifier=ZhoMDipQfa7iMabyG-wSQ83ATy1GCVvE8Lh3SlDZdNo", "clientId" : "4025658857453025" }'
```
#### Response
```json theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "Q4nS7518ZP4W0JaabMGJRBYGj919SY1IyxuDHZUw_Qc",
"action": "OK",
"authorizationDetails": {
"elements": [
{
"actions": [
"read",
"write"
],
"dataTypes": [
"contacts",
"photos"
],
"locations": [
"https://example.com/customers"
],
"type": "customer_information"
}
]
},
"idToken": "eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIzOTY3ODczNjY4MyIsIm5hbWUiOiJDYXNleSBQdXJkeSIsImVtYWlsIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwic3ViIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImF1ZCI6WyI0MDI1NjU4ODU3NDUzMDI1Il0sImV4cCI6MjUzNDAyMTI4MDAwLCJpYXQiOjE2Mjg1MzIzMjAsInNfaGFzaCI6ImJPaHRYOEY3M0lNalNQZVZBcXh5VFEifQ.Vrc3BcXtnAUtrnfSOPWagTWnV_SB0DL5cEp535pt33n8S4op94GSM51waTS6OcoZ-R7YKQ0l7FrMGxFl6MGFd6Wn_FjHNyu7J2TmEH9sARZTW7ZmWQo5euWkLx6NjgMAp_9LsjDXwB8Cjr3ujkVt3DdKRg6fkETwgGYYkEIrfdtRO_yJLANunHG-wm92TzOd44xXaTBF4bBinBcZFUrpr2nCPRM0rrUmBLw...",
"responseContent": "{\"access_token\":\"Q4nS7518ZP4W0JaabMGJRBYGj919SY1IyxuDHZUw_Qc\",\"authorization_details\":[{\"type\":\"customer_information\",\"locations\":[\"https://example.com/customers\"],\"actions\":[\"read\",\"write\"],\"datatypes\":[\"contacts\",\"photos\"]}],\"scope\":\"openid\",\"id_token\":\"eyJraWQiOiIzIiwiYWxnIjoiUlMyNTYifQ.eyJjcGYiOiIzOTY3ODczNjY4MyIsIm5hbWUiOiJDYXNleSBQdXJkeSIsImVtYWlsIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwic3ViIjoiQ2lhcmFfU3BvcmVyQGdtYWlsLmNvbSIsImF1ZCI6WyI0MDI1NjU4ODU3NDUzMDI1Il0sImV4cCI6MjUzNDAyMTI4MDAwLCJpYXQiOjE2Mjg1MzIzMjAsInNfaGFzaCI6ImJPaHRYOEY3M0lNalNQZVZBcXh5VFEifQ.Vrc3BcXtnAUtrnfSOPWagTWnV_SB0DL5cEp535pt33n8S4op94GSM51waTS6OcoZ-R7YKQ0l7FrMGxFl6MGFd6Wn_FjHNyu7J2TmEH...
...
}
```
### Token introspection Request
Upon receiving an API request with an access token, the resource server utilizes the introspection endpoint to check the authorization granted to that token.
#### Request
The following example demonstrates a resource server conducting an RFC 7662-compliant token introspection request to the authorization server, which subsequently invokes Authlete's [/auth/introspection/standard](/api-reference/introspection-endpoint/process-oauth-20-introspection-request) API.
```http theme={null}
curl --request POST 'https://us.authlete.com/api/{YOU_SERVICE_ID}/auth/introspection/standard' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer gLmkGuAYT7V6LqH********kaWsk-QMvquoi1E' \
--data '{ "parameters": "token=Q4nS7518ZP4W0JaabMGJRBYGj919SY1IyxuDHZUw_Qc"}'
```
#### Response
The authorization server extracts `responseContent` from the API response to return it to the resource server as the introspection response. This response includes the authorization details (`authorization_details`) tied to the access token. The resource server uses this information to determine whether to allow or deny the API request.
```json theme={null}
{
"resultCode": "A145001",
"resultMessage": "[A145001] Introspection was performed successfully (type=access_token, active=true).",
"action": "OK",
"responseContent": "{
\"sub\":\"Ciara_Sporer@gmail.com\",
\"authorization_details\":[ {
\"type\":\"customer_information\",
\"locations\":[\"https://example.com/customers\"],
\"actions\":[\"read\",\"write\"],
\"datatypes\":[\"contacts\",\"photos\"]
}], \"scope\":\"openid\",
\"iss\":\"https://authlete.com\",
\"active\":true,
\"token_type\":\"Bearer\",
\"exp\":1628618721,
\"client_id\":\"4025658857453025\"
}"
}
```
# Scope Attributes
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/scope-attributes
This article provides information on scope attributes and how to create them available since Authlete 2.0.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/scopes-consent/scope-attributes).
Scope attributes are arbitrary key-value pairs associated with a scope. Each scope can have multiple attributes, which can be used for making authorization decisions or other processing in your authorization server. Authlete also provides predefined scope attributes for system settings.
The key and value of a scope attribute are strings, and multiple attributes can be assigned to a single scope.
> This feature is available since Authlete 2.0.
## How to Create Scope Attributes
To create scope attributes for a service:
1. Log in to the [Authlete Management Console](https://console.authlete.com).
2. Navigate to **Service Settings > Tokens and Claims > Advanced > Scope**.
3. In the **Supported Scopes** section, click the **Add** button.

4. In the **Add/Edit Supported Scope** dialog:
* Enter a **Scope Name** and **Description**.
* In the **Scope Attributes** section, click **Add**.
* Enter the **Key** and **Value** for the scope attribute.
* Example: Key = `attr_key1`, Value = `attr_value1`.
* Click **Add** to save the attribute.

5. Click **Save Changes** to apply the changes.
## Predefined Scope Attributes
The following scope attributes are predefined by Authlete for special purposes:
| **Attribute Key** | **Attribute Value** | **Description** |
| - | - | - |
| `access_token.duration` | `number` | Configures access token duration for each scope. |
| `refresh_token.duration` | `number` | Configures refresh token duration for each scope. |
| `fapi` | `r` | Enables the FAPI read-only API profile. |
| `fapi` | `rw` | Enables the FAPI read-and-write API profile. |
| `regex` | `regular expression` | Enables a scope string with a dynamic value as part of it. |
| `fapi2` | `sp` | Enables the FAPI 2.0 Security Profile. |
| `fapi2` | `ms-authreq` | Enables the FAPI 2.0 Message Signing profile for Authorization Requests. |
| `fapi2` | `ms-authres` | Enables the FAPI 2.0 Message Signing profile for Authorization Responses. |
## How to Use Scope Attributes
Scope attributes can be utilized for various use cases, such as tagging risk levels to scopes or specifying required ACRs for granting a scope. In addition to custom use cases, you can leverage the predefined scope attributes provided by Authlete for specific system-level functionality.
### The authorization response
The authorization response from Authlete from [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) endpoint
includes the scope attributes as the response body below
```
{
"resultCode": "...",
"resultMessage": "...",
"acrEssential": false,
"action": "...",
"client": {...},
"clientIdAliasUsed": false,
"maxAge": 0,
"responseContent": "...",
"scopes": [
{
"defaultEntry": false,
"description": "A permission to request an OpenID Provider to issue an ID Token. See OpenID Connect Core 1.0, 3.1.2.1. for details.",
"name": "openid"
},
{
"defaultEntry": false,
"name": "payment"
}
],
"service": {
...
"supportedScopes": [
{
"defaultEntry": false,
"description": "A permission to request an OpenID Provider to issue an ID Token. See OpenID Connect Core 1.0, 3.1.2.1. for details.",
"name": "openid"
},
{
"attributes": [
{ "key": "meta", "value": "this profile requires a second factor authentication" },
{ "key": "fapi", "value": "rw" }
],
"defaultEntry": false,
"name": "payment"
},
...
],
...
}
}
```
### Using Java Authlete library
The following code snippet of an authorization server is an example using
Authlete's
[/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) for
parsing an authorization request from a client, and doing something based on
attributes of scopes included in the request.
```
// Call Authlete /api/authorization API.
AuthorizationResponse res = callAuthorizationAPI();
// Get scopes contained in the original authorization request.
Scope[] scopes = res.getScopes();
if (scopes == null || scopes.length() == 0) {
return;
}
// Check each scope's attributes.
for (Scope scp in scopes) {
// Get the scope attributes of the scope.
Pair[] attributes = scp.getAttributes();
if (attributes == null || attributes.length() == 0) {
continue;
}
// Check each attributes.
for (Pair attr in attributes) {
// The key of the attribute.
String key = attr.getKey();
// The value of the attirbute.
String value = attr.getValue();
// If the key is the target one.
if ("targetkey".equals(key)) {
// Do something with the value.
doSomething(value);
}
}
}
```
# Token Duration Per Client
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/token-duration-per-client
Configuring access/refresh/ID token duration per client in Authlete 3.0.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/token-lifecycle-policies/token-duration-per-client).
## Overview
In Authlete 3.0, token durations can be configured at multiple levels, including the **Service level** and the **Client level**. These durations can be set **statically** at these levels or configured **dynamically** based on specific scopes. For more details on dynamic configuration, refer to the [Token Duration Per Scope documentation](/configuration-reference/tokens-and-claims/token-duration-per-scope).
This short article explains how to set token durations for **access tokens**, **refresh tokens** and **ID tokens** at the **client level**.
***
## Configuring Token Duration for a Client
To configure the token duration for **access tokens** or **refresh tokens**, follow these steps:
### Access Token Duration
1. Log in to the [Authlete Management Console](https://console.authlete.com).
2. Navigate to **Client Settings > Tokens and Claims > Access Token**.
3. In the **General** section, locate the **Enter Duration** field.
4. Set the desired duration for access tokens in seconds (as shown in the screenshot below).
5. Click **Save Changes** to apply the configuration.

***
### Refresh Token Duration
1. Log in to the [Authlete Management Console](https://console.authlete.com).
2. Navigate to **Client Settings > Tokens and Claims > Refresh Token**.
3. In the **General** section, locate the **Enter Duration** field.
4. Set the desired duration for refresh tokens in seconds (as shown in the screenshot below).
5. Click **Save Changes** to apply the configuration.

***
### ID Token Duration
1. Log in to the [Authlete Management Console](https://console.authlete.com).
2. Navigate to **Client Settings > Tokens and Claims > ID Token**.
3. In the **ID Token Duration** section, locate the **Enter Duration** field.
4. Set the desired duration for ID tokens in seconds (as shown in the screenshot below).
5. Click **Save Changes** to apply the configuration.

***
## Example
Assume there is a service and two clients registered with it. The values of the access token duration are configured as follows:
| Entity | Access Token Duration (seconds) |
| - | - |
| Service | 86,400 |
| Client 1 | (not set) |
| Client 2 | 3,000 |
Under these conditions, Authlete’s [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API responds to implicit grant flow authorization requests from the clients as follows:
### Response to an authorization request from Client 1
```json theme={null}
{
"accessTokenDuration": 86400,
"responseContent": "https://client.example.org/cb/example.com#access_token=1zT0XRynwLryWYRKCYSDjrwku5sD-WQTCtC1tnfExZE&token_type=Bearer&expires_in=86400&scope=openid"
}
```
**Explanation**: The access token duration for the Service is used since no value is configured for Client 1.
### Response to an authorization request from Client 2
```json theme={null}
{
"accessTokenDuration": 3000,
"responseContent": "https://client.example.org/cb/example.com#access_token=xg79MJucCq8f8QPA2_o9_q5nfzgbRQycgVwYSvMSWTY&token_type=Bearer&expires_in=3000&scope=openid"
}
```
**Explanation**: The access token duration for Client 2 is used since it is explicitly set.
## See Also
* [How Authlete determines token duration](/configuration-reference/tokens-and-claims/how-authlete-determines-token-duration)
* [Token Duration Per Scope](/configuration-reference/tokens-and-claims/token-duration-per-scope)
# Token Duration Per Scope
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/token-duration-per-scope
Setting token duration individually per scope in Authlete 3.0 for more granular control and customization.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/token-lifecycle-policies/token-duration-per-scope).
## Overview
In Authlete 3.0, token durations can be configured at the **Service level**, **Client level**, and **Scope level**. Configuring token durations per scope provides fine-grained control, such as issuing shorter-lived tokens for higher-privilege scopes like `write` while allowing longer durations for lower-privilege scopes like `read`.
For more details on configuring token durations statically at the client level, refer to the [Token Duration Per Client documentation](/configuration-reference/tokens-and-claims/token-duration-per-client).
This short article explains how to set token durations for **access tokens** at the **scope level**.
***
## Configuring Token Duration Per Scope
To configure token durations for specific scopes in Authlete 3.0, follow these steps:
1. Log in to the [Authlete Management Console](https://console.authlete.com).
2. Navigate to **Service Settings > Tokens and Claims > Advanced**.
3. Under the **Supported Scopes** section, click the **Add** button.

***
4. In the **Add/Edit Supported Scope** dialog:
* Enter the **Scope Name** (e.g., `read` or `write`).
* Provide a description for the scope.
5. Scroll to the **Scope Attributes** section.
6. Click **Add** to add a new attribute:
* Set the **Key** to `access_token.duration`, `refresh_token.duration` or `id_token.duration` depending on the type of token you want to change the duration for.
* Set the **Value** to the desired token duration in seconds (e.g., `3600` for 1 hour).
7. Click **Add** again at the bottom of the dialog to save the scope attribute.

***
## Example
Assume there is a service and two scopes configured within it. The access token durations are set as follows:
| Entity | Access Token Duration (seconds) |
| - | - |
| Service | 86,400 |
| `read` scope | 3,600 |
| `write` scope | 600 |
Under these conditions, Authlete's [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API responds to implicit grant flow authorization requests based on the scopes provided.
***
### 1. Response to an authorization request with no scopes
```json theme={null}
{
"accessTokenDuration": 86400,
"responseContent": "https://client.example.org/cb/example.com
#access_token=xbNhif-bsWOPyRasrEFUFurBSQUHnarjv6sMz8cSDjg
&token_type=Bearer
&expires_in=86400
&scope=",
...
}
```
**Explanation**: The access token duration for the service is used since no scopes are provided.
### 2. Response to an authorization request with `read` scope
```json theme={null}
{
"accessTokenDuration": 3600,
"responseContent": "https://client.example.org/cb/example.com
#access_token=8ihMgxhMf-HYBy-O2rYVlMHEQD7WcvFGUhaXfP3YZHs
&token_type=Bearer
&expires_in=3600
&scope=read",
...
}
```
**Explanation**: The access token duration for the `read` scope is used.
### 3. Response to an authorization request with `write` scope
```json theme={null}
{
"accessTokenDuration": 600,
"responseContent": "https://client.example.org/cb/example.com#access_token=lZ4rjCLlwDvgO2wgOaXhDhNGMhpUE_yGi3pyTPcHFyU
&token_type=Bearer
&expires_in=600
&scope=write",
...
}
```
**Explanation**: The access token duration for the `write` scope is used.
### 4. Response to an authorization request with `read` and `write` scopes
```json highlight={2} theme={null}
{
"accessTokenDuration": 600,
"responseContent": "https://client.example.org/cb/example.com
#access_token=3zQNzTiX5MUxO1Gy0ZFfD7mhn3U1Cg3Q15rhjNob6uc
&token_type=Bearer
&expires_in=600
&scope=read+write",
...
}
```
**Explanation**: The access token duration for the write scope is used since it has the shortest duration among the requested scopes.
## See Also
* [How Authlete determines token duration](/v2/configuration-reference/token-lifecycle-policies/how-to-calculate-token-duration).
* [Token Duration Per Client](/configuration-reference/tokens-and-claims/token-duration-per-client)
# Token Revocation Policy
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/token-revocation-policy
Technical Information about how Authlete handles token revocation requests and the corresponding invalidation of access tokens and refresh tokens.
## Overview
When an access token (or refresh token) is invalidated using Authlete's [/auth/revocation](/api-reference/revocation-endpoint/process-revocation-request) API, the corresponding refresh token (or access token) will be invalidated at the same time.
## How Authlete handles token revocation requests
On receiving a revocation request form a client, an authorization server will call Authlete's [/auth/revocation](/api-reference/revocation-endpoint/process-revocation-request) API with "parameters" parameter that contains content of the revocation request.
The revocation request from the client contains the following parameters as defined in [RFC 7009](https://tools.ietf.org/html/rfc7009).
| parameter | Required | value |
| - | - | - |
| token | yes | The token that the client wants to get revoked. |
| token\_type\_hint | no | A hint about the type of the token submitted for revocation. |
Authlete will assume the type of the token using the token\_type\_hint and look up the token of that type in its token database first. If no tokens of the type found, Authlete will next look up the token of the other type. If Authlete finds the token of either type, it will remove the token and corresponding one i.e. access token / refresh token pair.
In other words, the token\_type\_hint is not a parameter to specify the type of tokens to be removed. It is to help Authlete locate the token from its records. Authlete removes both the access token and the refresh token.
Details are as listed below.
| token\_type\_hint | how to locate the token record | invalidation |
| - | - | - |
| (none) | look up the access token records first, and refresh token records next. | both access and refresh tokens |
| access\_token | same as above | same as above |
| refresh\_token | look up the refresh token records first, and access token records next. | same as above |
# Updating Issued Token(s)
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/updating-issued-token-s
Technical information on different methods provided by Authlete for updating issued token(s) including scopes expiration time and other properties.
## Overview
Authlete provides two types of APIs for updating information (e.g. scopes, expiration time) of issued token(s). These APIs are different each other in terms of how to specify token(s) and which information can be updated.
| | Updating a single token | Updating tokens related to a pair of a client and a user |
| - | - | - |
| API | [/auth/token/update](/api-reference/token-operations/update-access-token) | [/client/authorization/update](/api-reference/client-management/update-client-tokens) |
| How to specify token(s) | An access token ("accessToken") | A pair of a client ("clientId") and a user ("subject") |
| # of tokens | 1 | At least 1 |
| Item(s) to be updated | - Scopes ("scopes") - Expiration time ("accesstokenExpiresAt") - Properties ("properties") etc. | - Scopes ("scopes") |
| | | |
This article summarizes these methods.
## Updating a single token
Authlete provides [/auth/token/update](/api-reference/token-operations/update-access-token) API. You can modify values e.g. new expiration time ("accessTokenExpiresAt"), new scope ("scopes") etc. in an arbitrary token which has been issued to the client.
In addition, you can store any key/value pairs, which are not related to OAuth 2.0, to Authlete's database as properties ("properties"). This feature is useful when you have some information related to access token and would like to have Authlete to manage them.
## Updating tokens related to both a certain client and a user
Authlete provides [/client/authorization/update](/api-reference/client-management/update-client-tokens) API. Tokens to be updated are determined with a client ("clientId") and a user ("subject"), one of those who have granted access to the client.
This API is useful when you would like to update all of permissions which a specific user have allowed to a specific client. An example use case is as follows:
1. A user grants read-only access to a client for first-time usage. The client obtains a first access token which has "read" scope.
2. The user uses the client for some time and comes to consider that he/she can allow the client to update his/her information. Thus the user grants read-write access as well as read-only to the client. The client obtains another new second token which has "read" and "write."
3. The user keeps using the client and sometime changes his/her mind. The user would like to revoke the write permission. So the user logs in to authorization server, browses a list of clients connected, and revoke "write" access which has been granted to the client.
The authorization server can leverage the [/client/authorization/update](/api-reference/client-management/update-client-tokens) API to implement feature of 3 above. It can be done by just hitting this API with a new scope value ("read"), rather than doing search-and-update for each token.\\
For your reference, there is an example in another article ([Managing issued tokens granted by each user](/configuration-reference/tokens-and-claims/managing-authorizations-issued-tokens-granted-for-a-client-by-a-user)), section "Updating authorization scopes for one of clients which have been authorized by a user".
## Supplemental information
[Enabling Single Access Token](/configuration-reference/tokens-and-claims/enabling-single-access-token-per-subject)
# Using DPoP
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/using-dpop
Technical guide on implementing DPoP with Authlete APIs for supporting OAuth 2.0 Proof-of-Possession.
## Preface
This article describes how to support "[RFC 9449: OAuth 2.0 Demonstrating Proof-of-Possession (DPoP)](https://www.rfc-editor.org/rfc/rfc9449)
" with Authlete APIs.
> This feature is available in Authlete version 2.2 or later.
## Instructions
Authorization servers and resource servers support DPoP by following these steps.
1. Receiving a request that includes a "DPoP Proof JWT"
* A endpoint (EP) of an authorization server (e.g., Token EP, PAR EP, UserInfo EP) or a resource server receives a request, with "DPoP Proof JWT" as one of HTTP headers, from a client.
2. Extracting DPoP-related information
* The server extracts DPoP-related information (DPoP Proof JWT, HTTP method and HTTP target URI) from the request.
3. Making a request to Authlete's APIs
* The server make a request, that includes the content of the client's request and the above DPoP-related information as request parameters, to Authlete APIs (/auth/token, /auth/introspection, etc.)
***
## Example
The following is an example of how to support DPoP in an authorization server's token EP.
*DPoP support on token EP*
### 1. Receiving a request that includes a "DPoP Proof JWT"
Let us assume that the authorization server's token EP receives the following HTTP request. (wrapped for readability)
```
POST /token HTTP/1.1
Host: as.example.com
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IlJTMjU2IiwiandrIjp7Imt0eSI6IlJ
JTQSIsImUiOiJBUUFCIiwidXNlIjoic2lnIiwia2lkIjoib3A4cTNIaUNPVHQtej
BmUE1kanZsV21WN2QwS2FxVG9xcDNlZF9wdHA1QSIsImFsZyI6IlJTMjU2Iiwibi
I6ImdadHZ5VmRCb1ZEVTZHVTFLOGE2eHphb2EwR0F6dE9wcG1zRVZFN1NDZFpjT2
9WanpURlNRSnBXa0RQQmktNzBmeHdtc041c016Q3RPeXZ1Sk5XcFk5RHFUb0lWYX
bXM2VJOFRIU3dNNnZ0bXpNSWt5WDY0RXdZb2VuN2twbkhtZ3VDWUZ6dWxLM2FKZz
JKWFpOWXl1YTFuTTRmSGs3TnA0UlhTYmFBb2p5THR0SVFCSXZpOFVPcVI1dEYwaXZ
CQzRKSGxnUDVZTjNjTjIxOEFqeTVmcHFJdi1naE0zcmhtZjJuZFNpWnU2R3JPNmdP
OXFyaE05NXJRcEprSDlMcmdQaEE2ZkhQWVB3WnR5dEpBVGNVd1FWTThKTmwybTFWR
zlXS1o0SjdTTGJxdVNNNUZ4b29Oekc2dmZBWlhWLTBVTVBqSU5TZ3dJMDdBYi1Ocn
ZBUHNKUSJ9fQ.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIjoiUE9TVCIsIm
h0dSI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20vdG9rZW4iLCJpYXQiOjE2NzkzMDg3
NDN9.VOoyMJVR5RwyOWDGF42Oc_AElsGf6x2ZLW-X
Y8g8xXbH7WMdGKeSY0Mm8YIXblviyBCgD0y6oQH07Ds5uEtH0GSO15z5brawJmivr
UyiIiaH6UK2GKE42HNHrQ29Ln66cEqbDYxizN4hfu9yqoQgGEw6oRhiQaOLDLyzGE
QQRMkDPEz2LwhfsUhSzWPjtyhj2WBobH5WaNCybo6SBI2eGj1Pj97PdREK0luR7rn
3AU_N-PtIDFNeXbtYjRcE6NNrfSCWcrkLcuOHuKgPv61-OuqQ0jRbLpkyhTWjxtmj
zOuzWspfvg8ln8bOaw_y4VOspvUL5FBGI6P-RXBlBUrKow
...
grant_type=authorization_code&scope=read&
redirect_uri={redirect_uri}&code={code}&client_id={client_id}
```
### 2. Extracting DPoP-related information
The authorization server detects a DPoP header in the request, and extracts HTTP method, HTTP target URI and DPoP Proof JWT. These values are then included in a request to Authlete's [/auth/token](/api-reference/token-endpoint/process-token-request) API, as `htm`, `htu` and `dpop` respectively.
The following table shows the values in this example.
| Item | Value |
| - | - |
| Method (htm) | POST |
| Target URI (htu) | [https://as.example.com/token](https://as.example.com/token) |
| DPoP Proof JWT (dpop) | `eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IlJTMjU2IiwidHlwIjoiYXQrand0In0.eyJraWQiOiJvOGpsc1IydXBkcnRjZ280NU5CejFrdDJ6aWg4cHNKV09KUU1lQ3E5OVdGIiwic2lnbmF0dXJlIjoiZ3Z0MmFNRXJsV0RqVk5ST3U1WlA4R2h3dnBJa0tLVTRjN3hwU1ZyT3ciLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoib3A4cTNIaUNPVHQtejBmUE1kanZsV21WN2QwS2FxVG9xcDNlZF9wdHA1USJ9.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20vdG9rZW4iLCJpYXQiOjE2NzkzMDg3NDN9.VOoyMJVR5RwyOWDGF42Oc_AElsGf6x2ZLW-X Y8g8xXbH7WMdGKeSY0Mm8YIXblviyBCgD0y6oQH07Ds5uEtH0GSO15z5brawJmivrU yiIiaH6UK2GKE42HNHrQ29Ln66cEqbDYxizN4hfu9yqoQgGEw6oRhiQaOLDLyzGEQQRMKB PEz2LwhfsUhSzWPjtyhj2WBobH5WaNCybo6SBI2eGj1Pj97PdREK0luR7rn3AU_N-PtI DFNeXbtYjRcE6NNrfSCWcrkLcuOHuKgPv61-OuqQ0jRbLpkyhTWjxtmjzOuzWspfvg8l n8bOaw_y4VOspvUL5FBGI6P-RXBlBUrKow` |
### 3. Making a request to Authlete's APIs
The following is a request body from the authorization server to the [/auth/token](/api-reference/token-endpoint/process-token-request) API.
```
{
"parameters": "grant_type=authorization_code&scope=read&
redirect_uri={redirect_uri}&code={code}&client_id={client_id}",
"htm":"POST",
"htu":"https://as.example.com/token",
"dpop":"eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IlJTMjU2IiwiandrIjp7Imt0eSI6IlJTQSIs
ImUiOiJBUUFCIiwidXNlIjoic2lnIiwia2lkIjoib3A4cTNIaUNPVHQtejBmUE1kanZs
V21WN2QwS2FxVG9xcDNlZF9wdHA1QSIsImFsZyI6IlJTMjU2IiwibiI6ImdadHZ5VmRC
b1ZEVTZHVTFLOGE2eHphb2EwR0F6dE9wcG1zRVZFN1NDZFpjT29WanpURlNRSnBXa0RQ
QmktNzBmeHdtc041c016Q3RPeXZ1Sk5XcFk5RHFUb0lWYXBXM2VJOFRIU3dNNnZ0bXpN
SWt5WDY0RXdZb2VuN2twbkhtZ3VDWUZ6dWxLM2FKZzJKWFpOWXl1YTFuTTRmSGs3TnA0
UlhTYmFBb2p5THR0SVFCSXZpOFVPcVI1dEYwaXZCQzRKSGxnUDVZTjNjTjIxOEFqeTVm
cHFJdi1naE0zcmhtZjJuZFNpWnU2R3JPNmdPOXFyaE05NXJRcEprSDlMcmdQaEE2ZkhQ
WVB3WnR5dEpBVGNVd1FWTThKTmwybTFWRzlXS1o0SjdTTGJxdVNNNUZ4b29Oekc2dmZB
WlhWLTBVTVBqSU5TZ3dJMDdBYi1OcnZBUHNKUSJ9fQ.eyJqdGkiOiItQndDM0VTYzZhY
2MybFRjIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20vd
G9rZW4iLCJpYXQiOjE2NzkzMDg3NDN9.VOoyMJVR5RwyOWDGF42Oc_AElsGf6x2ZLW-X
Y8g8xXbH7WMdGKeSY0Mm8YIXblviyBCgD0y6oQH07Ds5uEtH0GSO15z5brawJmivrUyi
IiaH6UK2GKE42HNHrQ29Ln66cEqbDYxizN4hfu9yqoQgGEw6oRhiQaOLDLyzGEQQRMkB
PEz2LwhfsUhSzWPjtyhj2WBobH5WaNCybo6SBI2eGj1Pj97PdREK0luR7rn3AU_N-PtI
DFNeXbtYjRcE6NNrfSCWcrkLcuOHuKgPv61-OuqQ0jRbLpkyhTWjxtmjzOuzWspfvg8l
n8bOaw_y4VOspvUL5FBGI6P-RXBlBUrKow"
}
```
Authlete returns the following response if the above request is successful.
```
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "{access_token}",
"action": "OK",
"responseContent": {
"access_token": "{access_token}",
"scope": "read",
"token_type": "DPoP",
"expires_in": 60
},
...
}
```
The authorization server will then return a token response to the client with responseContent as its body.
## Further Reading
* [Illustrated DPoP (OAuth Access Token Security Enhancement)](https://darutk.medium.com/illustrated-dpop-oauth-access-token-security-enhancement-801680d761ff)
# Using JWT-Based Access Tokens
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens
Guide on enabling the feature in Authlete to issue JWT-formatted access tokens and specifying additional claims available in Authlete 2.1 and later.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/token-issuance-formats/jwt-based-access-token).
## Preface
Authlete has a feature that can issue JWT-formatted access tokens. This article describes how to enable the feature, and how to specify additional claims to an access token.
> This feature is available in Authlete 2.1 and later.
## Enabling The Feature
Register a JWK set document to "JWK Set Content" section in Service Settings. See the following article for instructions.
* [JWK set settings for an Authlete service](/configuration-reference/key-management/jwk-set-settings-for-an-authlete-service)
After the registration, choose an appropriate "Access Token Signature Algorithm" in the same "Token" tab. For example, you would choose "ES256" if you have registered an ES256 signing key (shown in the article above).

*Access Token Signature Algorithm*
After the change, access tokens to be issued by Authlete are JWT-formatted.
## Embedding additional properties into access tokens
You can embed arbitrary key-value pairs into access tokens in either of the following ways:
1. Adding string values using “[Extra Properties](/configuration-reference/tokens-and-claims/how-to-add-extra-properties-to-an-access-token)”
2. Adding arbitrary claims using jwtAtClaims parameter
The following sections describe the overview.
> The jwtAtClaims parameter is available in Authlete 2.3 or later.
## 1. Add String Values Using “Extra Properties”
The “[Extra Properties](/configuration-reference/tokens-and-claims/how-to-add-extra-properties-to-an-access-token)
” feature allows an authorization server to associate arbitrary properties with either an access token or an authorization code. By using this feature for the JWT-formatted access tokens, visible (= not-hidden) extra properties are included in the access tokens as custom claims so that resource servers can extract them.
### Example
The following is an execution example of /auth/token API of the Authlete service configured to issue JWT-formatted access tokens. This example also shows output when using extra properties.
You will see a JWS signed JWT as a value of "access\_token" in "responseContent" (content of token response). The same value is also provided as "jwtAccessToken". (folded for readability)
* Request
```
curl -v -X POST https://us.authlete.com/api/{serviceId}/auth/token \
-H 'Authorization: Bearer V5a40R6********C*******95q4HC0Z-T0YKD9-nR6F' \
-H 'Content-Type: application/json' \
-d '{"parameters":
"clientId":"...",
"clientSecret":"...",
"parameters":
"grant_type=authorization_code&
redirect_uri=https://client.example.org/cb/example.com&
code=..."
}'
```
* Response
```
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "xx2...AFQ",
"accessTokenDuration": 86400,
"accessTokenExpiresAt": 1591690046802,
"action": "OK",
"clientId": 17201083166161,
"clientIdAliasUsed": false,
"grantType": "AUTHORIZATION_CODE",
"jwtAccessToken": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ. \
eyJleGFtcGxlX3BhcmFtZXRlciI6ImV4YW1wbGVfdmFsdWUiLCJz \
dWIiOiJ0ZXN0dXNlcjAxIiwic2NvcGUiOm51bGwsImlzcyI6Imh0 \
dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTkxNjkwMDQ2LCJp \
YXQiOjE1OTE2MDM2NDYsImNsaWVudF9pZCI6IjE3MjAxMDgzMTY2 \
MTYxIiwianRpIjoieHgycnNJODBER1Z4bHFLdTFQV2R4eWJSLTdB \
eTZWamJNcTAxY3dNYkFGUSJ9. \
-9RsKUSnJHmdqNtNpWbbbTah1YxTkicsabIgxrLWHtGiLsTIaEj_ \
q39AvKYWrmfnw5y0dfaD3qtTScxI94OSIg",
"properties": [
{
"hidden": false,
"key": "example_parameter",
"value": "example_value"
}
],
"refreshToken": "4rA7H1uRZkCQ7Yd0PN98h7IUqW7zT8p1a_BAg0jEyow",
"refreshTokenDuration": 864000,
"refreshTokenExpiresAt": 1592467646802,
"responseContent": "{"access_token": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ. \
eyJleGFtcGxlX3BhcmFtZXRlciI6ImV4YW1wbGVfdmFsdWUiLCJz \
dWIiOiJ0ZXN0dXNlcjAxIiwic2NvcGUiOm51bGwsImlzcyI6Imh0 \
dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTkxNjkwMDQ2LCJp \
YXQiOjE1OTE2MDM2NDYsImNsaWVudF9pZCI6IjE3MjAxMDgzMTY2 \
MTYxIiwianRpIjoieHgycnNJODBER1Z4bHFLdTFQV2R4eWJSLTdB \
eTZWamJNcTAxY3dNYkFGUSJ9. \
-9RsKUSnJHmdqNtNpWbbbTah1YxTkicsabIgxrLWHtGiLsTIaEj_ \
q39AvKYWrmfnw5y0dfaD3qtTScxI94OSIg", \
"refresh_token":"4rA7H1uRZkCQ7Yd0PN98h7IUqW7zT8p1a_BAg0jEyow",
"example_parameter": "example_value",
"scope": null,
"token_type": "Bearer",
"expires_in": 86400}",
"subject": "testuser01"
}
```
Header and payload of the JWT access token are as follows. The latter includes the extra properties (**"example\_parameter":"example\_value"**).
* Header
```
{
"kid": "1",
"alg": "ES256"
}
```
* Payload
```json highlight={2} theme={null}
{
"example_parameter": "example_value",
"sub": "testuser01",
"scope": null,
"iss": "https://authlete.com",
"exp": 1591690046,
"iat": 1591603646,
"client_id": "17201083166161",
"jti": "xx2rsI80DGVxlqKu1PWdxybR-7Ay6VjbMq01cwMbAFQ"
}
```
## 2. Add Arbitrary Claims Using `jwtAtClaims` Parameter
The `jwtAtClaims` request parameter allows you to add JSON objects as claims to a JWT access token. This parameter is available when making a request to the following Authlete APIs.
* [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response)
* [/auth/token](/api-reference/token-endpoint/process-token-request)
* [/auth/token/issue](/api-reference/token-endpoint/issue-token-response)
* [/auth/token/create](/api-reference/token-operations/create-access-token)
* [/backchannel/authentication/complete](/api-reference/ciba/complete-backchannel-authentication)
* [/device/complete](/api-reference/device-flow/complete-device-authorization)
The format of the value specified for `jwtAtClaims` is a JSON object. The properties in the JSON object specified in `jwtAtClaims` will be added to the JWT access token payload.
### Example
Below is an example using jwtAtClaims as one of the parameters of the request to the [/auth/authorization/issue](/api-reference/authorization-endpoint/issue-authorization-response) API.
```shell highlight={7} theme={null}
curl -v -X POST https://us.authlete.com/api/{serviceId}/auth/authorization/issue \
-H 'Authorization: Bearer V5a40R6********C*******95q4HC0Z-T0YKD9-nR6F' \
-H 'Content-Type: application/json' \
-d '{
"ticket": "{{ticket}}",
"subject": "abc",
"jwtAtClaims": "{\"realm_access\": {\"roles\":[\"A\", \"B\"]}}" ...
}'
```
The resulting JWT access token looks like this:
```json highlight={4-9} theme={null}
{
"sub": "abc",
"iss": "https://example.authlete.com",
"realm_access": {
"roles": [
"A",
"B"
]
},
...
}
```
### Relationship Between JWT-Based Access Tokens and Identifier-Type Access Tokens
When the Authlete token issuance endpoint successfully issues an access token, in addition to the `responseContent` that is returned to the authorization server, the following properties are also included in the response:
* `accessToken`
* `jwtAccessToken` (if configured to issue a JWT-based token, this is identical to the `access_token` in the `responseContent`)
The authorization server can freely construct its own token response using the above response. For example, it could add a custom property containing an identifier access token to the JSON in `responseContent` and return both tokens, or it could decide per client whether to return `accessToken` or `jwtAccessToken`.
Additionally, the `jti` claim contained in a JWT-based access token matches the value of Authlete’s identifier access token.
### Revocation Check for JWT-Based Access Tokens
A major advantage of JWT-based access tokens is that they do not require contacting the authorization server for revocation checking.
However, in certain special scenarios, you may want to perform revocation and revocation checks even for JWT-based access tokens.
For instance, you might prioritize performance by validating JWT-based tokens locally in general, but perform an additional revocation check with the authorization server for critical operations.
To implement revocation for JWT-based tokens issued by Authlete, it’s necessary to understand how Authlete handles them.
#### How to Revoke a JWT-Based Access Token
Authlete’s revocation endpoint ([/auth/revocation](/api-reference/revocation-endpoint/process-revocation-request)) does **not** directly support JWT-type access tokens, so submitting a JWT token itself to the endpoint will not revoke it. If you want to revoke a JWT-based access token (i.e., mark it as revoked in Authlete’s internal database), you must send the **identifier token** contained in the JWT’s `jti` claim to the revocation endpoint.
```bash theme={null}
### Cannot be revoked
curl -X POST \
https://us.authlete.com/api/{serviceId}/auth/authorization/revocation \
-H 'Authorization: Bearer V5a40R6********C*******95q4HC0Z-T0YKD9-nR6F' \
-H 'Content-Type: application/json' \
--data-raw '{
"parameters": "eyJraWQiOiIxIiwiYWxnIjoiRVMyNTYifQ....&token_type_hint=access_token",
"clientId": "...",
"clientSecret": "..."
}'
### Can be revoked
curl -X POST \
https://us.authlete.com/api/{serviceId}/auth/authorization/revocation \
-H 'Authorization: Bearer V5a40R6********C*******95q4HC0Z-T0YKD9-nR6F' \
-H 'Content-Type: application/json' \
--data-raw '{
"parameters": "xx2rsI80DGVxlqKu1PWdxybR-7Ay6VjbMq01cwMbAFQ....&token_type_hint=access_token",
"clientId": "...",
"clientSecret": "..."
}'
```
Regarding revocation checking, Authlete’s proprietary **token introspection API** (`/auth/introspection`) accepts JWT access tokens and verifies the database record based on the `jti`.
Therefore, if a JWT access token has been revoked using its `jti` claim, `/auth/introspection` will correctly reflect its revoked status.
On the other hand, `/auth/introspection/standard` only validates the JWT’s signature and expiration time and does **not** check the database record.
As a result, it does not perform revocation verification based on the `jti` claim.
## See also
* [OAuth Access Token Implementation](https://medium.com/@darutk/oauth-access-token-implementation-30c2e8b90ff0)
* [5. Authlete’s Implementation](https://medium.com/@darutk/oauth-access-token-implementation-30c2e8b90ff0#1061)
* [com.authlete.common.dto.Service](http://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html)
# Using “Parameterized Scopes”
Source: https://developers.authlete.com/configuration-reference/tokens-and-claims/using-parameterized-scopes
Configuring and utilizing parameterized scopes feature in Authlete to use dynamic values in scope strings.
## Introduction
Authlete supports “parameterized scopes” so that you can use a scope string with a dynamic value as a part of it. This article describes how to configure and use the feature.
> This feature is available since Authlete 2.2.
***
## Configuring parameterized scopes
A parameterized scope can be defined by specifying a scope attribute for a static scope. The instructions are as follows.1. Open the scope attribute setting window. (See “[Scope Attributes](/configuration-reference/tokens-and-claims/scope-attributes)
” for detailed instructions) 2. Enter “regex” (a special value for parameterized scopes) in the “key” field of the scope attribute. 3. Enter a regular expression in the “value” field of the scope attribute. The expression must match a scope string that may include dynamic values.
The example below specifies a regular expression “^consent:.+\*\$” for a static scope “consent” to configure Authlete to accept scope values that match the expression (e.g. “consent:urn:bancoex:C1DD33123”) as well as the static value “consent.”
*Example of setting up a parameterized scope for a "consent" scope.*
***
## Example of parameterized scopes
### Authorization request
The following request/response example using curl shows how Authlete's [/auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API
processes an authorization request that includes a dynamic value in its “scope” parameter.
* Request
```shell highlight={8} theme={null}
$ curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/authorization" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{"parameters":
"redirect_uri=...
&client_id=...
&response_type=code
&scope=email+consent:urn:bancoex:C1DD33123"}'
```
* Response
```json highlight={9-13} theme={null}
{
"resultCode": "A004001",
"resultMessage":
"[A004001] Authlete has successfully issued a ticket
to the service (API Key = 2407...) for the
authorization request from the client (ID = 2614...).
[response_type=code, openid=false]",
"action": "INTERACTION",
"dynamicScopes": [
{
"name": "consent",
"value": "consent:urn:bancoex:C1DD33123"
}
],
"scopes": [
{
"defaultEntry": false,
"description": "A permission to request an OpenID Provider to include the email claim and the email_verified claim in an ID Token. See OpenID Connect Core 1.0, 5.4. for details.",
...
"name": "email"
}
],
...
```
The scope that matches the regular expression, specified as the value of the scope attribute, is stored in “dynamicScopes” array. The “name” and “value” field of the array contain the scope name, and the scope string specified in the “value” parameter in the authorization request, respectively.
### Introspection
You can check if an access token has specific “parameterized scopes” in the same way as the checking for static scopes. The following is an example of [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API request/response using curl.
* Request
```shell highlight={5} theme={null}
curl -s -X POST "https://us.authlete.com/api/${SERVICE_API_KEY}/auth/introspection" \
-H "Authorization: Bearer ${SERVICE_ACCESS_TOKEN}" \
-H 'Content-type: application/json' \
-d '{"token":"L2sOx9...",
"scopes":["email","consent:urn:bancoex:C1DD33123"]}'
```
* Response
```json highlight={9-10} theme={null}
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
"clientId": ...,
"expiresAt": ...,
"responseContent": "Bearer error=\"invalid_request\"",
"scopes": [
"email",
"consent:urn:bancoex:C1DD33123" ],
"subject": "testuser01",
...
}
```
For details on checking the scope covered by a token using the [/auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API
, see "[Checking if an access token has particular scopes](/configuration-reference/endpoints/checking-if-an-access-token-has-particular-scopes)
".
## Discovery Document
Only the scope name will be shown in a discovery document even if “parameterized scopes” feature is enabled. The following is an example of [/service/configuration](/api-reference/service-management/get-service-configuration) API response.
```json highlight={14} theme={null}
{
"issuer": "...",
"authorization_endpoint": "...",
"token_endpoint": "...",
"userinfo_endpoint": "...",
"jwks_uri": "...",
"scopes_supported": [
"address",
"email",
"openid",
"offline_access",
"phone",
"profile",
"consent" ],
...
}
```
***
## See also
* [DynamicScope](http://authlete.github.io/authlete-java-common/com/authlete/common/dto/DynamicScope.html)
class in authlete-java-common library
# OpenID Federation 1.0
Source: https://developers.authlete.com/guides/flows-and-protocols/identity-and-federation/openid-federation-1-0
Explanation about OpenID Federation 1.0 and how Authlete supports the specification.
This page is for Authlete 3.0. For 2.x, see [OpenID Federation 1.0 (2.x)](/v2/guides/flows-and-protocols/identity-and-federation/openid-federation-1-0).
# Introduction
This document explains **[OpenID Federation 1.0][OPENID_FEDERATION]**.
Revision History
Date
Changes
2023-01-01
2024-08-03
# Overview
**[OpenID Federation 1.0][OPENID_FEDERATION]** defines a mechanism where an **identity provider** / **authorization server** and a **relying party** (**client**) that have no direct relationship trust each other based on **trust chains** and the identity provider accepts OAuth/OIDC requests from the relying party without needing prior registration of the relying party.
Trust chains are uni-directional. Therefore, two trust chains are built when an identity provider and a relying party trust each other. One is a trust chain for the relying party to trust the identity provider.
The other is a trust chain for the identity provider to trust the relying party.
A trust chain is built by third-party **authorities**. An authority that works as the origin of a trust chain is called "**trust anchor**" and other authorities in between the trust anchor and the target **leaf entity** (an identity provider or a relying party) are called **intermediate authority**. Trust anchors and intermediate authorities are conceptually equivalent to **root certificate authority** (Root CA) and **intermediate certificate authority** (Intermediate CA) in the **[Public Key Infrastructure][PKI]** (PKI).
A relying party trusts one or more trust anchors. If any of the trust anchors has a path to the target identity provider, the relying party can trust the identity provider.
Likewise, an identity provider trusts one or more trust anchors. If any of the trust anchors has a path to the target relying party, the identity provider can trust the relying party.
The diagram below illustrates an overview of OpenID Federation 1.0 that has been explained in this section.
# Trust Chain
Technically speaking, a trust chain is a sequence of **JWT**s ([RFC 7519 JSON Web Token][RFC_7519]) that are issued by a leaf entity, zero or more intermediate authorities, and a trust anchor.
## JWT Issued by Leaf Entity
A leaf entity issues a JWT that is signed by the leaf entity's private key.
The OpenID Federation specification requires that the payload part of the JWT issued by a leaf entity contain the public key corresponding to the private key which is used to sign the JWT. The `jwks` claim is used to include the public key.
As a result, the JWT becomes a *self-signed* JWT. Therefore, both the `iss` claim (Issuer; [RFC 7519 Section 4.1.1][RFC_7519_411]) and the `sub` claim (Subject; [RFC 7519 Section 4.1.2][RFC_7519_412]) in the payload part of the JWT hold the leaf entity's identifier.
## JWT Issued by Intermediate Authority
An intermediate authority (or a trust anchor if no intermediate authority exists in a trust chain) that authorizes the leaf entity issues a JWT that is signed by the intermediate authority's private key.
The purpose of the JWT is to indicate that the intermediate authority authorizes the leaf entity. For the purpose, the `sub` claim holds the leaf entity's identifier while the `iss` claim holds the intermediate authority's identifier.
In addition, the `jwks` claim holds the public key of the leaf entity.
## JWT Issued by Trust Anchor
A trust chain is terminated by a JWT signed by a trust anchor's private key.
The JWT indicates that the trust anchor authorizes the last intermediate authority. The value of the `sub` claim is the last intermediate authority's identifier and the value of the `iss` claim is the trust anchor's identifier. If no intermediate authorities are involved, the trust anchor issues a JWT that directly authorizes the leaf entity. In that case, the `sub` claim holds the leaf entity's identifier.
The JWT issued by the trust anchor contains the public key of the last intermediate authority.
A trust chain may optionally have one more JWT that is self-signed by the trust anchor at the end.
As it is a self-signed JWT, both the `iss` claim and the `sub` claim hold the trust anchor's identifier.
## Trust Chain Summary
The following diagram depicts the entire trust chain.
# Trust Chain Resolution
## Entity Configuration
The resolution process of a trust chain starts from getting the self-signed JWT of a leaf entity.
The location of the self-signed JWT is defined in the OpenID Federation specification. It is a URL constructed by concatenating the leaf entity's identifier and the fixed string `"/.well-known/openid-federation"`.
```
Leaf Entity's Identifier + /.well-known/openid-federation
```
If you are familiar with the specifications of [OpenID Connect Discovery 1.0][OIDC_DISCOVERY] and [RFC 8414 OAuth 2.0 Authorization Server Metadata][RFC_8414], you will guess that the leaf entity's identifier is a URL that starts with `http://` or `https://`. Your guess is correct.
Participants of a federation, i.e., identity providers, relying parties, intermediate authorities and trust anchors, are called "**federation entity**". Each federation entity has a globally unique identifier. The identifier is called "**entity ID**". And, entity IDs must be URLs whose scheme part is either `http` or `https`. It is expected that Web servers are running on hosts identified by the URLs so that federation entities can issue JWTs that are necessary to build trust chains.
For instance, when the entity ID of a relying party is `https://rp.example.com/123`, the self-signed JWT of the relying party is expected to be published at the following URL.
```
https://rp.example.com/123/.well-known/openid-federation
```
In general, self-signed JWTs of federation entities that are published at their respective `/.well-known/openid-federation` are called "**entity configuration**".
## Authority Hints
The payload part of an entity configuration contains the `authority_hints` claim. It is a JSON array listing entity IDs of intermediate authorities and/or trust anchors that can issue a JWT indicating that the authority authorizes the federation entity of the entity configuration.
For example, when a relying party whose entity ID is `https://rp.example.com/123` has a direct intermediate authority whose entity ID is `https://ia.example.com`, the `authority_hints` claim in the entity configuration of the relying party contains `https://ia.example.com`.
Likewise, if the intermediate authority has a trust anchor whose entity ID is `https://ta.example.com` as a direct authority, the `authority_hints` claim in the entity configuration of the intermediate authority contains `https://ta.example.com`.
On the other hand, because a trust anchor has no upper authority by definition, the entity configuration of a trust anchor does not include the `authority_hints` claim.
## Federation Fetch Endpoint
The next step after getting the entity configuration of a leaf entity is to get a JWT from the direct authority of the leaf entity that indicates that the authority authorizes the leaf entity.
Authorities are supposed to provide an endpoint that issues JWTs for the purpose. The endpoint is called "**federation fetch endpoint**". The table below is a summary of the specification of the endpoint.
**Request**
HTTP Method
GET
Parameters
`iss`
Optional
The entity ID of the issuer.
`sub`
Optional
The entity ID of the subject.
**Response**
HTTP Status Code
200
Content-Type
`application/entity-statement+jwt`
For example, when an intermediate authority `https://ia.example.com` provides its federation fetch endpoint at `https://ia.example.com/fetch`, a JWT that indicates that the intermediate authority authorizes a federation entity `https://rp.example.com/123` can be obtained by making the following HTTP request.
```
https://ia.example.com/fetch?sub=https://rp.example.com/123
```
The location of the federation fetch endpoint of an authority is written in its entity configuration. The `federation_fetch_endpoint` claim holds the URL of the endpoint, but the claim is put at a bit deep place. It is placed inside the `federation_entity` JSON object which is inside the `metadata` JSON object.
The process of fetching JWTs from federation fetch endpoints is repeated until a JWT from a trust anchor is obtained. If the trust anchor is one of trusted ones, it means that a trust chain from the leaf entity to the trust anchor can be built. On the other hand, if the trust anchor is unknown, the trust chain cannot be used to trust the leaf entity. In this case, other paths need to be tried. To be concrete, other entries in the `authority_hints` claim need to be tried.
## Trust Chain Resolution Flow
Let's review the flow of trust chain resolution.
**(1)** The first step is to get the entity configuration of a leaf entity.
**(2)** The entity configuration contains the `authority_hints` claim. It is a JSON array listing entity IDs of intermediate authorities or trust anchors. Web servers of authorities can be identified by the entity IDs. In this example, an intermediate authority `https://ia.example.com` is included in the `authority_hints` claim.
**(3)** The entity configuration of the intermediate authority is needed to know the URL of its federation fetch endpoint.
**(4)** An HTTP request with the `sub` request parameter is sent to the federation fetch endpoint. In this example, the entity ID of the leaf entity (`https://rp.example.com/123`) is specified as the value of the `sub` request parameter.
**(5)** The federation fetch endpoint returns a JWT that indicates that the intermediate authority authorizes the leaf entity.
**(6)** The upper authorities of the intermediate authority are listed in the `authority_hints` claim in the entity configuration of the intermediate authority. In this example, a trust anchor `https://ta.example.com` is included in the `authority_hints` claim.
**(7)** The entity configuration of the trust anchor is needed to know the URL of its federation fetch endpoint.
**(8)** An HTTP request with the `sub` request parameter is sent to the federation fetch endpoint. In this example, the entity ID of the intermediate authority (`https://ia.example.com`) is specified as the value of the `sub` request parameter.
**(9)** The federation fetch endpoint returns a JWT that indicates that the trust anchor authorizes the intermediate authority.
**(10)** The entity configuration of the leaf entity and the JWTs issued from the federation fetch endpoints consist of a trust chain.
## Trust Chain Resolution Summary
The following diagram depicts the entire flow of trust chain resolution.
All the JWTs that appear in trust chain resolution are called "**entity statement**". An entity configuration is a kind of entity statement.
# Metadata
An entity configuration contains **metadata** of the federation entity.
## Identity Provider Metadata
Metadata of an identity provider are put in the `openid_provider` JSON object which is in the `metadata` JSON object.
Metadata that are defined in [OpenID Connect Discovery 1.0][OIDC_DISCOVERY], [RFC 8414 OAuth 2.0 Authorization Sever Metadata][RFC_8414] and other standard specifications may appear in the `openid_provider` JSON object.
In addition, the OpenID Federation specification defines the following server metadata.
* `client_registration_types_supported`
* `organization_name`
* `federation_registration_endpoint`
* `request_authentication_methods_supported` (removed in OpenID Federation 1.0 final)
* `request_authentication_signing_alg_values_supported` (removed in OpenID Federation 1.0 final)
* `signed_jwks_uri`
* `jwks`
## Relying Party Metadata
Metadata of a relying party are put in the `openid_relying_party` JSON object which is in the `metadata` JSON object.
Metadata that are defined in [OpenID Connect Dynamic Client Registration 1.0][OIDC_REGISTRATION], [RFC 7591 OAuth 2.0 Dynamic Client Registration Protocol][RFC_7591] and other standard specifications may appear in the `openid_relying_party` JSON object.
In addition, the OpenID Federation specification defines the following client metadata.
* `client_registration_types`
* `organization_name`
* `signed_jwks_uri`
## Authority Metadata
Metadata of authorities (intermediate authorities and trust anchors) are put in the `federation_entity` JSON object which is in the `metadata` JSON object.
The OpenID Federation specification defines the following metadata for authorities.
* `federation_fetch_endpoint`
* `federation_list_endpoint`
* `federation_resolve_endpoint`
* `federation_trust_mark_status_endpoint`
* `organization_name`
* `contacts`
* `logo_uri`
* `policy_uri`
* `homepage_uri`
# Metadata Policy
The entity configuration of the leaf entity in a trust chain includes metadata of the leaf entity. The `metadata` claim is used to hold the metadata.
On the other hand, other JWTs in the trust chain issued by authorities may include **metadata policies** that are used to adjust the metadata of the leaf entity. The `metadata_policy` claim is used to hold the metadata policy.
The metadata policies are combined before use.
Then, the combined metadata policy is applied to the metadata of the leaf entity. Metadata used by a federation is the metadata after the adjustment.
The following diagram depicts the flow of metadata policy application.
See the OpenID Federation specification for details about metadata policy.
# Client Registration
In a typical implementation of identity provider, identifiers of relying parties (clients) are under the management of the identity provider. The identifiers are unique only in the realm of the identity provider. On the other hand, in the OpenID Federation world, every federation entity has a globally unique identifier.
An interesting characteristic of OpenID Federation is that the globally unique identifiers, i.e., **entity IDs, can be used as a client ID in OAuth/OIDC requests**. For example, an authorization request like below can be made. The point to pay attention to in the example is that the value of the `client_id` request parameter is the entity ID of the relying party.
```
https://idp.example.com/authorize?request_uri=...&client_id=https://rp.example.com/123
```
## Automatic Client Registration
When the client ID in an OAuth/OIDC request is unknown but looks like an entity ID, an identity provider that supports OpenID Federation tries to find a trust chain that starts from the entity ID and ends with one of trust anchors that the identity provider trusts.
If such a trust chain is found, the identity provider automatically registers a new client with the entity ID and metadata and continues to process the OAuth/OIDC request.
The discovery document ([OpenID Connect Discovery 1.0][OIDC_DISCOVERY]) of an identity provider that supports **automatic client registration** includes `automatic` in the `client_registration_types_supported` server metadata. Note that the OpenID Federation specification defines the `client_registration_types_supported` server metadata as mandatory.
Also, the `client_registration_types` client metadata of the client being automatically registered must include `automatic`. Note that the OpenID Federation specification defines the `client_registration_types` client metadata as mandatory.
## Request Authentication
When an OAuth/OIDC request triggers automatic client registration, the request needs to be authenticated in some way or other. The OpenID Federation specification requires the requester to prove that it has a private key that is paired with a public key in the request. The specification lists the following as available **request authentication methods**.
1. `request_object` - Request object signed with an asymmetric algorithm.
2. `private_key_jwt` - Client authentication using client assertion.
3. `tls_client_auth` - Client authentication using X.509 certificate.
4. `self_signed_tls_client_auth` - Client authentication using X.509 certificate.
References:
Request Object — [Implementer's note about JAR (JWT-Secured Authorization Request)](https://medium.com/@darutk/implementers-note-about-jar-fff4cbd158fe)
Client Authentication — [OAuth 2.0 Client Authentication](https://medium.com/@darutk/oauth-2-0-client-authentication-4b5f929305d4)
X.509 Certificate — [Illustrated X.509 Certificate](https://medium.com/@darutk/illustrated-x-509-certificate-84aece2c5c2e)
Automatic client registration may happen at various endpoints, but available request authentication methods vary depending on characteristics of respective endpoints.
For example, request authentication methods based on client authentication are not usable at the authorization endpoint because client authentication is not performed at the authorization endpoint. Likewise, the request authentication method using a request object is not usable at the token endpoint because the token endpoint recognizes neither the `request` request parameter nor the `request_uri` request parameter.
The following table shows availability of request authentication methods per endpoint.
Endpoint
Request Authentication Methods
`request_object`
`private_key_jwt`
`tls_client_auth`
`self_signed_tls_client_auth`
Authorization
✓
N/A
N/A
N/A
Token
N/A
✓
✓
✓
PAR
✓
✓
✓
✓
Backchannel Authentication
✓
✓
✓
✓
Device Authorization
N/A
✓
✓
✓
References:
Authorization Endpoint — [The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html)
Token Endpoint — [The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html)
PAR Endpoint — [RFC 9126 OAuth 2.0 Pushed Authorization Requests](https://www.rfc-editor.org/rfc/rfc9126.html)
Backchannel Authentication Endpoint — [OpenID Connect Client-Initiated Backchannel Authentication Flow - Core 1.0](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html)
Device Authorization Endpoint — [RFC 8628 OAuth 2.0 Device Authorization Grant](https://www.rfc-editor.org/rfc/rfc8628.html)
The discovery document of an identity provider that supports automatic client registration is supposed to include the `request_authentication_methods_supported` server metadata that indicates which request authentication methods are supported at which endpoints.
The value of the server metadata is a JSON object. Property names that appear in the JSON object are endpoint names such as `authorization_endpoint`. A property value is a JSON array listing request authentication methods available at the corresponding endpoint.
`request_authentication_methods_supported` and `request_authentication_signing_alg_values_supported` were dropped when OpenID Federation 1.0 was finalized. Authlete 3.0.32 and later no longer publish them in the discovery document. Request authentication itself is still required.
## Explicit Client Registration
In addition to automatic client registration, the OpenID Federation specification defines a way to register a client explicitly. The mechanism is called "**explicit client registration**". It is similar to **dynamic client registration** defined in [OpenID Connect Dynamic Client Registration 1.0][OIDC_REGISTRATION] and [RFC 7591 OAuth 2.0 Dynamic Client Registration Protocol][RFC_7591].
An identity provider that supports explicit client registration has the **federation registration endpoint** that accepts explicit client registration requests.
Input data to the federation registration endpoint is either of the following.
1. **Entity configuration**. Its Content-Type is `application/entity-statement+jwt`.
2. **Trust chain**, a JSON array listing entity statements. Its Content-Type is `application/trust-chain+json`.
When client registration succeeds, the federation registration endpoint returns an entity statement as a response. A distinctive point of the response is the inclusion of the `trust_anchor` claim. The value of the claim denotes the entity ID of the trust anchor that has been selected for the client registration.
Authlete versions earlier than 3.0.32 emit this claim under its pre-final name, `trust_anchor_id`.
The value of the `client_id` client metadata in the response is the client ID assigned to the relying party by the identity provider. The value may be different from the entity ID.
The discovery document of an identity provider that supports explicit client registration includes `explicit` in the `client_registration_types_supported` server metadata and advertises the URL of the federation registration endpoint by the `federation_registration_endpoint` server metadata.
Also, the `client_registration_types` client metadata of the client being explicitly registered must include `explicit`.
## Automatic Client Update
All entity statements in a trust chain include the `exp` claim (Expiration Time; [RFC 7519 Section 4.1.4][RFC_7519_414]) as the OpenID Federation specification requires so. The minimum value among the `exp` claims is regarded as the expiration time of the trust chain.
The OpenID Federation specification expects that identity providers treat the expiration time of the trust chain as the expiration time of the client which was registered based on the trust chain.
When an identity provider detects that the trust chain of a client has expired, it tries to refresh the trust chain and update metadata of the client accordingly.
Authlete performs the automatic update only for clients whose `client_registration_types` client metadata includes `automatic`.
## The `trust_chain` Request Parameter
An OAuth/OIDC request may contain the `trust_chain` request parameter. The identity provider may use the trust chain specified by the request parameter to update metadata of the client or register the client if it has not been registered yet.
**Authlete does not reference the `trust_chain` request parameter for registration.**
Authlete references the request parameter only for update.
# Authlete Implementation
## Version
OpenID Federation 1.0 is supported in Authlete 2.3 and later versions.
As the specification is actively evolving, new features may be introduced, and breaking changes may occur in the future. Authlete will continue to align with these updates as they are released.
## Feature Availability
To enable the feature of OpenID Federation 1.0, the following conditions must be satisfied. Otherwise, the Authlete Server would behave as if it didn't know the specification at all.
1. Authlete Server must be deployed with `feature.oidc_federation.enabled=true`.
2. The `federationEnabled` flag of `Service` must be `true`. (cf. [Service.setFederationEnabled(boolean)][Service_setFederationEnabled])
In addition, to make your identity provider function, the following settings must be configured properly.
1. At least one authority (intermediate authority or trust anchor) must be registered. Otherwise, the service cannot generate its entity configuration. (cf. [Service.setAuthorityHints(URI\[\])][Service_setAuthorityHints])
2. At least one pair of trust anchor and its JWK Set must be registered. Otherwise, the service cannot trust any trust chain. (cf. [Service.setTrustAnchors(TrustAnchor\[\])][Service_setTrustAnchors])
3. At least one private key dedicated to OpenID Federation must be registered. Otherwise, the service cannot sign entity statements. (cf. [Service.setFederationJwks(String)][Service_setFederationJwks])
4. At least either of automatic client registration or explicit client registration must be supported. Otherwise, the service cannot register federation clients. (cf. [Service.setSupportedClientRegistrationTypes(ClientRegistrationType\[\])][Service_setSupportedClientRegistrationTypes])
The relying party's metadata must also fit within what the service supports. If a relying party declares a value the service does not support — for example a `token_endpoint_auth_method` that is missing from the service's supported client authentication methods — client registration fails with `A320308`, naming the metadata in question (`A320309` when the offending value is an element of an array metadata).
### Configuring Federation
To make OpenID Federation 1.0 work, configure the following four places in the Authlete Management Console. For each of them, log in to the [Authlete Management Console](https://console.authlete.com), click the organization name, and select the target service first.
| Where | What to configure |
| - | - |
| **Service Settings > Endpoints > Federation > Client Registration** | **Federation Support**, **Client Registration Types**, **Registration Endpoint** |
| **Service Settings > Key Management > JWK Set > Federation** | **JWK Set**, **Signed JWK Set URI**, **Signature Key ID** |
| **Service Settings > Endpoints > Federation > Entity Configuration** | **Organization Name**, **Authorities**, **Entity Configuration Duration** |
| **Service Settings > Endpoints > Federation > Trust Anchors** | **Entity Identifier**, **JWKS** |
#### Client Registration
Enable the federation feature itself and choose which client registration types the service accepts.
1. Navigate to **Service Settings > Endpoints > Federation** and open the **Client Registration** tab.
2. In the **Federation Support** section, turn on **Enable** to activate OpenID Federation 1.0 support.
3. Under **Client Registration Types**, select one or both registration methods:
* **Automatic**: Enables automatic client registration.
* **Explicit**: Enables explicit client registration.
4. In the **Registration Endpoint** section, provide the URI of the federation registration endpoint (e.g., `https://idp.example.com/fedreg`). This field is mandatory when **Explicit** is selected.
5. Click **Save Changes** to apply the configuration.
#### Federation JWK Set
Register the key used to sign entity statements. Without it, the entity configuration endpoint cannot generate an entity statement.
1. Navigate to **Service Settings > Key Management > JWK Set** and open the **Federation** tab.
2. In the **JWK Set** section, enter the JWK Set containing the private key used for signing.
3. If you publish a signed JWK Set, enter the URI of that endpoint in the **Signed JWK Set URI** section.
4. In the **Signature Key ID** section, select the `kid` of the key used to sign the entity configuration.
5. Click **Save Changes** to apply the configuration.
#### Entity Configuration
Configure the information carried in the self-signed entity statement. If **Authorities** is empty, the entity configuration endpoint cannot generate a valid entity statement.
1. Navigate to **Service Settings > Endpoints > Federation** and open the **Entity Configuration** tab.
2. In the **Organization Name** section, enter the name of the organization that operates the service. The value is published as `organization_name`.
3. In the **Authorities** section, click **Add** and enter the entity ID of a superior authority (an intermediate authority or a trust anchor). The value corresponds to `authority_hints`.
4. In the **Entity Configuration Duration** section, enter the duration of the entity configuration in seconds. When `0` is specified, the default value determined by your Authlete server is used.
5. Click **Save Changes** to apply the configuration.
#### Trust Anchors
Register the trust anchors used as the root of trust when the service resolves trust chains of relying parties.
1. Navigate to **Service Settings > Endpoints > Federation** and open the **Trust Anchors** tab.
2. Click **Add**.
3. Enter the entity ID of the trust anchor in **Entity Identifier** and its JWK Set in **JWKS**, then click **Confirm**.
4. Click **Save Changes** to apply the configuration.
## Authlete API
Authlete Server provides the following APIs for OpenID Federation.
### The `/api/federation/configuration` API
This API is used to implement the entity configuration endpoint (`/.well-known/openid-federation`).
**Request**
HTTP Method
GET
**Response**
HTTP Status Code
200
Content-Type
`application/json`
Parameters
`action`
The next action that the implementation of the entity configuration endpoint
(`/.well-known/openid-federation`) should take after getting a
response from the Authlete API.
In a successful case, the value of the `action` response parameter
is `"OK"`. It means that the entity configuration endpoint should
return a response with `200 OK`,
`application/entity-statement+jwt`, and the entity configuration
prepared by the Authlete API.
`responseContent`
The content that the implementation of the entity configuration endpoint
(`/.well-known/openid-federation`) should use when it constructs
a response.
In a successful case, the value of the `responseContent` response
parameter is the entity configuration of the identity provider in JWT format.
The [authlete-java-common][authlete-java-common] library includes [FederationConfigurationRequest][FederationConfigurationRequest] class and [FederationConfigurationResponse][FederationConfigurationResponse] class that can be mapped to the request and the response of the Authlete API. The FederationConfigurationRequest class is empty as of this writing, though. The class exists just for future extensibility.
The [authlete-java-jaxrs][authlete-java-jaxrs] library contains utility classes for implementations of the entity configuration endpoint ([FederationConfigurationRequestHandler][FederationConfigurationRequestHandler] and [BaseFederationConfigurationEndpoint][BaseFederationConfigurationEndpoint]). With the utility classes, the entity configuration endpoint can be implemented like below (excerpt from [FederationConfigurationEndpoint][FederationConfigurationEndpoint] in [java-oauth-server][java-oauth-server]).
```java theme={null}
@Path("/.well-known/openid-federation")
public class FederationConfigurationEndpoint extends BaseFederationConfigurationEndpoint
{
/**
* Entity configuration endpoint.
*/
@GET
public Response get()
{
// Handle the request to the endpoint.
return handle(AuthleteApiFactory.getDefaultApi());
}
}
```
### The `/api/federation/registration` API
This API is used to implement the federation registration endpoint.
The entity configuration in a federation registration request.
`trustChain`
The trust chain in a federation registration request.
**Response**
HTTP Status Code
200
Content-Type
`application/json`
Parameters
`action`
The next action that the implementation of the federation registration endpoint
should take after getting a response from the Authlete API.
In a successful case, the value of the `action` response parameter
is `"OK"`. It means that the federation registration endpoint should
return a response with `200 OK`,
`application/entity-statement+jwt`, and the entity statement
prepared by the Authlete API.
`responseContent`
The content that the implementation of the federation registration endpoint
should use when it constructs a response.
In a successful case, the value of the `responseContent` response
parameter is the entity statement of the newly registered client in JWT format.
`client`
Information about the newly registered client in JSON object format.
(cf. [Client](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Client.html))
The [authlete-java-common][authlete-java-common] library includes [FederationRegistrationRequest][FederationRegistrationRequest] class and [FederationRegistrationResponse][FederationRegistrationResponse] class that can be mapped to the request and the response of the Authlete API.
The [authlete-java-jaxrs][authlete-java-jaxrs] library contains utility classes for implementations of the federation registration endpoint ([FederationRegistrationRequestHandler][FederationRegistrationRequestHandler] and [BaseFederationRegistrationEndpoint][BaseFederationRegistrationEndpoint]). With the utility classes, the federation registration endpoint can be implemented like below (excerpt from [FederationRegistrationEndpoint][FederationRegistrationEndpoint] in [java-oauth-server][java-oauth-server]).
```java theme={null}
@Path("/api/federation/register")
public class FederationRegistrationEndpoint extends BaseFederationRegistrationEndpoint
{
@POST
@Consumes("application/entity-statement+jwt")
public Response entityConfiguration(String jwt)
{
// Client registration by a relying party's entity configuration.
return handle(
AuthleteApiFactory.getDefaultApi(),
request().setEntityConfiguration(jwt));
}
@POST
@Consumes("application/trust-chain+json")
public Response trustChain(String json)
{
// Client registration by a relying party's trust chain.
return handle(
AuthleteApiFactory.getDefaultApi(),
request().setTrustChain(json));
}
private static FederationRegistrationRequest request()
{
return new FederationRegistrationRequest();
}
}
```
## Client Registration and Update
This section explains Authlete's behavior on client registration and update in detail.
### Automatic Client Registration
1. When Authlete processes an OAuth/OIDC request, Authlete tries to identify a client at a relatively early phase. First, Authlete interprets the specified client identifier as an original numeric ID and searches the database for a client having the numeric ID.
2. If a client having the numeric ID was not found, but if the "[Client ID Alias][CLIENT_ID_ALIAS]" feature of the service is enabled, Authlete interprets the specified client identifier as an alias and searches the database for a client having the alias.
3. If a client having the alias was not found, but if the "OpenID Federation" feature of the service is enabled, Authlete interprets the specified client identifier as an entity ID and searches the database for a client having the entity ID.
4. If a client having the entity ID was not found, but if the service supports automatic client registration, Authlete tries to resolve a trust chain of the client.
5. If the trust chain resolution succeeds, Authlete applies the combined metadata policy of the trust chain to the metadata of the client.
6. Authlete checks whether the client metadata contains the `client_registration_types` client metadata and it includes `automatic`.
7. Authlete validates the client metadata to ensure that the client can be registered to the service.
8. Authlete performs request authentication for the OAuth/OIDC request.
9. Finally, Authlete registers the client and continues to process the OAuth/OIDC request.
**Authlete does not reference the `trust_chain` request parameter for registration.**
Authlete references the request parameter only for update. When the request parameter is included
in an OAuth/OIDC request that triggers automatic registration, the trust chain specified by the
request parameter is referenced for update right after registration.
The reason for the limitation is that it is difficult, if not impossible, to write a coherent and
orderly implementation that performs **request authentication** which depends on client metadata
in the trust chain specified by the `trust_chain` request parameter.
Also, when the request parameter is in a **request object**, implementation complexity jumps up.
In the case, the key that should be used to verify the signature of the request object is embedded
in the trust chain inside the request object. In that sense, it can be said that the request object
is self-signed. In general, in a procedure that utilizes a self-signed object, the content of the
self-signed object is referenced before the signature of the object is verified. Such procedures
exist in the world, so the self-signed request object will maybe work. However, processes that
must be done before signature verification, including the long and complex steps of trust chain
resolution and verification, are too complex and dependent on unverified data too much.
### Trust Chain Update
**Case 1: when the `trust_chain` request parameter is given**
1. If the "OpenID Federation" feature of the service is not enabled, Authlete does not try to update trust chains.
2. Authlete checks whether the service supports automatic client registration and whether the client's `client_registration_types` metadata contains `automatic`. If these conditions are not satisfied, an error is returned.
3. Authlete validates the trust chain specified by the `trust_chain` request parameter. Especially, the entity ID of the entity configuration in the trust chain must match the entity ID of the existing client.
4. Authlete applies the combined metadata policy of the trust chain to the client metadata in the trust chain.
5. Authlete checks whether the client metadata contains the `client_registration_types` client metadata and it includes `automatic`.
6. Authlete validates the client metadata to ensure that the client can be updated with the metadata.
7. Authlete updates metadata of the client.
8. Authlete continues to process the OAuth/OIDC request.
When the `trust_chain` request parameter is given, trust chain update is always tried regardless of whether the current trust chain has expired or not. Even when trust chain update fails, the client is NOT deleted.
**Case 2: when the `trust_chain` request parameter is not given**
1. If the "OpenID Federation" feature of the service is not enabled, Authlete does not try to update trust chains.
2. Authlete checks whether the current trust chain of the client has expired. If the trust chain has not expired, Authlete does not try to update the trust chain.
3. If the current trust chain has expired but the service does not support automatic client registration, the client is **deleted** and the OAuth/OIDC request fails.
4. If the current trust chain has expired but `automatic` is not included in the `client_registration_types` client metadata, the client is **deleted** and the OAuth/OIDC request fails.
5. Authlete builds a trust chain from the entity ID of the client from scratch.
6. Authlete applies the combined metadata policy of the trust chain to the client metadata in the trust chain.
7. Authlete checks whether the client metadata contains the `client_registration_types` client metadata and it includes `automatic`.
8. Authlete validates the client metadata to ensure that the client can be updated with the metadata.
9. Authlete updates metadata of the client.
10. Authlete continues to process the OAuth/OIDC request.
When the `trust_chain` request parameter is not given, trust chain update is tried only when the current trust chain has expired. When trust chain update fails, the client is **deleted**.
# Finally
OpenID Federation 1.0 is one of the most complex standard specifications related to OAuth and OpenID Connect and it is difficult to find commercial-quality implementations. Authlete is a rare example.
If you are interested in trying OpenID Federation 1.0, please [contact us][CONTACT_US].
[OIDC_DISCOVERY]: https://openid.net/specs/openid-connect-discovery-1_0.html
[OIDC_REGISTRATION]: https://openid.net/specs/openid-connect-registration-1_0.html
[OPENID_FEDERATION]: https://openid.net/specs/openid-federation-1_0.html
[RFC_7519]: https://www.rfc-editor.org/rfc/rfc7519.html
[RFC_7519_411]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1
[RFC_7519_412]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2
[RFC_7519_414]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4
[RFC_7591]: https://www.rfc-editor.org/rfc/rfc7591.html
[RFC_8414]: https://www.rfc-editor.org/rfc/rfc8414.html
[authlete-java-common]: https://github.com/authlete/authlete-java-common
[FederationConfigurationRequest]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/FederationConfigurationRequest.html
[FederationConfigurationResponse]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/FederationConfigurationResponse.html
[FederationRegistrationRequest]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/FederationRegistrationRequest.html
[FederationRegistrationResponse]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/FederationRegistrationResponse.html
[Service_setAuthorityHints]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#setAuthorityHints-java.net.URI:A-
[Service_setFederationEnabled]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#setFederationEnabled-boolean-
[Service_setFederationJwks]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#setFederationJwks-java.lang.String-
[Service_setSupportedClientRegistrationTypes]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#setSupportedClientRegistrationTypes-com.authlete.common.types.ClientRegistrationType:A-
[Service_setTrustAnchors]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#setTrustAnchors-com.authlete.common.dto.TrustAnchor:A-
[authlete-java-jaxrs]: https://github.com/authlete/authlete-java-jaxrs
[BaseFederationConfigurationEndpoint]: https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/BaseFederationConfigurationEndpoint.java
[BaseFederationRegistrationEndpoint]: https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/BaseFederationRegistrationEndpoint.java
[FederationConfigurationRequestHandler]: https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/FederationConfigurationRequestHandler.java
[FederationRegistrationRequestHandler]: https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/FederationRegistrationRequestHandler.java
[java-oauth-server]: https://github.com/authlete/java-oauth-server
[FederationConfigurationEndpoint]: https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/FederationConfigurationEndpoint.java
[FederationRegistrationEndpoint]: https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/FederationRegistrationEndpoint.java
[CLIENT_ID_ALIAS]: /kb/oauth-and-openid-connect/client-id-alias
[CONTACT_US]: https://www.authlete.com/contact/
[PKI]: https://en.wikipedia.org/wiki/Public_key_infrastructure
# Client Initiated Backchannel Authentication (CIBA)
Source: https://developers.authlete.com/protocols-and-flows/advanced-flows/client-initiated-backchannel-authentication-ciba
Technical information about Client Initiated Backchannel Authentication.
# What is CIBA?
Client Initiated Backchannel Authentication (CIBA) is one of the latest standards from OpenID Foundation. It is to define new authentication and authorization flows, which are categorized as “decoupled flow”, compared to the traditional OAuth / OpenID Connect “redirect flow.” It enables new ways of obtaining an end user’s consent that can significantly improve customer experience.
# How CIBA works
CIBA defines two types of devices; *Consumption Device* and *Authentication Device*. The *Consumption Device* initiates the CIBA flow by interacting with its correspoinding OpenID Connect (OIDC) Relying Party (RP) to determine an OIDC Identity Provider (IdP) and identifier of a target user, and make an OIDC authentication request to the server. The server sends a notification to the target user's *Autentication Device*. Once the user authenticates with the *Authentication Device* to the server and optionally authorizes the request, the server make a response including tokens such as OAuth Access Token / Refresh Token, OIDC ID Token.
Note that these two devices are *decoupled* each other - these devices don't have to be in the same place. In addition, a person who kicks off the flow with the consumption device is not necessarily the same as the target user with the authentication device. This architecture brings more flexibility to user authentication and consent.
# Authlete and CIBA
Authlete is a pioneer of supporting CIBA. We have implemented [CIBA Core 1.0](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) since February 2019, and been granted certifications for [Financial-grade API Client Initiated Backchannel Authentication Profile (FAPI-CIBA) conformance profiles](https://openid.net/certification/#FAPI-CIBA_OPs).
We are also active to develop the [CIBA Core](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) and related specifications (e.g. [Financial-grade API: Client Initiated Backchannel Authentication Profile](https://openid.net/specs/openid-financial-api-ciba-ID1.html)) in OpenID Foundation's [Financial-grade API (FAPI) Working Group](https://openid.net/wg/fapi/).
# How to implement CIBA with Authlete
Please check [this document](/guides/flows-and-protocols/grant-types-and-token-flows/how-to-implement-ciba-with-authlete).
# Resources
1. [“CIBA”, a new authentication/authorization technology in 2019, explained by an implementer](https://medium.com/@darutk/ciba-a-new-authentication-authorization-technology-in-2019-explained-by-an-implementer-d1e0ac1311b4)
2. [Authlete API Reference](https://docs.authlete.com/)
# Grant Management for OAuth 2.0
Source: https://developers.authlete.com/protocols-and-flows/advanced-flows/grant-management-for-oauth-2-0
Explanation about the "Grant Management for OAuth 2.0" specification and Authlete's implementation.
# Introduction
**[Grant Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html)** is a technical specification that enables to manage privileges granted to client applications in a finer-grained way than pre-existing standard specifications.
A reason to pay attention to the specification is that it is positioned as a part of **Financial-grade API 2.0**, the successor of **Financial-grade API 1.0** which is now the de facto standard for high API security and has been adopted by ecosystems such as **[UK Open Banking](https://www.openbanking.org.uk/)**, **[Australian Consumer Data Right](https://www.cdr.gov.au/)** and **[Open Banking Brasil](https://openbankingbrasil.org.br/)**. Grant Management is mentioned also in the [whitepaper](https://gainforum.org/GAINWhitePaper.pdf "GAIN DIGITAL TRUST") of **GAIN** (**Global Assured Identity Network**), which is a project to build a global high-trust digital identity network over the Internet.
This article explains the specification based on its first Implementer's Draft which was released in September, 2021 ([announcement](https://openid.net/2021/09/21/implementers-draft-of-fapi-grant-management-approved/ "Implementer's Draft of FAPI Grant Management Approved")). However, as with others, the first draft is not perfect. Some technical definitions that implementers will need sooner or later are missing and there is room for inviting different interpretations. But, considering that the purpose of the first draft is to urge implementers to start working, it is rather natural.
In addition to explanation about the specification itself, this article also describes how Authlete interprets and implements the specification, hoping to be feedback from an implementer in order to brush up the specification.
# Specification
## Concept of Grant
In OAuth 2.0, a user grants some privileges to a client application. As a result, an access token is issued from an authorization server to the client application.
The process of access token issuance may be repeated. Privileges requested by the client application through the second and subsequent processes may be different from the ones requested through the first process. In this way, privileges granted to the client application accumulate.
The Grant Management specification calls the set of accumulated privileges "**grant**" and defines means whereby to query about, merge and revoke a grant.
## Request and Response Parameters
A **grant ID** is assigned to each grant in order to make grants manageable. A grant ID is issued together with an access token from the token endpoint. To request an authorization server to issue a grant ID, a client application includes a new authorization request parameter `grant_management_action` in an authorization request with a value `create`. The issued grant ID is included in the token response as the value of a new token response parameter `grant_id`.
To accumulate privileges, the client application includes `grant_management_action=merge` in the next authorization request with a new authorization request parameter `grant_id` whose value is a previously-issued grant ID.
The grant management action `update` was renamed to `merge` by [FAPI PR 344](https://bitbucket.org/openid/fapi/pull-requests/344). The change takes effect from the second Implementer's Draft of Grant Management for OAuth 2.0.
In addition to `create` and `merge`, `replace` is defined as a value for the `grant_management_action` request parameter. The `replace` action replaces accumulated privileges tied to the specified grant ID with new ones that are granted only through the request including `grant_management_action=replace`. The previous privileges are revoked.
Discussion related to `grant_management_action=replace` can be found in *"[FAPI ISSUE 454: Expected behavior for grant\_management\_action=replace](https://bitbucket.org/openid/fapi/issues/454)"*.
When the value of the `grant_management_action` request parameter is `merge` or `replace`, the `grant_id` request parameter is mandatory.
**Implementer's Note**
The first Implementer's Draft does not mention the following cases: (1) `grant_id` is given but `grant_management_action` is not, and (2) `grant_id` is given but the value of `grant_management_action` is `create`. I created *"[FAPI ISSUE 447: grant\_id is given but grant\_management\_action is not](https://bitbucket.org/openid/fapi/issues/447)"* to discuss this issue. Authlete's current implementation rejects requests in the cases.
**Update**
[FAPI PR 290](https://bitbucket.org/openid/fapi/pull-requests/290) was created to explicitly require that authorization server implementations reject requests in the cases described above.
Regarding the `grant_management_action` and `grant_id` request parameters, the specification states as follows.
These parameters can be used with any request serving as authorization request, e.g. it may be used with CIBA requests.
To be concrete, the specification expects that the backchannel authentication endpoint ([CIBA Core](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) / [Section 7](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#rfc.section.7)) recognizes the request parameters.
**Implementer's Note**
Although it is not explicitly mentioned in the specification, it may be okay to regard a device authorization request ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628.html) / [Section 3.1](https://www.rfc-editor.org/rfc/rfc8628.html#section-3.1)) as a *"request serving as authorization request"*. Authlete's current implementation recognizes the request parameters related to Grant Management at the backchannel authentication endpoint ([CIBA](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html)) and the device authorization endpoint ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628.html)) in addition to the authorization endpoint ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html)).
## Grant Management Endpoint
The Grant Management specification defines a new endpoint called "**grant management endpoint**". The endpoint enables to query about and revoke a grant. To access the endpoint, a pre-issued access token is necessary.
### Query
| **QUERY** | | |
| :- | :- | :- |
| Request | HTTP Method | `GET` |
| | URL | `{endpoint}/{grant-id}` |
| | Protection | Acccess token having the `grant_management_query` scope |
| Response | Status Code | 200 OK |
| | Content-Type | `application/json` |
| | Format | See "[6.4. Query Status of a Grant](https://openid.net/specs/fapi-grant-management.html#name-query-status-of-a-grant)". |
The following is an example response excerpted from "[6.4. Query Status of a Grant](https://openid.net/specs/fapi-grant-management.html#name-query-status-of-a-grant)" of the Grant Management specification.
The JSON in the response body shows accumulated privileges of a grant. It has `"scopes"`, `"claims"` and `"authorization_details"` as top-level properties.
#### scopes
The value of `"scopes"` is an array of combinations of scopes and resources (hereinafter referred to as "scope-resource clusters"). Each cluster consists of `"scope"` and `"resource"`. The value of `"scope"` is a space-delimited scopes. The value of `"resource"` is an array of resources. Values of the scopes and resources are ones specified by the `scope` request parameter ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) / [Section 3.3](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.3)) and `resource` request parameters ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) / [Section 2](https://www.rfc-editor.org/rfc/rfc8707.html#name-resource-parameter)).
The structure of `"scopes"` makes implementers realize that scope-resource clusters should be managed separately even if privileges are accumulated through repeated `merge` operations. If contents of clusters were mixed through `merge` operations, scopes might be coupled with unintended resources. The right-bottom JSON in the figure below indicates that the `change` scope is combined with the `https://payment.example.com` resource if contents of clusters are mixed, which can cause a serious security incident such as theft of money.
#### claims
The value of `"claims"` is an array of "claims" that the user has consented for the client application to know. Put simply, "claims" here are user attributes. Some user attributes such as `family_name` and `phone_number` are defined as standard claims in [Section 5.1](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html).
A client application can request claims by including special scopes (`profile`, `email`, `address` and `phone`) in the `scope` request parameter ([OIDC Core](https://openid.net/specs/openid-connect-core-1_0.html) / [Section 5.4](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)) and/or by using the `claims` request parameter ([OIDC Core](https://openid.net/specs/openid-connect-core-1_0.html) / [Section 5.5](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter)).
#### authorization\_details
The value of `"authorization_details"` is an array of pieces of detailed information about authorization. The pieces of information are ones specified by the `authorization_details` request parameter which is defined in "**[OAuth 2.0 Rich Authorization Requests](https://datatracker.ietf.org/doc/draft-ietf-oauth-rar/)**" (**RAR**).
Before RAR was developed, authorization server implementations had to introduce a nonstandard request parameter or use the `scope` request parameter in a nonstandard way in order to attach detailed information to an authorization request. The latter approach is called "parameterized scope" or "dynamic scope" which embeds a dynamic value in a scope string. For example, [Open Banking Brasil Financial-grade API Security Profile 1.0](https://openbanking-brasil.github.io/specs-seguranca/open-banking-brasil-financial-api-1_ID3.html) defines "Dynamic Consent Scope" ([Section 7.1.2](https://openbanking-brasil.github.io/specs-seguranca/open-banking-brasil-financial-api-1_ID3.html#name-dynamic-consent-scope-defin)) whose format is `consent:{ConsentID}` where `consent:` is a fixed-string prefix and `{ConsentID}` is the dynamic part (e.g. `consent:urn:bancoex:C1DD33123`).
The problem of the dynamic scope approach is that it is not standardized and interoperability among implementations is hopeless due to its intrinsically variant formats. RAR was developed to solve the problem and is positioned as a part of Financial-grade API 2.0.
### Revoke
| **REVOKE** | | |
| :- | :- | :- |
| Request | HTTP Method | `DELETE` |
| | URL | `{endpoint}/{grant-id}` |
| | Protection | Access token having the `grant_management_revoke` scope |
| Response | Status Code | 204 No Content |
The Grant Management specification states that all refresh tokens associated with the revoked grant must be revoked and all access tokens associated with the revoked grant should be revoked.
**Implementer's Note**
Implementations of access token and refresh token differ among authorization server implementations. Especially, it should be noted that **self-contained access tokens, typically JWT access tokens, cannot be revoked** unless the authorization server operates something equivalent to PKI's [CRL](https://en.wikipedia.org/wiki/Certificate_revocation_list) (Certificate Revocation List) or [OCSP](https://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol) (Online Certificate Status Protocol). See *"[OAuth Access Token Implementation](https://darutk.medium.com/oauth-access-token-implementation-30c2e8b90ff0)"* for details.
Authlete's current implementation revokes all refresh tokens and access tokens associated with the revoked grant.
## Server Metadata
The Grant Management specification adds the following server metadata.
| Metadata | Description |
| :- | :- |
| `grant_management_actions_supported` | Grant management actions supported by the authorization server. Possible values are `create`, `query` `replace`, `revoke` and `merge`. |
| `grant_management_endpoint` | The URL of the grant management endpoint |
| `grant_management_action_required` | Boolean flag indicating whether the `grant_management_action` request parameter is always required. |
Because *"grant management is restricted to confidential only clients due to security reasons"* ([Section 5.1](https://openid.net/specs/fapi-grant-management.html#name-requirements-for-authorizat)), if the discovery document ([OIDC Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html)) includes `"grant_management_action_required":true`, it implies that the authorization endpoint of the authorization server rejects any request from public clients.
# Implementation
## Decisions to Make
Implementations of the Grant Management specification will considerably differ from each other because implementers will face many choices that have no absolute answer. The following are examples of such choices.
1. Whether lifecycle of a grant is managed independently of elements of the grant or it completely depends on them.
2. Whether the content of a grant may change without explicit grant management actions (authorization requests with `grant_management_action` and revocation requests to the grant management endpoint) or it never changes without explicit actions.
3. Whether privileges of access tokens and refresh tokens may change when the content of their associated grant changes or they never change after issuance.
4. How refresh tokens are linked to a grant. Linkage is necessary because refresh tokens must be revoked when their associated grant is revoked.
5. Whether access tokens are revoked or not when an associated grant is revoked. The specification says that access tokens should be revoked. Note that some authorization server implementations cannot revoke access tokens once they are issued. (cf. *"[OAuth Access Token Implementation](https://darutk.medium.com/oauth-access-token-implementation-30c2e8b90ff0)"*)
6. How privileges inherited through `grant_management_action=merge` are managed. As a snapshot at the timing of access token issuance (which never changes later) or as a link to something else (whose content may change later), or in other forms.
7. How a resource server validates an access token which has inherited privileges through `grant_management_action=merge`. Currently, there is no agreed standard formats of introspection response and JWT access token for Grant Management, and the discussion is a bit controversial. (cf. *"[FAPI ISSUE 455: Impact of grant\_management\_action=update on AT implementation and introspection](https://bitbucket.org/openid/fapi/issues/455)"*)
## Actual Example of Implementation
Grant Management is supported from Authlete 2.3. As feedback to the community, I describe the design adopted in Authlete's implementation.
### Prior Knowledge
The following are prior knowledge about Authlete's access token implementation necessary to read the subsequent sections.
1. The implementation type of access tokens is "handle", not "self-contained" (typically JWT). By setting "Access Token Signature Algorithm", you can make Authlete issue JWT access tokens (cf. [Enabling JWT-based access tokens](/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens)). However, for privacy and security reasons, Authlete's JWT access tokens intentionally do not contain all data associated with them, and some portions of the data are stored only in the database of Authlete without being embedded in access tokens themselves. Read *"[Leakage of Personal Information via JWT Access Token](https://darutk.medium.com/leakage-of-personal-information-via-jwt-access-token-c222a3293898)"* and *"[OAuth Access Token Implementation](https://darutk.medium.com/oauth-access-token-implementation-30c2e8b90ff0)"* for details.
2. When a refresh token flow ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) / [Section 6](https://www.rfc-editor.org/rfc/rfc6749.html#section-6)) succeeds, the previous access token tied to the used refresh token is revoked even if it has not expired yet.
3. It is configurable whether a new refresh token is issued when a refresh token flow succeeds or new one is not issued and the used refresh token remains valid (cf. [Refresh tokens after being used](/configuration-reference/tokens-and-claims/refresh-tokens-after-being-used)). When a new refresh token is issued, the used refresh token is revoked even if it has not expired yet.
4. The relationship between refresh token and access token is one-to-one. That is, the number of valid access tokens per refresh token is at most one, and vice versa.
5. When a refresh token is deleted, the access token coupled with the refresh token is deleted. Likewise, when an access token is deleted, the refresh token coupled with the access token is deleted. Some people may get surprised at the latter behavior, but [Section 2.1](https://www.rfc-editor.org/rfc/rfc7009.html#section-2.1) of [RFC 7009 OAuth 2.0 Token Introspection](https://www.rfc-editor.org/rfc/rfc7009.html) states explicitly as follows: *"If the token passed to the request is an access token, the server MAY revoke the respective refresh token as well."*
6. The behaviors of refresh tokens described above can be explained by the design decision made in the very early phase of Authlete: **a pair of access token and refresh token is managed in one database record in the access token table.**
7. An access token record is regarded as live (not expired) when either or both of access token and refresh token in the record have not expired.
### Grant
**Authlete defines a grant as a collection of "live" access token records.** As a result, the content of a grant changes when access token records of the grant get expired. The change can happen without explicit grant management actions (authorization requests with `grant_management_action` and revocation requests to the grant management endpoint) because expiration of access token records happens independently of grant management actions.
Authorization server implementations may choose to create a new database table to manage grant IDs, but Authlete chose a different approach and simply added a `grant_id` column to the existing access token table. The figure below illustrates that a grant consists of live access token records.
### Grant ID Issuance
In Authlete, a token response contains a grant ID only when the preceding authorization request (or backchannel authentication request or device authorization request) contains the `grant_management_action` request parameter although [Section 5.4 Token Response](https://openid.net/specs/fapi-grant-management.html#name-token-response) of the first Implementer's Draft says *"The AS will return a `grant_id` if it supports any of the grant management actions"*.
The description in the specification results in that it requires the authorization server always issue a grant ID unconditionally once it supports Grant Management. However, it is meaningless to issue a grant ID to public clients which are not allowed to use grant management ([Section 5.1](https://openid.net/specs/fapi-grant-management.html#name-requirements-for-authorizat)). I'm suggesting changing the description in *"[FAPI ISSUE 455: Condition for a token response to include a grant\_id](https://bitbucket.org/openid/fapi/issues/445)"*
Authlete does not issue a grant ID from the authorization endpoint even when an authorization request includes the `grant_management_action` request parameter and an access token is issued from the authorization endpoint because it was confirmed that the authors of the specification has intentionally excluded the case from the specification. I'm suggesting mentioning it explicitly in the specification in *"[FAPI ISSUE 453: Grant ID from Authorization Endpoint](https://bitbucket.org/openid/fapi/issues/453)"*.
On the other hand, Authlete includes a grant ID in a CIBA push notification when a backchannel authentication request includes the `grant_management_action` request parameter and the client is configured to use the CIBA PUSH mode. However, I've not suggested registering the `grant_id` parameter for CIBA push notifications yet. In any case, because the [FAPI-CIBA Profile](https://openid.net/specs/openid-financial-api-ciba.html) prohibits use of the CIBA PUSH mode, the [FAPI WG](https://openid.net/wg/fapi/) won't show any interest in this.
### Inherited Privileges
When an authorization request includes `grant_management_action=merge`, the newly issued access token inherits privileges of the grant identified by the `grant_id` request parameter. Authlete achieves this inheritance by copying privileges of the existing access token records to the `grant` column of the newly inserted access token record.
The content of the `grant` column is a snapshot of privileges that existed at the timing of access token issuance. The snapshot does not change even after the access token records that provided the privileges have expired or been deleted.
**Authlete Internals**
The actual column name is different. The snapshot consists of multiple columns and is encrypted because contents of inherited `authorization_details` arrays may contain sensitive information.
### Metadata in Discovery Document
Values of the server metadata related to Grant Management are determined as the table below describes.
| Metadata | Value |
| :- | :- |
| `grant_management_actions_supported` | When the grant management endpoint is set, \[`create`, `query`, `replace`, `revoke`, `merge`]. Otherwise, \[`create`, `replace`, `merge`]. |
| `grant_management_endpoint` | Configurable at "Grant Management Endpoint" in the web console. |
| `grant_management_action_required` | Configurable at "Grant Management Action Required" in the web console. |
**Authlete Internals**
If `feature.gm.enabled` in `authlete-server.properties` is not `true`, the server metadata related to Grant Management do not appear in the discovery document.
In actuality, more than that. If the flag is not `true`, request parameters related to Grant Management are not recognized (simply ignored) and the `/api/gm` API (which developers use to implement the grant management endpoint) refuses to respond.
### Different User
A user is authenticated (if not yet) in an authorization process. It can happen that the authenticated user is different from the user of the grant specified by the `grant_id` request parameter. The specification does not describe how the authorization server should behave in the case. I created *"[FAPI 452: When the user being authenticated is different from the user of the grant](https://bitbucket.org/openid/fapi/issues/452)"* to discuss it, but the conclusion seems that it should be left to implementations.
When the authenticated user is different from the user of the grant, Authlete silently skips steps related to Grant Management without raising any error.
**Authlete Internals**
When the value of the `subject` request parameter passed to the `/api/auth/authorization/issue` API is different from the subject of the user of the grant, Authlete regards that the authenticated user is different from the user of the grant. The same logic applies to the `/api/backchannel/authentication/complete` API (for CIBA) and the `/api/device/complete` API (for Device Flow).
Authlete APIs that parse the `grant_id` request parameter (the `/api/auth/authorization` API, the `/api/backchannel/authentication` API and the `/api/device/authorization` API) include information about the grant in their responses. `grantSubject` is one of such response parameters, which represents the identifier of the user of the grant. (cf. `AuthorizationResponse.getGrantSubject()`)
### Consented Claims
By design, Authlete does not get involved in interaction between an authorization server and a user. Authlete is not an authorization server but a set of Web APIs with which developers implement their authorization servers by themselves. Therefore, Authlete does not know how authorization servers developed by developers get consent from a user. When special scopes (`profile`, `email`, `address` and `phone`) that are expanded to sets of claims are included in the `scope` request parameter, an authorization server may get consent about the expanded claims one by one or may omit the step. Authlete just incorporates provided claim data into an ID token or a UserInfo response without knowing whether the claims have been consented by the user or not.
However, to make the `claims` property in responses from the grant management endpoint meaningful, Authlete should know the exact set of consented claims. For this purpose, `consentedClaims` request parameter has been added to the following APIs. (cf. `[AuthorizationIssueRequest.setConsentedClaims()](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationIssueRequest.html#setConsentedClaims-java.lang.String:A-)`)
* `/api/auth/authorization/issue`
* `/api/backchannel/authentication/complete`
* `/api/device/complete`
When the request parameter is given and its value is not empty, Authlete regards the value as the set of consented claims. Otherwise, Authlete computes the set from consented special scopes (e.g. `profile`) and provided claim data (by the `claims` request parameter) although Authlete knows the possibility that the computed set may be different from the actual set of consented claims. Especially, the computed set may not include claims that the authorization server returns from the UserInfo Endpoint. Therefore, if the exact set of consented claims needs to be controlled, the `consentedClaims` request parameter should be used.
**Authlete Internals**
The set of consented claims is stored in the `consented_claims` column of the access token table.
### Compression of Grant Management Response
Authlete compresses the content of a grant management response.
#### scopes
Entries in the `scopes` array are grouped by a set of resources. The entries are sorted by the resource set. Scopes in the same resource set are merged into the `scope` property.
For example, when live access token records of a grant have the following privileges,
| scope | resource |
| :- | :- |
| `"X23 L23"` | \[`"r2"`, `"r3"`] |
| `"X2 K2"` | \[`"r2"` ] |
| `"X3 J3"` | \[`"r3"` ] |
| `"X13 I13"` | \[`"r1"`, `"r3"`] |
| `"X12 H12"` | \[`"r1"`, `"r2"`] |
| `"X1 G1"` | \[`"r1"` ] |
| `"X3 F3"` | \[`"r3"` ] |
| `"X23 E23"` | \[`"r2"`, `"r3"`] |
| `"X13 D13"` | \[`"r1"`, `"r3"`] |
| `"X2 C2"` | \[`"r2"` ] |
| `"X1 B1"` | \[`"r1"` ] |
| `"X12 A12"` | \[`"r1"`, `"r2"`] |
the collection will be compressed to the following.
| scope | resource |
| :- | :- |
| `"B1 G1 X1"` | \[`"r1"` ] |
| `"A12 H12 X12"` | \[`"r1"`, `"r2"` ] |
| `"D13 I13 X13"` | \[`"r1"`, `"r3"` ] |
| `"C2 K2 X2"` | \[`"r2"` ] |
| `"E23 L23 X23"` | \[`"r2"`, `"r3"` ] |
| `"F3 J3 X3"` | \[`"r3"` ] |
As a result, the `scopes` array in the grant management response will look like below.
```json theme={null}
{
"scopes": [
{ "scope": "B1 G1 X1", "resource": [ "r1" ] },
{ "scope": "A12 H12 X12", "resource": [ "r1", "r2" ] },
{ "scope": "D13 I13 X13", "resource": [ "r1", "r3" ] },
{ "scope": "C2 K2 X2", "resource": [ "r2" ] },
{ "scope": "E23 L23 X23", "resource": [ "r2", "r3" ] },
{ "scope": "F3 J3 X3", "resource": [ "r3" ] }
]
}
```
#### claims
Consented claims of live access token records of a grant are collected, duplicates are dropped, remaining elements are sorted and then put into the `claims` array.
For example, when live access token records of a grant have the following consented claims,
| consented claims |
| :- |
| \[ `c3`, `c5` ] |
| \[ `c1`, `c3` ] |
| \[ `c2`, `c4`, `c5` ] |
the collection will be compressed to the following.
| consented claims |
| :- |
| \[ `c1`, `c2`, `c3`, `c4`, `c5` ] |
As a result, the `claims` array in the grant management response will look like below.
```json theme={null}
{
"claims": [
"c1", "c2", "c3", "c4", "c5"
]
}
```
#### authorization\_details
Duplicates in the `authorization_details` array are removed. Authlete detects duplicates by comparing hash values of the array elements. Spaces and newlines in JSON do not affect the result of hash computation. The key order in JSON objects do not affect the result, either. The following two `authorization_details` elements are regarded as identical.
```json theme={null}
{
"authorization_details": [
{
"type": "t1",
"actions": [ "a1", "a2" ],
"my_custom_data": {
"key1": "value1",
"key2": "value2"
}
},
{
"my_custom_data": { "key2": "value2", "key1": "value1" },
"actions": [
"a1",
"a2"
],
"type": "t1"
}
]
}
```
### Access Token Introspection
As mentioned previously, in order to prevent scopes from being coupled with unintended resources, authorization server implementations cannot help managing scope-resource clusters separately even if privileges are accumulated through repeated `merge` operations.
An access token inherits such scope-resource clusters through the `merge` operation. If so, it is necessary to extend the formats of introspection response ([RFC 7662](https://www.rfc-editor.org/rfc/rfc7662.html)) and JWT access token ([RFC 9068](https://www.rfc-editor.org/rfc/rfc9068.html)). It is because otherwise protected resource endpoints cannot validate access tokens properly.
To discuss the issue, I created *"[FAPI ISSUE 455: Impact of grant\_management\_action=update on AT implementation and introspection](https://bitbucket.org/openid/fapi/issues/455)"* and wrote an article *"[Complexity of Access Token Privileges Introduced by Grant Management](https://darutk.medium.com/complexity-of-access-token-privileges-introduced-by-grant-management-ec527b5c6d6a)"* that articulates discussion points.
However, it was much harder to reach a consensus than I expected😅. As of this writing, it seems unrealistic for the community to agree on a solution for the issue in a short period.
Fortunately, Authlete provides developers with its own introspection API (`/api/auth/introspection`) which is different from the standard introspection endpoint. The API is much more useful than the standard one in the following points.
1. Developers can make the introspection API perform complex validation for certificate binding ([RFC 8705](https://www.rfc-editor.org/rfc/rfc8705.html)), [DPoP](https://datatracker.ietf.org/doc/draft-ietf-oauth-dpop/), resources ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html)), scopes ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html)) and subject of resource owner.
2. In error cases, the introspection API prepares an error message that conforms to [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750.html) so that protected resource endpoints can conform to the specification easily.
3. Developers can tie arbitrary key-value pairs to an access token by using Authlete's "Extra Properties" feature (cf. [How to add extra properties to an access token](/configuration-reference/tokens-and-claims/how-to-add-extra-properties-to-an-access-token)). A response from the introspection API includes the key-value pairs.
Authlete has enhanced the introspection API for Grant Management.
First, new response parameters, `grantId` and `grant`, have been added.
| Parameter | Description |
| :- | :- |
| `grantId` | The grant ID tied to the access token. The parameter holds a non-null value if the authorization request for the access token included `grant_management_action`. |
| `grant` | The inherited privileges. The parameter holds a non-null value if the authorization request for the access token included `grant_management_action=merge`. The `[Grant](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Grant.html)` Java class represents the format of the value. |
Second, the introspection API now recognizes a new request parameter, `resources`. And, validation logic for `scopes` and `resources` was rewritten from scratch and now behaves as the following table explains.
| scopes | resources | Behavior |
| :-: | :-: | :- |
| not given | not given | Not perform any validation on scopes and resources. |
| given | not given | Check whether all the specified scopes are covered by the access token. The inclusion check is performed per scope-resource cluster. For example, if an access token holds two clusters which are `"scope":"s1"` and `"scope":"s2"` respectively and the value of the `scopes` request parameter is \[`s1`, `s2`], the validation *fails*. For backward compatibility, resources of scope-resource clusters are *not* taken into consideration. |
| not given | given | Check whether all the specified resources are covered by the access token. The inclusion check is performed per scope-resource cluster. For example, if an access token holds two clusters which are `"resource":["r1"]` and `"resource":["r2"]` respectively and the value of the `resources` request parameter is \[`r1`, `r2`], the validation *fails*. Scopes of scope-resource clusters are *not* taken into consideration. |
| given | given | Check whether all the specified scopes and resources are covered by the access token. The inclusion check is performed per scope-resource cluster. |
### Grant Management Endpoint Implementation
A new API, `/api/gm`, has been added. Developers can implement the grant management endpoint by using the API.
The table below lists the request parameters of the `/api/gm` API. See the JavaDoc of the `[GMRequest](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/GMRequest.html)` class for details.
| Parameter | Necessity | Description |
| :- | :-: | :- |
| `gmAction` | REQUIRED | `QUERY` or `REVOKE` |
| `grantId` | REQUIRED | Grant ID |
| `accessToken` | REQUIRED | Access token |
| `clientCertificate` | OPTIONAL | \[[RFC 8705](https://www.rfc-editor.org/rfc/rfc8705.html)] Client certificate in the mutual TLS connection |
| `dpop` | OPTIONAL | \[[DPoP](https://datatracker.ietf.org/doc/draft-ietf-oauth-dpop/)] DPoP proof JWT |
| `htm` | OPTIONAL | \[[DPoP](https://datatracker.ietf.org/doc/draft-ietf-oauth-dpop/)] HTTP method (Authlete can compute the default from `gmAction`) |
| `htu` | OPTIONAL | \[[DPoP](https://datatracker.ietf.org/doc/draft-ietf-oauth-dpop/)] The URL of the endpoint (Authlete can compute the default from `Service.grantManagementEndpoint` and `grantId`) |
# Finally
Grant Management for OAuth 2.0 is supported from Authlete 2.3. The version is not deployed on the shared server (`api.authlete.com`) as of this writing. Please [contact us](https://www.authlete.com/contact/) for early access to Authlete 2.3.
*November 9, 2021*
*Responsibility for the wording and content of this article: Takahiko Kawasaki*
# JWT Authorization Grant (RFC 7523 2.1)
Source: https://developers.authlete.com/protocols-and-flows/advanced-flows/jwt-authorization-grant-rfc-7523-2-1
Explanation about JWT Authorization Grant defined in RFC 7523 Section 2.1 and Authlete's implementation.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/grant-types/jwt-authorization-grant).
# Overview
[Section 2.1. Using JWTs as Authorization Grants](https://www.rfc-editor.org/rfc/rfc7523.html#section-2.1) of [RFC 7523 JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://www.rfc-editor.org/rfc/rfc7523.html) defines another flow for access token issuance which is different from OAuth 2.0 standard flows defined in [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html). We call it **JWT Authorization Grant** flow.
In the flow, a JWT ([RFC 7519](https://www.rfc-editor.org/rfc/rfc7519.html)) is used as an **authorization grant**, which indicates that its holder has been authorized to get an access token. The **JWT as an authorization grant** is the same concept as an **authorization code** in the authorization code flow ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) [Section 4.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1)).
To obtain an access token, the requester presents a JWT at the **token endpoint** ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) [Section 3.2](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2)). The following diagram illustrates this flow:
# Specification
## Source of JWT
**[RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html) does not define details about how the JWT as an authorization grant is generated by whom**. Consequently, it is not defined in the specification how to obtain the key whereby to verify the signature of the JWT. Therefore, each deployment has to define their own rules which are necessary to identify the key for signature verification.
For example, some systems may define a rule like *"The JWT must be an ID Token issued by `https://example.com`"*. If a JWT is an ID Token, authorization server implementations can find the key for signature verification by using the standard mechanism (i.e. the discovery endpoint and the `jwks_uri` [server metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata)). Other systems may choose to embed the key for signature verification in the JWT itself as entity statements of [OpenID Connect Federation 1.0](https://openid.net/specs/openid-connect-federation-1_0.html) do.
In any case, systems must define additional rules to complement [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html) when they employ the specification.
## Token Request
### Grant Type
To distinguish the JWT authorization grant flow from others, the specification has defined a new grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`. The value is used as the value of the `grant_type` request parameter of a token request.
### Client Identification and Authentication
The specification does not require **[client authentication](https://darutk.medium.com/oauth-2-0-client-authentication-4b5f929305d4)** and even **client identification** at the token endpoint. The specification states it as follows.
> JWT authorization grants may be used with or without client authentication or identification.
Technically speaking, *"with or without client authentication"* means that the specification does not care about whether the **client type** ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) [Section 2.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.1)) of the client application is **public** or **confidential**, and *"without client identification"* means that a token request does not contain information whereby to identify the client application making the request (e.g. lack of the `client_id` request parameter).
### Scopes
A token request of the JWT authorization grant flow may have the `scope` request parameter to specify scopes to request as the standard OAuth 2.0 flows may do.
### Assertion
A JWT used as an authorization grant is specified by the `assertion` request parameter, which is defined in [Section 4.1. Using Assertions as Authorization Grants](https://www.rfc-editor.org/rfc/rfc7521.html#section-4.1) of [RFC 7521 Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants](https://www.rfc-editor.org/rfc/rfc7521.html).
The table below is a summary about necessity of JWT claims. Detailed requirements on the claims are described in [Section 3](https://www.rfc-editor.org/rfc/rfc7523.html#section-3) of [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html).
| Claim | Necessity |
| :-: | :-: |
| `iss` | REQUIRED |
| `sub` | REQUIRED |
| `aud` | REQUIRED |
| `exp` | REQUIRED |
| `nbf` | OPTIONAL |
| `iat` | OPTIONAL |
| `jti` | OPTIONAL |
### Example Request
The following is an example listed in [Section 2.1](https://www.rfc-editor.org/rfc/rfc7523.html#section-2.1) of [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html).
```http theme={null}
POST /token.oauth2 HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0.
eyJpc3Mi[...omitted for brevity...].
J9l-ZhwP[...omitted for brevity...]
```
## Token Response
Token responses in the JWT authorization grant flow conform to [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html). No additional response parameters are defined by [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html).
Only point to note is that `invalid_grant` must be used as the value of the `error` response parameter when the given JWT is invalid.
# Implementation
JWT authorization grant flow is supported by Authlete 2.3 onwards.
## Response from the `/auth/token` API
To support JWT authorization grant flow, a new action value `JWT_BEARER` was added. When the value of the `grant_type` request parameter of a token request is `urn:ietf:params:oauth:grant-type:jwt-bearer` and the request passes basic validation steps which are performed on Authlete server side, the value of the `action` response parameter in the response from Authlete's `/auth/token` API holds `JWT_BEARER`.
Authorization server implementations have to process the `JWT_BEARER` action to support JWT authorization grant flow. The `switch` statement below excerpted from [TokenRequestHandler.java](https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/TokenRequestHandler.java) in the [authlete-java-jaxrs](https://github.com/authlete/authlete-java-jaxrs/) library is an example of processing the `JWT_BEARER` action. The `switch` statement has a `case` entry for `JWT_BEARER`.
```java theme={null}
// Dispatch according to the action.
switch (action)
{
case INVALID_CLIENT:
// 401 Unauthorized
return ResponseUtil.unauthorized(content, CHALLENGE);
case INTERNAL_SERVER_ERROR:
// 500 Internal Server Error
return ResponseUtil.internalServerError(content);
case BAD_REQUEST:
// 400 Bad Request
return ResponseUtil.badRequest(content);
case PASSWORD:
// Process the token request whose flow is "Resource Owner Password Credentials".
return handlePassword(response);
case OK:
// 200 OK
return ResponseUtil.ok(content);
case TOKEN_EXCHANGE:
// Process the token exchange request (RFC 8693)
return handleTokenExchange(response);
case JWT_BEARER:
// Process the token request which uses the grant type
// urn:ietf:params:oauth:grant-type:jwt-bearer (RFC 7523).
return handleJwtBearer(response);
default:
// This never happens.
throw getApiCaller().unknownAction("/api/auth/token", action);
}
```
The following table lists response parameters related to JWT authorization grant in a response from the `/auth/token` API.
| Response Parameter | Type | Description |
| :- | :- | :- |
| `assertion` | string | The value of the `assertion` request parameter of the token request. |
| `scopes` | string array | The value of the `scope` request parameter of the token request. |
## Request Validation
When the grant type of a token request is `urn:ietf:params:oauth:grant-type:jwt-bearer`, Authlete (to be exact, the implementation of Authlete's `/auth/token` API) performs validation steps listed below in the order. Therefore, authorization server implementations can omit the same validation steps.
| No. | Validation |
| :-: | :- |
| 1 | Confirm that the `assertion` request parameter is given and its value is not empty. |
| 2 | Confirm that the format conforms to the JWT specification ([RFC 7519 JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519.html)). |
| 3 | Check if the JWT is encrypted and if it is encrypted, then (a) reject the token request when the `jwtGrantEncryptedJwtRejected` flag of the service is `true` or (b) skip remaining validation steps when the flag is `false`. |
| 4 | Confirm that the JWT contains the `iss` claim and its value is a JSON string. |
| 5 | Confirm that the JWT contains the `sub` claim and its value is a JSON string. |
| 6 | Confirm that the JWT contains the `aud` claim and its value is either a JSON string or an array of JSON strings. |
| 7 | Confirm that the issuer identifier of the service (the `issuer` server metadata) or the URL of the token endpoint (the `token_endpoint` server metadata) is listed as audience in the `aud` claim. |
| 8 | Confirm that the JWT contains the `exp` claim and the current time has not reached the time indicated by the claim. |
| 9 | Confirm that the current time is equal to or after the time indicated by the `iat` claim if the JWT contains the claim. |
| 10 | Confirm that the current time is equal to or after the time indicated by the `nbf` claim if the JWT contains the claim. |
| 11 | Check if the JWT is signed and if it is not signed, then (a) reject the token request when the `jwtGrantUnsignedJwtRejected` flag of the service is `true` or (b) finish validation on the JWT. |
Authlete does not validate an encrypted JWT because there is no standard way to obtain the key to decrypt the JWT with. This means that **you must validate an encrypted JWT by yourself**.
**Authlete does not verify the signature of the JWT** because there is no standard way to obtain the key to verify the signature of a JWT with. This means that **you must verify the signature of the JWT by yourself**.
## Configuration
To configure options related to the JWT Authorization Grant, follow these steps:
Log in to the [Authlete Management Console](https://console.authlete.com).
Navigate to **Service Settings > Endpoints > Token > JWT Authz Grant**.
The following options are available for configuration:
| Configuration Option | Description |
| - | - |
| **Client ID** | Toggle this option to reject token requests that do not include a client identifier. |
| **Encrypted JWT** | Toggle this option to allow or reject token requests using encrypted JWTs as authorization grants. |
| **Unsigned JWT** | Toggle this option to allow or reject token requests using unsigned JWTs as authorization grants. |

# Example
## Authorization Server Implementation Example
[JwtAuthzGrantProcessor.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/JwtAuthzGrantProcessor.java) in **[java-oauth-server](https://github.com/authlete/java-oauth-server)**, an open-source sample implementation of authorization server written in Java, is a sample implementation of processing a token request of the JWT authorization grant flow.
Note that the implementation is just an example and does not intend to be perfect for commercial use.
## Request and Response Example
**1. Prepare a JWT in some way or other.**
```shell theme={null}
$ JWT=eyJraWQiOiJhdXRobGV0ZS1mYXBpZGV2LWFwaS0yMDE4MDUyNCIsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJodHRwczovL2ZhcGlkZXYtYXMuYXV0aGxldGUubmV0LyIsInN1YiI6IjEwMDQiLCJhdWQiOlsiNTg5OTQ2MzYxNDQ0ODA2MyJdLCJleHAiOjE2NTg2NzExMzAsImlhdCI6MTY1ODY3MDgzMCwiYXV0aF90aW1lIjoxNjU4NjcwODMwLCJub25jZSI6IjEyMzQ1Njc4OSIsInZlcmlmaWVkX2NsYWltcyI6eyJ2ZXJpZmljYXRpb24iOnsidHJ1c3RfZnJhbWV3b3JrIjoibmlzdF84MDBfNjNBIn0sImNsYWltcyI6eyJnaXZlbl9uYW1lIjoiSW5nYSIsImZhbWlseV9uYW1lIjoiU2lsdmVyc3RvbmUiLCJiaXJ0aGRhdGUiOiIxOTkxLTExLTA2IiwiOmFnZV8xOF9vcl9vdmVyIjpudWxsfX19.mxE8FQaDb0edY_rWasSQ7pEMXbFon7oWr-Ccv1dB15q8eh2MaKRGrgvwPw_XjAdXlMNzkcV6iEUjRUvLGTvzm7_45cdoOxRX1xWzQw-vwvRbM46xd3Yht3EVjyRUUBJ_92J1yBmu7Nn93rygcnCE-fC_bSTSIJWgEnoC7dpxHYnoJ2QHrIOYFMBAA_3ZYCLGpgiWbIZnB2D1ib2eqwJ9zoJqeFNEBhXo9ThYkASHYaG-ZWofy7364lgeV4Rqy1r4XqzchFRW4yzWs_IM72bTtXTUkstlNOxZU12KEz50uVhtcOXv06iI71I9vceRP-ZVICpq7Knt0vEKWTM41E3ziw
```
**2. Make a token request.**
```shell theme={null}
$ curl http://localhost:8080/api/token -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer -d assertion=$JWT -d client_id=5908895171 -d scope=email
```
**3. Receive a token response.**
```json theme={null}
{
"access_token":"NEdL-q9EfOI4S5XzaMeimXAXVqS139Jm9DTYeLUAd5o",
"token_type":"Bearer",
"expires_in":86400,
"scope":"email"
}
```
# Native SSO
Source: https://developers.authlete.com/protocols-and-flows/advanced-flows/native-sso
Explanation about "OpenID Connect Native SSO for Mobile Apps 1.0" specification and Authlete's implementation.
## Concept
**[OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html)**
(hereafter referred to as "**Native SSO**") is a standard specification that
defines a mechanism for achieving single sign-on (SSO) across multiple mobile
applications under the control of the same vendor. If this specification is
implemented, users are not required to authenticate individually for each
mobile application; instead, a single authentication suffices for all
applications integrated via Native SSO.
## Specification Overview
Before diving into the details, let's start with an overview of the specification.
The first application (hereafter referred to as "App 1") obtains the following
set of tokens using the authorization code flow. Notably, the ID token conforms
to the Native SSO specification, and a new type of token called a
**device secret** is included.
1. Access Token
2. Refresh Token (optional)
3. ID Token (Native SSO-compliant)
4. Device Secret
App 1 stores the ID token and the device secret in shared storage that the
second application (hereafter referred to as "App 2") can access.
Next, App 2 retrieves the ID token and device secret from the shared storage
and sends a token exchange request using them as parameters.
In response, a token exchange response containing a new set of tokens for App 2
is returned.
To summarize, the following diagram illustrates the overview.
### Device Secret
The device secret is described in the Native SSO specification as follows.
> The device secret contains relevant data to the device and the current users
> authenticated with the device. The device secret is completely opaque to the
> client and as such the AS MUST adequately protect the value such as using a
> JWE if the AS is not maintaining state on the backend.
When issuing a device secret, the OpenID Provider obtains some form of
information about the device and associates it with the device secret. Then,
upon receiving a token exchange request, it verifies whether the device
associated with the device secret in the request matches the device from
which the request was sent.
## Specification Details
### App 1 Authorization Request
App 1 sends an authorization request based on the authorization code flow to
the OpenID Provider via a web browser. A Native SSO-specific requirement is
that the `scope` parameter must include both the `openid` scope and the
`device_sso` scope. The `device_sso` scope is defined by the Native SSO
specification.
The minimum required request parameters for an authorization request compliant
with Native SSO are as follows:
| Parameter | Description |
| :- | :- |
| `client_id` | The client identifier, such as `app_1`. |
| `response_type` | A space-separated list of the tokens being requested. When requesting an authorization code, it includes `code`. |
| `scope` | A space-separated list of the scopes being requested. To comply with the Native SSO specification, both `openid` and `device_sso` must be included. |
| `redirect_uri` | The redirect URI. According to the OpenID Connect specification, the `redirect_uri` parameter is required when requesting the `openid` scope. |
Here is an example of an authorization request:
```text theme={null}
https://trial.authlete.net/api/authorization?client_id=app_1&response_type=code&scope=openid+device_sso&redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection
```
This example actually works. When the authorization page is displayed, please
enter `inga` and `inga` as the login ID and password,
respectively. If you are already logged in and would like to display the login
ID field and password field again, add `&prompt=login`
at the end of the authorization request.
### App 1 Token Request
A token request is constructed using the authorization code obtained from the
above authorization request. The request parameters required are as follows:
| Parameter | Description |
| :- | :- |
| `grant_type` | This is the grant type. It is a mandatory parameter regardless of the flow used. For the authorization code flow, the value should be specified as `authorization_code`. |
| `code` | This is a mandatory parameter in the authorization code flow. It specifies the authorization code obtained from the authorization request. |
| `redirect_uri` | This is the redirect URI. If the `redirect_uri` parameter was included in the preceding authorization request, it is also required in the token request. Its value must be the same as the one specified in the authorization request. |
In addition to the above, depending on the application’s client type
([RFC 6749 Section 2.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.1)),
whether it is confidential or public, and which client authentication method is
used in the case of a confidential client, additional parameters may be required.
For example, if the client type is public, the `client_id` request parameter is
mandatory. On the other hand, if the client type is confidential and the
`private_key_jwt` client authentication method is used, the
`client_assertion` and `client_assertion_type`
request parameters are required. For more details on client authentication methods,
refer to
"[OAuth 2.0 Client Authentication](https://medium.com/@darutk/oauth-2-0-client-authentication-4b5f929305d4)".
The following is an example of a token request by a public client:
```text theme={null}
POST https://trial.authlete.net/api/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client_id=app_1&grant_type=authorization_code&code={{authorization_code}}&redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection
```
If you try this example in practice, replace `{{authorization_code}}`
with the actual value (the authorization code obtained as a result of the
authorization request).
### App 1 Token Response
The token response that conforms to Native SSO includes, in addition to the
access token and refresh token (optional), an ID token and a device secret
that comply with Native SSO. The device secret is returned as the value of
the `device_secret` property.
```json theme={null}
{
"access_token": "R28TIqhCydVvH2x2a3XsOzJykFEs7yFotO4ip-a2MbY",
"token_type": "Bearer",
"expires_in": 86400,
"scope": "openid device_sso",
"refresh_token": "lmlNXafSApRDAq7gZvy40ojya9bplgFSHczms46mTms",
"id_token": "eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzEiXSwiZXhwIjoxNzQ2NDM3MTE5LCJpYXQiOjE3NDYzNTA3MTksImF1dGhfdGltZSI6MTc0NjM1MDY3MiwiZHNfaGFzaCI6IlhrYmdHQ1JKUTFOQUhuS25NbjhKMFhIS25fOEVNenhCOWFRdUZITk0ycDQiLCJzaWQiOiJub2RlMDM4Y2F0N2ozMDhzZzE4MjhtMXNnMmRleGwzIn0.JAYlCEbGhjJwpgSZ4lUNaXkWD2ICeDs6FCBd3bKRvKPhrrGZKUAZDRij_Bmn_AF7DyTQS5ALHl82cJqjaLCcIw",
"device_secret": "b81d5ae9-9f85-4c6d-8658-1a36ffa42c83"
}
```
The value of the `id_token` property included in the token response is the
ID token:
```text theme={null}
eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzEiXSwiZXhwIjoxNzQ2NDM3MTE5LCJpYXQiOjE3NDYzNTA3MTksImF1dGhfdGltZSI6MTc0NjM1MDY3MiwiZHNfaGFzaCI6IlhrYmdHQ1JKUTFOQUhuS25NbjhKMFhIS25fOEVNenhCOWFRdUZITk0ycDQiLCJzaWQiOiJub2RlMDM4Y2F0N2ozMDhzZzE4MjhtMXNnMmRleGwzIn0.JAYlCEbGhjJwpgSZ4lUNaXkWD2ICeDs6FCBd3bKRvKPhrrGZKUAZDRij_Bmn_AF7DyTQS5ALHl82cJqjaLCcIw
```
The payload of this ID token, when decoded using base64url, is as follows.
The ID token compliant with Native SSO specification includes the `ds_hash`
claim and the `sid` claim.
```json theme={null}
{
"iss": "https://trial.authlete.net",
"sub": "1004",
"aud": [
"app_1"
],
"exp": 1746437119,
"iat": 1746350719,
"auth_time": 1746350672,
"ds_hash": "XkbgGCRJQ1NAHnKnMn8J0XHKn_8EMzxB9aQuFHNM2p4",
"sid": "node038cat7j308sg1828m1sg2dexl3"
}
```
The `ds_hash` claim is the hash value of the device secret. How this hash value
is calculated depends on the implementation, but the `ds_hash` claim allows the
ID token to be associated with the device secret.
The `sid` claim is a string that uniquely identifies the user's authentication
session, essentially the session ID.
App 1 stores the obtained ID token and device secret in a location accessible
to other applications that are integrated with Native SSO.
### App 2 Token Request
The Native SSO specification extends the
[RFC 8693: OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html)
specification by adding requirements to achieve Native SSO. App 2 retrieves the
ID token and device secret stored by App 1 and uses them to construct a token
exchange request that complies with the Native SSO specification.
For an explanation of RFC 8693, please refer to the article
"RFC 8693 OAuth
2.0 Token Exchange".
The request parameters for a token exchange request compliant with the Native
SSO specification are as follows:
| Parameter | Description |
| :- | :- |
| `grant_type` | The grant type. In a token exchange request, specify `urn:ietf:params:oauth:grant-type:token-exchange`. |
| `audience` | The intended recipient of the token issued by the token exchange request. In a Native SSO token exchange request, specify the identifier of the OpenID Provider. |
| `subject_token` | The token that indicates on whose behalf the token exchange is being performed. In a Native SSO token exchange request, this is the ID token. |
| `subject_token_type` | The identifier indicating the type of the `subject_token`. In a Native SSO token exchange request, the `subject_token` is always an ID token, so the value must be `urn:ietf:params:oauth:token-type:id_token`. |
| `actor_token` | The token that represents the actor executing the token exchange. In a Native SSO token exchange request, this is the device secret. |
| `actor_token_type` | The identifier indicating the type of the `actor_token`. In a Native SSO token exchange request, the `actor_token` is always a device secret, so the value must be `urn:openid:params:token-type:device-secret`. This is a token type newly defined by the Native SSO specification. |
| `scope` | The scopes to associate with the access token issued as a result of the token exchange request. This parameter is optional. |
In addition to the above, depending on the application’s client type
([RFC 6749 Section 2.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.1)),
whether it is confidential or public, and which client authentication method is
used in the case of a confidential client, additional parameters may be required.
For example, if the client type is public, the `client_id` request parameter is
mandatory. On the other hand, if the client type is confidential and the
`private_key_jwt` client authentication method is used, the
`client_assertion` and `client_assertion_type`
request parameters are required. For more details on client authentication methods,
refer to
"[OAuth 2.0 Client Authentication](https://medium.com/@darutk/oauth-2-0-client-authentication-4b5f929305d4)".
The following is an example of a token exchange request by a public client:
```text theme={null}
POST https://trial.authlete.net/api/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client_id=app_2&grant_type=urn:ietf:params:oauth:grant-type:token-exchange&audience=https://trial.authlete.net&subject_token={{id_token}}&subject_token_type=urn:ietf:params:oauth:token-type:id_token&actor_token={{device_secret}}&actor_token_type=urn:openid:params:token-type:device-secret&scope=openid
```
If you want to try this example in practice, replace `{{id_token}}`
and `{{device_secret}}` with actual values.
### App 2 Token Response
The response to the token exchange request is almost identical to the token
response of the authorization code flow. The only difference is that it
includes the `issued_token_type` property. In the case
of Native SSO, the value of `issued_token_type` is
`urn:ietf:params:oauth:token-type:access_token`.
```json theme={null}
{
"access_token": "rH9115-g83z9zIiCJ1mzIe8mza3bX4NaBTWmGs5qqow",
"token_type": "Bearer",
"expires_in": 86400,
"scope": "openid",
"refresh_token": "_F7NMCU1ny8DQ-3Pru_owgII52gIew0T6wuWKeIrfL4",
"id_token": "eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzIiXSwiZXhwIjoxNzQ2NDM4MzUxLCJpYXQiOjE3NDYzNTE5NTEsImRzX2hhc2giOiJYa2JnR0NSSlExTkFIbktuTW44SjBYSEtuXzhFTXp4QjlhUXVGSE5NMnA0Iiwic2lkIjoibm9kZTAzOGNhdDdqMzA4c2cxODI4bTFzZzJkZXhsMyJ9.8jNNF5mpeHnbqp1FTK_1adR8FlgPmHK9_rwUzaz-o5P7RMyaelBaSj74IhxHY6wbCJeD0n_N14h8vD8zWYh-8w",
"device_secret": "b81d5ae9-9f85-4c6d-8658-1a36ffa42c83",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}
```
Extracting the ID token from the above response example:
```text theme={null}
eyJraWQiOiJaWUdJT0hZdUE5SXBVaWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6IjEwMDQiLCJhdWQiOlsiYXBwXzIiXSwiZXhwIjoxNzQ2NDM4MzUxLCJpYXQiOjE3NDYzNTE5NTEsImRzX2hhc2giOiJYa2JnR0NSSlExTkFIbktuTW44SjBYSEtuXzhFTXp4QjlhUXVGSE5NMnA0Iiwic2lkIjoibm9kZTAzOGNhdDdqMzA4c2cxODI4bTFzZzJkZXhsMyJ9.8jNNF5mpeHnbqp1FTK_1adR8FlgPmHK9_rwUzaz-o5P7RMyaelBaSj74IhxHY6wbCJeD0n_N14h8vD8zWYh-8w
```
and decoding its payload part using base64url will show this result:
```json theme={null}
{
"iss": "https://trial.authlete.net",
"sub": "1004",
"aud": [
"app_2"
],
"exp": 1746438351,
"iat": 1746351951,
"ds_hash": "XkbgGCRJQ1NAHnKnMn8J0XHKn_8EMzxB9aQuFHNM2p4",
"sid": "node038cat7j308sg1828m1sg2dexl3"
}
```
The values of the `ds_hash` and `sid` claims are the same as those in the ID
token received by App 1, but the value of the `aud` claim is different. In this
ID token, the identifier of App 2, `app_2`, is included in the `aud` array.
## Settings
Authlete supports Native SSO starting from version 3.0.
### Service Settings
#### nativeSsoSupported Property
A new boolean property, `nativeSsoSupported`, has been added to the service to
indicate whether Native SSO is supported. The default value of this property is
`false`, so if you want to enable Native SSO, you must explicitly set it to `true`.
The `nativeSsoSupported` property corresponds to the
`native_sso_supported` server metadata defined by the
Native SSO specification. When `nativeSsoSupported` is set
to `true`, the discovery document generated by Authlete’s
`/service/configuration` API (as defined in
[OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html))
will include the following entry:
```json theme={null}
"native_sso_supported": true
```
If `nativeSsoSupported` is set to `false`, Authlete behaves as if it has no
knowledge of Native SSO. For example, the `device_sso` scope no longer has any
special meaning, and the token type
`urn:openid:params:token-type:device-secret`
is treated as an unknown token type (which results in an error if specified as
the value of `actor_token_type`).
#### device\_sso Scope
For Native SSO, the authorization server must support the `device_sso` scope.
Be sure to explicitly register the `device_sso` scope.
Due to requirements in the OAuth 2.0 specification, an authorization server
does not treat unknown scopes as errors; it simply ignores them. As a result,
if the `device_sso` scope is not registered, including it in an authorization
request will not trigger any warning, and processing specific to Native SSO
will silently be skipped.
#### Service Grant Type
Please add `TOKEN_EXCHANGE` to the list of supported grant types of the service.
Additionally, Authlete has several configuration items related to Token Exchange,
so please verify that they are set correctly. Pay special attention to the
following two items:
| Property | Description |
| :- | :- |
| `tokenExchangeByConfidentialClientsOnly` | This boolean property indicates whether token exchange requests are limited to confidential clients only. If set to `true`, token exchange requests from public clients will not be accepted. |
| `tokenExchangeByPermittedClientsOnly` | This boolean property indicates whether token exchange requests are restricted to pre-approved clients. If set to `true`, token exchange requests from clients that have not been explicitly authorized will not be accepted. |
#### Console Configuration
To configure `Native SSO` in the Authlete Management Console:
1. Login to the [Authlete Management Console](https://console.authlete.com/)
2. Navigate to `Service Settings > Tokens and Claims > Advanced > Token Exchange`
3. Go to the `Native SSO` section and toggle `Allow` to enable.
4. Click `Save Changes` to apply the update.
To register the `device_sso` scope:
1. Login to the [Authlete Management Console](https://console.authlete.com/)
2. Navigate to `Service Settings > Tokens and Claims > Advanced > Scope`
3. Go to the `Supported Scopes` and click the `Add` button.
4. Enter `device_sso` for the scope name and click add.
5. Once back in the scope page, click `Save Changes` to apply the update.
To add the `Token_Exchange` Grant Type:
1. Navigate to `Service Settings > Endpoints > Global Settings > General`
2. Go to `Supported Grant Types`, and select the `Token_Exchange` option.
3. Click `Save Changes` to apply the update.
> **Note:** When using the `Token_Exchange` Grant Type, make sure to enable additional settings in the client. Go to `client settings > Tokens and claims > Advanced > Token Exchange` and enable the `Explicit Permission for Token Exchange` option.
### Client Settings
#### device\_sso Scope
Depending on the settings, the scopes that a client can request may be
restricted. In such cases, ensure that the `device_sso` scope is added
to the list of allowed scopes that can be requested.
#### Client Grant Type
Add `TOKEN_EXCHANGE` to the list of grant types that the client can use.
Also, if the service's `tokenExchangeByPermittedClientsOnly`
property is set to `true`, token exchange requests from clients that are not
explicitly permitted will be rejected. In such cases, you need to set the
`extension.tokenExchangePermitted` property of
the client to `true` in the client's settings.
#### Console Configuration
To add the `Token_Exchange` Grant Type:
1. Navigate to `Client Settings > Endpoints > Global Settings > General`
2. Go to `Supported Grant Types`, and select the `Token_Exchange` option.
3. Click `Save Changes` to apply the update.
To register the `device_sso` scope:
1. Login to the [Authlete Management Console](https://console.authlete.com/)
2. Navigate to `Client Settings > Tokens and Claims > Advanced > Scope`
3. Go to the `Add Requestable scope` and enter `device_sso`for the scope name.
4. Once back in the scope page, click `Save Changes` to apply the update.
## OpenID Provider Implementation
### Authentication Session and Device Secret Management
A key feature of Authlete is the complete separation of user authentication and
management from OAuth 2.0/OpenID Connect protocol processing, focusing only on
the latter, which has gained significant market support. Due to this unique
architecture, Authlete does not manage user authentication sessions.
Consequently, the management of authentication sessions is the responsibility
of the Authlete users (OpenID Provider implementers).
For ID tokens conforming to Native SSO, the session ID value must be embedded
as the `sid` claim, and this value will be managed by the OpenID Provider.
Another characteristic of the Authlete architecture is that it operates in the
backend and does not communicate directly with client applications. As a result,
Authlete cannot directly access information about the device on which the
client application is running. The OpenID Provider conforming to Native SSO
must issue a device secret associated with the device information, but Authlete
cannot do this. The generation and management of device secrets are also the
responsibility of the OpenID Provider.
On the other hand, Authlete generates ID tokens and token responses conforming
to Native SSO. The necessary session ID, device secret (and device secret hash)
for generating these must be passed from the OpenID Provider to Authlete
through the request parameters of Authlete APIs.
### Authorization Endpoint Implementation
When an authorization request is determined to be requesting Native SSO,
specifically when all of the following conditions are met, the response from
Authlete's `/auth/authorization` API will include a boolean
property `nativeSsoRequested` with the value `true`.
| | Condition |
| :- | :- |
| 1 | The service supports Native SSO. (The value of `Service.nativeSsoSupported` is set to `true`.) |
| 2 | The service supports the `openid` and `device_sso` scopes. |
| 3 | The client is permitted to request the `openid` and `device_sso` scopes. (The client is not restricted by the Requestable Scopes feature.) |
| 4 | The `scope` of the authorization request includes both `openid` and `device_sso`. |
| 5 | The service supports the authorization code flow. (`Service.supportedGrantTypes` includes `AUTHORIZATION_CODE`.) |
| 6 | The client declares that it uses the authorization code flow. (`Client.grantTypes` includes `AUTHORIZATION_CODE`.) |
| 7 | The `response_type` of the authorization request includes `code`. |
| 8 | The service supports this response type. (`Service.supportedResponseTypes` includes the corresponding response type.) |
| 9 | The client declares that it uses this response type. (`Client.responseTypes` includes the corresponding response type.) |
The conditions in this table may seem complex, but if we focus only on the
conditions of the authorization request, excluding the service and client
settings, it can be simplified: *"If the `scope` includes `openid` and
`device_sso` and the `response_type` includes `code`"*, it is considered
a request for Native SSO.
When the value of the `nativeSsoRequested` property is `true`, the
implementation of the authorization endpoint must include the `sessionId`
request parameter when calling the `/auth/authorization/issue`
API. The value should represent an identifier for the current user's
authentication session (i.e., the session ID). The value provided here will
be used as the value for the `sid` claim in the ID token.
The implementation of the authorization endpoint may pass the actual session
ID value as the `sessionId` parameter to Authlete, or it may pass a transformed
version of it. However, the string should not be too long. Generally, the upper
limit is around 150 characters. If the length of the string resulting from
encrypting the `sessionId` and base64url encoding it exceeds 255 characters, an
error will occur (the encryption logic is not publicly disclosed).
It is also acceptable to always pass the `sessionId` parameter to the
`/auth/authorization/issue` API, regardless of whether the
value of `nativeSsoRequested` in the
`/auth/authorization` API response is `true` or `false`. If the
authorization request does not request Native SSO, even if the `sessionId`
parameter is provided, Authlete will not embed the `sid` claim in the ID token.
### Token Endpoint Implementation (Verification)
If a token request is determined to be for Native SSO — specifically, if one of
the following sets of conditions is satisfied — then the value of the `action`
property in the response from Authlete's `/auth/token` API will
be `NATIVE_SSO`.
| | Conditions Set 1: Authorization Code Flow |
| :-: | :- |
| 1 | The service supports Native SSO. (The value of `Service.nativeSsoSupported` is set to true.) |
| 2 | The service supports the `openid` and `device_sso` scopes. |
| 3 | The client is allowed to request the `openid` and `device_sso` scopes. (The client is not restricted by the Requestable Scopes feature.) |
| 4 | The corresponding authorization request's `scope` contains both `openid` and `device_sso`. |
| 5 | The service supports the authorization code flow. (`Service.supportedGrantTypes` includes `AUTHORIZATION_CODE`.) |
| 6 | The client declares the use of the authorization code flow. (`Client.grantTypes` includes `AUTHORIZATION_CODE`.) |
| 7 | The value of the `grant_type` parameter is `authorization_code`. |
| | Conditions Set 2: Refresh Token Flow |
| :-: | :- |
| 1 | The service supports Native SSO. (The value of `Service.nativeSsoSupported` is set to true.) |
| 2 | The service supports the `device_sso` scope. |
| 3 | The client is allowed to request the `device_sso` scope. (The client is not restricted by the Requestable Scopes feature.) |
| 4 | The service supports the refresh token flow. (`Service.supportedGrantTypes` includes `REFRESH_TOKEN`.) |
| 5 | The client declares the use of the refresh token flow. (`Client.grantTypes` includes `REFRESH_TOKEN`.) |
| 6 | The value of the `grant_type` parameter is `refresh_token`. |
| 7 | Even if the scope range is narrowed by the `scope` parameter in the token request, the `device_sso` scope is still covered. |
| 8 | The presented refresh token is associated with the user authentication session. (Effectively, only refresh tokens generated by an authorization code flow compliant with Native SSO can be used.) |
| | Conditions Set 3: Token Exchange Flow |
| :-: | :- |
| 1 | The service supports Native SSO. (The value of `Service.nativeSsoSupported` is set to true.) |
| 2 | The service supports the token exchange flow. (`Service.supportedGrantTypes` includes `TOKEN_EXCHANGE`.) |
| 3 | The client declares the use of the token exchange flow. (`Client.grantTypes` includes `TOKEN_EXCHANGE`.) |
| 4 | Various settings for the service's token exchange flow (e.g., `tokenExchangeByConfidentialClientsOnly`) do not reject the client's token exchange request. |
| 5 | The value of the `grant_type` parameter is `urn:ietf:params:oauth:grant-type:token-exchange`. |
| 6 | The value of the `actor_token_type` parameter is `urn:openid:params:token-type:device-secret`. |
The conditions listed above may seem complex, but essentially, if Authlete
determines that it needs to generate an ID token and token response compliant
with Native SSO, the value of `action` will be `NATIVE_SSO`.
When the `action` is set to `NATIVE_SSO`, the implementation of the token
endpoint must call the `/nativesso` API to complete the token request process.
However, prior to that, it must validate the session ID and device secret and,
if necessary, generate the device secret.
If the `/auth/token` API response includes an `additionalClaims` field containing a JSON string with the `nonce` and `s_hash` claims, pass that value directly to the `/nativesso` API via the `claims` parameter:
```json theme={null}
{
"accessToken": "{{accessToken}}",
"deviceSecret": "{{deviceSecret}}",
"claims": "{{additionalClaims}}"
}
```
When `additionalClaims` is provided, the `claims` parameter must contain that exact JSON string. These claims are essential for ID token validation and will be embedded in the generated ID token by the `/nativesso` API.
#### Session ID Verification
When the value of `action` in the response from the `/auth/token`
API is `NATIVE_SSO`, the response will include a `sessionId` parameter, which
represents the user's authentication session, i.e., the session ID. The token
endpoint implementation must verify whether this session ID is still valid.
If it is invalid, the implementation should not call the `/nativesso` API.
Instead, it should generate a token response indicating the `invalid_grant`
error and return it to the client.
```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
{
"error": "invalid_grant",
"error_description": "The session ID is no longer valid."
}
```
The `sessionId` value in the response from the `/auth/token` API is originally
passed from the OpenID Provider to Authlete as the `sessionId` request parameter
in the `/auth/authorization/issue` API. Authlete does not
determine whether the session ID is valid (since it cannot), so the validation
of the session ID must be performed by the OpenID Provider.
If the token request is for the authorization code flow or refresh token flow,
the `sessionId` value is the session ID associated with the authorization code
or refresh token.
In the case of a token request for token exchange, the `sessionId` value is the
`sid` claim value from the ID token presented as the
`subject_token` parameter.
#### Device Secret Verification (in Authorization Code and Refresh Token Flows)
When the `action` value is `NATIVE_SSO` and the token request is for either the
authorization code flow or refresh token flow (i.e., when the `grantType` is
`AUTHORIZATION_CODE` or `REFRESH_TOKEN`), the
response from the `/auth/token` API may include the
`deviceSecret` parameter.
The value of this parameter will be the value of the `device_secret`
request parameter from the token request. However, note that this
`device_secret` request parameter is optional, meaning that it
may or may not be included in the request.
If the value of `deviceSecret` is not `null`, it must be
verified. If it is valid, the same value should be passed to the `/nativesso`
API later.
If the value of `deviceSecret` is either missing or invalid,
a new device secret must be generated and that value must be passed to the
`/nativesso` API.
#### Device Secret Verification (in Token Exchange Flow)
When the `action` value is `NATIVE_SSO` and the token request is for a token
exchange flow (i.e., `grantType` is `TOKEN_EXCHANGE`), the
response from the `/auth/token` API will always include both
the `deviceSecret` and `deviceSecretHash`
parameters.
The `deviceSecret` value corresponds to the device secret
specified in the `actor_token` parameter of the token request.
The `deviceSecretHash` value corresponds to the `ds_hash`
claim included in the ID token specified in the `subject_token`
parameter of the token request.
In the token endpoint implementation, the device secret hash must be verified
to ensure it corresponds to the correct device secret. If it does not match,
the `/nativesso` API should not be called. Instead, a token response indicating
the `invalid_grant` error should be generated and returned to the client.
```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
{
"error": "invalid_grant",
"error_description": "The device secret hash in the subject token does not correspond to the device secret."
}
```
### Token Endpoint Implementation (nativesso API call)
After the verification of the session ID and the verification or generation of
the device secret, the `/nativesso` API should be called to generate the ID
token and token response that comply with Native SSO.
#### nativesso Request
The `/nativesso` API accepts HTTP POST requests in either
`application/json` or
`application/x-www-form-urlencoded` formats.
The request parameters are as follows:
| Parameter | Necessity | Description |
| :- | :-: | :- |
| `accessToken` | Required | If the `jwtAccessToken` is included in the `/auth/token` API response, specify its value. If not, specify the value of `accessToken`. The provided value will be used as the `access_token` property in the token response prepared by the `/nativesso` API. |
| `refreshToken` | Optional | Specify the value of `refreshToken` included in the `/auth/token` API response. The specified value will be used as the `refresh_token` property in the token response prepared by the `/nativesso` API. |
| `deviceSecret` | Required | If the `deviceSecret` is included in the `/auth/token` API response, specify its value. If not, generate a new device secret and specify its value. The provided value will be used as the `device_secret` property in the token response prepared by the `/nativesso` API. |
| `deviceSecretHash` | Recommended | Specify the hash value of the device secret. The logic to derive the hash value from the device secret depends on the implementation of the OpenID Provider. If this parameter is omitted, the implementation of the `/nativesso` API will compute the SHA-256 hash of the `deviceSecret` value and use the base64url encoded version of the hash as the device secret hash. The value specified in the `deviceSecretHash` parameter, or the value generated by the `/nativesso` API, will be embedded as the `ds_hash` claim in the ID token generated by the `/nativesso` API. |
| `sub` | Optional | This is the `sub` claim value of the ID token generated by the `/nativesso` API. If this parameter is omitted, the subject linked to the access token specified in the `accessToken` parameter will be used as the `sub` claim value.
Since Authlete APIs that generate ID tokens accept a `sub` request parameter, the `/nativesso` API also accepts this parameter. However, using a different value from the subject of the access token as the `sub` claim may cause unintended inconsistencies, so please use this `sub` parameter carefully.
Regardless of the value of this `sub` parameter, when generating a new access token in the Native SSO token exchange flow, Authlete will set the `sub` claim from the subject token (the ID token generated from a previous `/nativesso` API call) as the subject of the new access token. Once the `/auth/token` API has returned the response, the access token has already been created, and the `sub` parameter in the `/nativesso` API cannot change the subject of the access token. |
| `claims` | Optional | Specify claims to be embedded in the ID token. The value must be a JSON object represented as a string.
For Native SSO, this parameter serves two purposes: 1. **Propagating required protocol claims:** When the `/auth/token` API response includes an `additionalClaims` field (containing `nonce` and `s_hash`), its value must be passed unchanged to the `claims` parameter. These claims are required for ID token validation. 2. **Adding custom claims:** If the implementation needs to embed additional custom claims, merge them with the `additionalClaims` JSON before sending it in this parameter.
In summary: - If `additionalClaims` is present and no custom claims are needed, set `claims` to that value directly. - If `additionalClaims` is present and custom claims are also needed, merge both JSON objects and send the result as `claims` parameter. - If `additionalClaims` is not returned and no custom claims are needed, you can omit the `claims` parameter or send an empty JSON object. - If `additionalClaims` is not returned but you need to add custom claims, set `claims` to the custom claims JSON. |
| `idtHeaderParams` | Optional | Specify additional parameters to be embedded in the JWS header of the ID token. The format must be a string representing a JSON object. |
| `idTokenAudType` | Optional | Specify the format of the `aud` claim in the ID token. If `array` is specified, the value of the `aud` claim will be a JSON array. If `string` is specified, the value will be a JSON string. If this `idTokenAudType` parameter is omitted, the `idTokenAudType` property of the service will be referenced. If this property is not set in the service, the value of the `aud` claim will be a JSON array. |
#### nativesso Response
The response message body from the `/nativesso` API is in JSON format. As with
many other Authlete APIs, the `/nativesso` API response also includes an
`action` property. The token endpoint implementation constructs the token
response according to the value of this `action`.
When the `action` is `OK`, it indicates that the `/nativesso` API processing
has successfully completed. In this case, the token endpoint implementation
should return a successful response (`200 OK`) to the client. The value of
the `responseContent` property in the `/nativesso` API response can be used
directly as the message body of the token response. Therefore, the success
response can be constructed as follows:
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
(Embed the value of responseContent here.)
```
When the `action` is `INTERNAL_SERVER_ERROR`, it indicates
that something has gone wrong on the Authlete side. For example, an issue such
as a database error might have occurred when retrieving the access token
specified by the `accessToken` parameter from the database.
In such cases, the token endpoint implementation should return an error
response to the client. The simplest implementation would be to return a
`500 Internal Server Error`.
```http theme={null}
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Cache-Control: no-store
(Embed the value of responseContent here.)
```
However, in a production environment, it may be better to return a more abstract
error (one that does not directly describe the nature of the issue), rather
than a `500` error.
When the `action` is `CALLER_ERROR`, it indicates that the issue lies with the
caller of the API (i.e., the implementation of the OpenID Provider).
For example, this could be due to missing a required parameter such as
`accessToken`.
If `CALLER_ERROR` is returned, please review the implementation of your OpenID
Provider.
### Single Logout
The process of logging out from multiple applications in a single operation is
called Single Logout (SLO). It complements Single Sign-On (SSO).
The Native SSO specification does not define a specific protocol to achieve
Single Logout. However, Single Logout can be realized by collectively deleting
the access tokens and refresh tokens associated with a particular session ID.
Authlete provides a Single Logout feature via the `/nativesso/logout`
API. This API accepts HTTP POST requests in either `application/json`
or `application/x-www-form-urlencoded` format.
It takes a single request parameter: `sessionId`.
When the `/nativesso/logout` API is called with the target
session ID specified in the `sessionId` parameter, all access tokens and
refresh tokens associated with that session ID will be deleted.
```http theme={null}
POST https://{{authlete_api_server}}/api/{{service_id}}/nativesso/logout HTTP/1.1
Authorization: Bearer {{authlete_access_token}}
Content-Type: application/json
{
"sessionId": "{{session_id}}"
}
```
```json theme={null}
{
"action": "OK",
"count": 2,
"resultCode": "A503001",
"resultMessage": "[A503001] The /nativesso/logout API call successfully deleted 2 access/refresh token record(s)."
}
```
Even if there are no access tokens or refresh tokens associated with the
specified session ID — and as a result, the number of deleted tokens is
zero — the `/nativesso/logout` API does not return an error.
```json theme={null}
{
"action": "OK",
"count": 0,
"resultCode": "A503002",
"resultMessage": "[A503002] The /nativesso/logout API call completed without deleting any access/refresh token records."
}
```
### Sample Implementation
The sample implementation of Native SSO on the OpenID Provider side is included
in the [java-oauth-server](https://github.com/authlete/java-oauth-server) and
[authlete-java-jaxrs](https://github.com/authlete/authlete-java-jaxrs) library.
Both are open-source implementations written in Java.
Below are some tips for reading through the sample implementation:
* The session ID passed to the `/auth/authorization/issue` API is obtained in the AuthorizationDecisionEndpoint by executing `HttpServletRequest.getSession(false).getId()`. However, since this session is the HTTP session between the web server and browser, a different mechanism would likely be used in a production-ready Native SSO implementation.
* The obtained session ID is passed to the AuthorizationDecisionHandler via the AuthorizationDecisionHandlerSpi interface.
* The AuthorizationDecisionHandler calls the Authlete `/auth/authorization/issue` API through the `callAuthorizationIssue` method of AuthleteApiCaller.
* The dispatch logic based on the `action` in the `/auth/token` API response is written in the TokenRequestHandler.
* If the `action` is `NATIVE_SSO`, TokenRequestHandler invokes the `nativeSso` method of the TokenRequestHandlerSpi interface.
* The implementation of TokenRequestHandlerSpi, TokenRequestHandlerSpiImpl, calls the `process` method of the NativeSsoProcessor from within its `nativeSso` method.
* The NativeSsoProcessor extracts the session ID, device secret, and device secret hash from the instance of the TokenResponse class, which represents the response from the `/auth/token` API.
* NativeSsoProcessor obtains the device identifier of the accessing device in the `retrieveDeviceId()` method. **Note: in the sample implementation, this method is empty.**
* The `validateParameters` method in NativeSsoProcessor performs validation on the session ID, device secret, device secret hash, and device identifier.
* The check for whether a session ID is valid is done by calling the `isActiveSessionId(String)` method of the SessionTracker. SessionTracker implements the HttpSessionListener interface and monitors session creation and deletion. It is registered as a listener in [web.xml](https://github.com/authlete/java-oauth-server/blob/master/src/main/webapp/WEB-INF/web.xml).
* The relationship between device secret, session ID, device secret hash, and device identifier is represented by the DeviceSecret class.
* Instances of DeviceSecret are managed by the DeviceSecretManager.
## Authlete Implementation
### Authlete Version
Authlete supports Native SSO starting from version 3.0.
### Native SSO Version
The first version of Authlete Native SSO was implemented based on draft 07 of
[OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html)
specification. Therefore, `urn:openid:params:\*`
identifiers are used instead of `urn:openid:params:\*`
identifiers, which were used in earlier drafts.
### Token Exchange Verification
If the token exchange request contains the `actor_token_type`
parameter, and its value is
`urn:openid:params:token-type:device-secret`,
Authlete will perform the following validation for the request parameters
specific to the token exchange request. Only if all validations pass, will
the `action` in the `/auth/token` API response be
`NATIVE_SSO`.
1. The `audience` parameter is specified and its value matches the service's OpenID Provider identifier (the value set in `Service.issuer`). Note that multiple `audience` values can be specified, and as long as one of them matches, it is sufficient.
2. The `requested_token_type` parameter is specified, and its value is a known token type.
3. The `subject_token_type` parameter is specified, and its value is `urn:ietf:params:oauth:token-type:id_token`.
4. The `subject_token` parameter is specified, and its value (ID token) passes all the following validation checks:
* It can be parsed as a JWT.
* It includes the `exp` claim, and its value is numeric. Note: In the context of Native SSO, it is not checked that the `exp` value is in the future relative to the current time. This means that an expired ID token can be specified as a subject token. (see: [id\_token usage](https://openid.net/specs/openid-connect-native-sso-1_0.html#name-id_token-usage))
* It includes the `iat` claim, and its value indicates the current time or a time in the past.
* If the `nbf` claim is included, its value indicates the current time or a time in the past.
* It includes the `iss` claim, and its value matches the service's OpenID Provider identifier (the value set in `Service.issuer`).
* It includes the `sub` claim, and its value is a string.
* It includes the `aud` claim, and its value is either a string or an array. If an array is used, it must contain at least one element, and all elements must be strings.
* If the `nonce` claim is included, its value is a string.
* It includes the `sid` claim, and its value is a string.
* It includes the `ds_hash` claim, and its value is a string.
* It is not a JWE.
* It is signed.
* The signature verification is successful.
5. The `actor_token` parameter is specified.
In draft 07 of the Native SSO specification, it is stipulated that if the
`scope` parameter is included in the token exchange request,
the scope list must include `openid`.
However, due to the reasons described in AB/Connect ISSUE 2178:
Native SSO the openid scope on token exchange, Authlete’s current
implementation does not check whether `openid` is included.
## References
* Specification: [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html)
* Specification: [RFC 8693: OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html)
* Native SSO Specification Source Code: [openid-connect-native-sso-1\_0.xml](https://bitbucket.org/openid/connect/src/master/openid-connect-native-sso-1_0.xml)
* Native SSO Specification Issue Tracker: [bitbucket.org/openid/connect/issues](https://bitbucket.org/openid/connect/issues?status=new\&status=open\&status=submitted\&component=Native%20SSO%20for%20Apps)
* Documentation: [RFC 8693 OAuth 2.0 Token Exchange](/protocols-and-flows/advanced-flows/oauth-2-0-token-exchange-rfc-8693)
# Device Flow (OAuth 2.0 Device Authorization Grant)
Source: https://developers.authlete.com/protocols-and-flows/advanced-flows/oauth-2-0-device-authorization-grant-device-flow
Technical information about device flow.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/endpoints/enabling-device-flow).
# What is Device Flow?
[RFC 8628 (OAuth 2.0 Device Authorization Grant)](https://tools.ietf.org/html/rfc8628) is a Proposed Standard, published by IETF in August 2019.
It defines an authorization flow (Device Flow) to issue access tokens for API clients running on devices with no Web browsers and/or that have limited capability for inputting text,
with end user consent.
# How it Works
The device flow implies the following preconditions.
* **User** can use devices with Web browsers (e.g. PC, smartphones, tablets)
* **Client** (e.g. TV, appliances) is a device that can establish an outbound connection to the Internet
* **Authorization Server** (e.g. streaming service providers) exposes Device Authorization Endpoint to clients
Here is an overview of the flow:
1. **Client (device)** sends a device authorization request to device authorization endpoint in Authorization Server.
2. **Authorization Server** replies a device authorization response to Client. The response contains the following main parameters:
* `user_code`: a value to be submitted from User to Authorization Server.
* `device_code`: a value for Client to send a token request to Authorization Server.
3. **Client** presents `user_code` to User.
4. The following processes run simultaneously.
* **Client** sends a token request that includes `device_code` to Authorization Server. The same requests are periodically made (i.e. polling) to Authorization Server until an access token (or an error) is returned.
* **User** goes to Authorization Server using an Web browser, authenticates itself, submits `user_code` and grants access to Client.
5. **Authorization Server** verifies the `user_code`, issues an access token and sends a token response including the token against the token request that contains the `device_code` that corresponds to the `user_code`.
As a result of the flow above, Client obtains the access token for authorized API requests.
# Authlete and Device Flow
Authlete [version 2.1 and later support the device flow](https://docs.authlete.com/#device-flow).
Developers can simply implement their authorization servers that support the device flow, using the following newly added or enhanced Authlete APIs.
* [`/device/authorization` API](https://docs.authlete.com/#device-authorization-api) (New)
* Processing a device authorization request. After an authorization server receives the request from a client, it forwards the content of the request as a value to this API.
Authlete inspects the content. If it is valid, Authlete creates another content of a device authorization response and send it back to the authorization server.
The authorization server passes the device authorization response back to the client.
* [`/device/verification` API](https://docs.authlete.com/#device-verification-api) (New)
* Verifying `user_code`.
* [POST /device/complete](/api-reference/device-flow/complete-device-authorization) API (New)
* Processing approval/denial of the ongoing device flow. The processing result affects a response from `/auth/token` API.
* [POST /auth/token](/api-reference/token-endpoint/process-token-request) API (Enhanced)
* Processing token requests.
It is enhanced to support `grant_type=urn:ietf:params:oauth:grant-type:device_code` and corresponding `device_code` parameter.
You can manage settings for the device flow using the Authlete Management Console.
# Detailed Information
* [Illustrated Device Flow (RFC 8628)](https://medium.com/@darutk/illustrated-device-flow-rfc-8628-d23d6d311acc)
* A detailed explanation of the device flow specification and Authlete's implementation and its usage.
* [Authlete API Reference](https://docs.authlete.com/)
* Reference to Authlete APIs.
* [java-oauth-server](https://github.com/authlete/java-oauth-server)
* A reference authorization server implementation using Authlete APIs.
It is freely available by Authlete under open source license and supports the device flow.
* [JavaDoc of authlete-java-common](https://authlete.github.io/authlete-java-common/)
* For further information about the Authlete APIs above,
refer to descriptions of Device\* classes in the JavaDoc of the [authlete-java-common library](https://github.com/authlete/authlete-java-common).
* [Sequence Diagrams Templates](https://www.authlete.com/resources/templates/sequence-diagrams/)
* A collection of templates to draw your sequence diagrams of OAuth 2.0 / OIDC (OpenID Connect) flows using Authlete.
It contains a diagram of the device flow using Authlete.
# RFC 9470 OAuth 2.0 Step Up Authentication Challenge Protocol
Source: https://developers.authlete.com/protocols-and-flows/advanced-flows/oauth-2-0-step-up-authentication-challenge-protocol-rfc-9470
Explanation about RFC 9470 OAuth 2.0 Step Up Authentication Challenge Protocol and how Authlete supports the specification.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/grant-types/stepup-authn).
# Overview
**[RFC 9470: OAuth 2.0 Step Up Authentication Challenge Protocol](https://www.rfc-editor.org/rfc/rfc9470.html)** *"introduces a mechanism for a resource server to signal to a client that the authentication event associated with the access token of the current request doesn't meet its authentication requirements and specify how to meet them."* (excerpt from "Abstract")
As the first step, a **client application** (a.k.a. **relying party**) accesses a **protected resource** endpoint with an **access token**.
The implementation of the endpoint extracts the access token from the request.
Next, the implementation of the endpoint extracts information about **user authentication** (which the **authorization server** performed during the course of issuing the access token) from the access token.
Then, the implementation of the endpoint checks whether the information about user authentication satisfies **authentication requirements** that the endpoint imposes.
If the requirements are not satisfied, the implementation of the endpoint returns an error response to the client application with information about the authentication requirements.
The diagram below illustrates the flow from making a request with an access token to returning an error response with authentication requirements.
After receiving an error response with authentication requirements, the client application will make an **authorization request** again. This time, the client application needs to ask the authorization server to issue an access token that satisfies the authentication requirements.
# Authentication Requirements
The OAuth 2.0 Step Up Authentication Challenge Protocol specification presumes two types of authentication requirements.
One is **authentication context class reference** (a.k.a. **ACR**) of user authentication. The other is **maximum authentication age** (a.k.a. **max age**) of user authentication.
## Authentication Context Class Reference (ACR)
User authentication is performed somewhere during an authorization flow. OAuth 2.0 and OpenID Connect do not define details about how to authenticate users, but client applications can specify criteria of user authentication by including a list of ACRs in an authorization request.
The authorization server tries to perform user authentication that satisfies at least one of the specified ACRs. However, no error is reported even if the authorization server cannot perform such user authentication unless the `acr` claim is requested as "essential" (described later).
### The `acr_values` Request Parameter
There are three ways to specify a list of ACRs. The first way is to use the `acr_values` request parameter. The parameter is defined in [Section 3.1.2.1](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) as follows.
* `acr_values`: OPTIONAL. Requested Authentication Context Class Reference values. Space-separated string that specifies the `acr` values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication Context Class satisfied by the authentication performed is returned as the `acr` Claim Value, as specified in Section 2. The `acr` Claim is requested as a Voluntary Claim by this parameter.
The value of the `acr_values` request parameter is a space-separated list of ACRs. It appears in an authorization request like the following.
```text theme={null}
https://as.example.com/authorize?acr_values=acr1+acr2+acr3&...
```
### The `claims` Request Parameter
The second way to specify a list of ACRs is to use the `claims` request parameter. The parameter is defined in [Section 5.5](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter) of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html). The parameter takes a JSON object as a value and the syntax of the JSON is complex.
The following JSON is an example value of the `claims` request parameter excerpted from OpenID Connect Core 1.0.
```json theme={null}
{
"userinfo":
{
"given_name": {"essential": true},
"nickname": null,
"email": {"essential": true},
"email_verified": {"essential": true},
"picture": null,
"http://example.info/claims/groups": null
},
"id_token":
{
"auth_time": {"essential": true},
"acr": {"values": ["urn:mace:incommon:iap:silver"] }
}
}
```
This example specifies a list of ACRs that has just one element, `urn:mace:incommon:iap:silver`. When an authorization request includes this JSON as the value of the `claims` request parameter, the authorization server tries to perform user authentication that satisfies criteria of `urn:mace:incommon:iap:silver`.
To make the authorization server return an error when none of the specified ACRs can be satisfied, the client application must request the `acr` as an **essential** claim. By adding `"essential":true` in the JSON object that follows a claim name, the client application can request the claim as essential. In the example below, the `auth_time` claim and the `acr` claim are requested as essential.
```json theme={null}
{
"id_token":
{
"auth_time": {
"essential": true
},
"acr": {
"values": ["urn:mace:incommon:iap:silver"],
"essential": true
}
}
}
```
The `acr_values` request parameter cannot request the `acr` claim as essential.
### The `default_acr_values` Client Metadata
The last way is to use the `default_acr_values` client metadata. The metadata is defined in [Section 2](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata) of [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html) as follows.
* `default_acr_values`: OPTIONAL. Default requested Authentication Context Class Reference values. Array of strings that specifies the default `acr` values that the OP is being requested to use for processing requests from this Client, with the values appearing in order of preference. The Authentication Context Class satisfied by the authentication performed is returned as the `acr` Claim Value in the issued ID Token. The `acr` Claim is requested as a Voluntary Claim by this parameter. The `acr_values_supported` discovery element contains a list of the supported `acr` values supported by this server. Values specified in the `acr_values` request parameter or an individual `acr` Claim request override these default values.
When an authorization request does not specify a list of ACRs explicitly, the value of the `default_acr_values` is, if set, used as a list of ACRs.
The `default_acr_values` client metadata cannot request the `acr` claim as essential.
### Unmet Authentication Requirements
If the `acr` claim is requested as an essential claim, the authorization server returns an error response when none of specified ACRs can be satisfied. If the authorization server supports "[OpenID Connect Core Error Code unmet\_authentication\_requirements](https://openid.net/specs/openid-connect-unmet-authentication-requirements-1_0.html)", `unmet_authentication_requirements` is used as the value of the `error` response parameter.
The following is the description about the error code in the specification.
* `unmet_authentication_requirements`: The Authorization Server is unable to meet the requirements of the Relying Party for the authentication of the End-User. This error code SHALL be used if the Relying Party wants the OP to conform to a certain Authentication Context Class Reference value using an essential claim `acr` claim as specified in Section 5.5.1.1. of OpenID Connect Core \[OpenID.Core] and the OP is unable to meet this requirement and MAY be used in other cases, if appropriate.
If the authorization server does not support the specification, it is up to the authorization server what error code is used.
### Unmet Authentication Requirements for Step Up Authentication
In the context of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html), no error is reported even if the authorization server cannot perform such user authentication unless the `acr` claim is requested as essential.
However, [OAuth 2.0 Step Up Authentication Challenge Protocol](https://www.rfc-editor.org/rfc/rfc9470.html) recommends that an ACR request by the `acr_values` request parameter (which requests the `acr` claim as a **voluntary** claim) be ***"treated as required"*** and the authorization server return the `unmet_authentication_requirements` error in case none of specified ACRs can be satisfied.
The recommendation slightly conflicts with the following paragraph excerpted from OpenID Connect Core 1.0.
> Note that even if the Claims are not available because the End-User did not authorize their release or they are not present, the Authorization Server **MUST NOT generate an error** when Claims are not returned, whether they are Essential or Voluntary, unless otherwise specified in the description of the specific claim.
However, in practice, the slight conflict won't be a big issue.
### Actual ACR Values
OpenID Connect Core 1.0 does not define actual ACR values. Therefore, to utilize ACR, it is necessary to define actual ACR values in a deployment-specific way. The following table shows examples of actual ACR values.
| Deployment | ACR Values |
| :- | :- |
| UK Open Banking | `urn:openbanking:psd2:ca` `urn:openbanking:psd2:sca` |
| AU CDR | `urn:cds:au:cdr:2` `urn:cds:au:cdr:3` |
| Open Banking Brasil | `urn:brasil:openbanking:loa2` `urn:brasil:openbanking:loa3` |
Authorization servers should advertise information about ACR values they support through their discovery documents. [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) defines the `acr_values_supported` server metadata for the purpose as follows.
* `acr_values_supported`: OPTIONAL. JSON array containing a list of the Authentication Context Class References that this OP supports.
## Maximum Authentication Age (Max Age)
The previous section states *"User authentication is performed somewhere during an authorization flow."* at the beginning. However, in practice, the authorization server may skip the user authentication process if it detects that a user has already been authenticated (= if a user has already logged in). Therefore, the time at which an authorization request was made is not necessarily equal to the time of user authentication that was performed for the authorization request.
The amount of time that has elapsed since the last user authentication is called "**authentication age**". A client application may want to ask the authorization server to redo user authentication during an authorization flow in a case where the authentication age exceeds a particular threshold. The threshold is called "**maximum authentication age**".
### The `max_age` Request Parameter
[Section 3.1.2.1](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) defines the request parameter `max_age` as follows in order to enable client applications to specify the maximum authentication age.
* `max_age`: OPTIONAL. Maximum Authentication Age. Specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated by the OP. If the elapsed time is greater than this value, the OP MUST attempt to actively re-authenticate the End-User. (The `max_age` request parameter corresponds to the OpenID 2.0 PAPE \[OpenID.PAPE] `max_auth_age` request parameter.) When `max_age` is used, the ID Token returned MUST include an `auth_time` Claim Value.
The value of the `max_age` is an integer that represents the maximum authentication age in seconds. It appears in an authorization request like the following.
```text theme={null}
https://as.example.com/authorize?max_age=600&...
```
### The `default_max_age` Client Metadata
If an authorization request does not contain the `max_age` request parameter but the `default_max_age` client metadata of the client is set, the value of the metadata is used as the maximum authentication age. The metadata is defined in [Section 2](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata) of [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html) as follows.
* `default_max_age`: OPTIONAL. Default Maximum Authentication Age. Specifies that the End-User MUST be actively authenticated if the End-User was authenticated longer ago than the specified number of seconds. The `max_age` request parameter overrides this default value. If omitted, no default Maximum Authentication Age is specified.
### The `prompt` Request Parameter
When a client application wants to ask the authorization server to perform user authentication unconditionally (regardless of whether the maximum authentication age has been reached or not) during an authorization flow, it can use the `prompt` request parameter with the `login` value included. Its use will look like the following.
```text theme={null}
https://as.example.com/authorize?prompt=login&...
```
See [Section 3.1.2.1](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) for details about the `prompt` parameter.
# Authentication Requirements Challenge
Protected resource endpoints that utilize the OAuth 2.0 Step Up Authentication Challenge Protocol specification return an error response when the presented access token does not satisfy either or both of the authentication requirements (namely ACR and Max Age).
The diagram below illustrates the flow from returning an error response to making an authorization request asking for an access token that satisfies authentication requirements.
Parameters in the error response are explained in the subsequent sections.
## The `insufficient_user_authentication` Error Code
To indicate that the reason of an error response is that authentication requirements are not satisfied, the specification defines an error code `insufficient_user_authentication` as follows.
* `insufficient_user_authentication`: The authentication event associated with the access token presented with the request doesn't meet the authentication requirements of the protected resource.
The error code appears in the `WWW-Authenticate` HTTP header of an error response as shown below.
```text theme={null}
WWW-Authenticate: Bearer error="insufficient_user_authentication",...
```
## The `acr_values` Parameter
To list ACRs one of which an access token must satisfy, the specification defines a parameter `acr_values` as follows.
* `acr_values`: A space-separated string listing the authentication context class reference values, in order of preference, one of which the protected resource requires for the authentication event associated with the access token.
The parameter is used in conjunction with the error code `insufficient_user_authentication` like below (excerpt from the specification):
```text theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="insufficient_user_authentication",
error_description="A different authentication level is required",
acr_values="myACR"
```
If a client application receives the error response above, the client application needs to make an authorization request that requests the `acr` claim as an essential with `myACR` included in `values` like below. Line-breaks in the example below are for display purposes only. An actual request does not contain line-breaks.
```text theme={null}
https://as.example.com/authorize?claims={
"id_token": {
"acr": {
"essential": true,
"values": [ "myACR" ]
}
}
}&...
```
If the authorization server treats the `acr_values` request parameter in the way recommended by the OAuth 2.0 Step Up Authentication Challenge Protocol specification, `acr_values=myACR` would be enough.
## The `max_age` Parameter
To indicate the maximum authentication age which an access token must satisfy, the specification defines a parameter `max_age` as follows.
* `max_age`: Indicates the allowable elapsed time in seconds since the last active authentication event associated with the access token.
The parameter is used in conjunction with the error code `insufficient_user_authentication` like below (excerpt from the specification):
```text theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="insufficient_user_authentication",
error_description="More recent authentication is required",
max_age="5"
```
If a client application receives the error response above, the client application needs to make an authorization request with the `prompt` request parameter with `login` included.
# Authentication Information Bound To Access Token
To enable protected resource endpoints to check whether a presented access token satisfies authentication requirements or not, the access token must hold information about the user authentication that was performed during the course of issuing the access token.
The OAuth 2.0 Step Up Authentication Challenge Protocol specification defines the following two attributes of access token that may appear in the payload part of JWT access tokens and the message body of token introspection responses (cf. [RFC 7662](https://www.rfc-editor.org/rfc/rfc7662.html)).
| Attribute | Description (excerpted from the specification) |
| :- | :- |
| `acr` | Authentication Context Class Reference. String specifying an Authentication Context Class Reference value that identifies the Authentication Context Class that the user authentication performed satisfied. |
| `auth_time` | Time when the user authentication occurred. A JSON numeric value representing the number of seconds from 1970-01-01T00:00:00Z UTC until the time of date/time of the authentication event. |
The JSON below is an example excerpted from the specification that shows how the `acr` and `auth_time` attributes appear in the payload part of JWT access tokens.
```json theme={null}
{
"active": true,
"client_id": "s6BhdRkqt3",
"scope": "purchase",
"sub": "someone@example.net",
"aud": "https://rs.example.com",
"iss": "https://as.example.net",
"exp": 1639528912,
"iat": 1618354090,
"auth_time": 1646340198,
"acr": "myACR"
}
```
# Authlete's Implementation
Authlete supports the OAuth 2.0 Step Up Authentication Challenge Protocol specification since version 2.3.
## Binding Authentication Information
Some Authlete APIs such as the `/auth/authorization/issue` API accept the `acr` request parameter and the `authTime` request parameter which are used as values of the `acr` claim and the `auth_time` claim in the payload of an **ID token** being issued (cf. [AuthorizationIssueRequest](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationIssueRequest.html)). Authlete 2.3 onwards binds information passed by the request parameters to an access token being issued, too.
The following set of shell commands simulates the authorization code flow ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) [Section 4.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1)) that issues an access token that holds information about ACR (`acr`) and authentication time (`auth_time`).
(1) Call the `/auth/authorization` API, which receives an authorization request as `parameters` and returns information about the request in JSON format (cf. [AuthorizationResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationResponse.html)).
```shell theme={null}
curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/authorization \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer ' \ --data-urlencode parameters="response_type=code&scope=openid&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${STATE}&claims={\"id_token\":{\"acr\":{\"essential\":true,\"values\":[\"${ACR}\"]}}}" > authorization_response.json
```
The value of `${ACR}` in the command line above needs to be registered in advance as one of "Supported Authentication Context Class References" (which corresponds to the `acr_values_supported` server metadata). Otherwise, the specified ACR would be ignored.
ACR values of an authorization request are listed in the `acrs` parameter in the response from the `/auth/authorization` API regardless of where the values come from (`acr_values`, `claims` or `default_acr_values`).
In addition, when the `acr` claim is requested as essential, the boolean parameter `acrEssential` in the response from the `/auth/authorization` API holds `true`.
(2) Call the `/auth/authorization/issue` API, which receives a ticket issued by the `/auth/authorization` API as `ticket`, generates tokens (an authorization code, an access token, and/or an ID token), prepares an authorization response that conforms to related standard specifications, and returns JSON (cf. [AuthorizationIssueResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationIssueResponse.html)).
```shell theme={null}
$ TICKET=`jq -r .ticket authorization_response.json`
$ AUTH_TIME=`date +%s`
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/authorization/issue \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer ' -d ticket=$TICKET -d subject=$SUBJECT -d authTime=$AUTH_TIME -d acr=$ACR > authorization_issue_response.json
```
(3) Call the `/auth/token` API, which receives a token request as `parameters`, generate tokens (an access token and optionally an ID token), prepares a token response that conforms to related standard specifications, and returns JSON (cf. [TokenResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/TokenResponse.html)).
```shell theme={null}
$ AUTHORIZATION_CODE=`jq -r .authorizationCode authorization_issue_response.json`
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/token \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer ' \
--data-urlencode parameters="client_id=${CLIENT_ID}&grant_type=authorization_code&code=${AUTHORIZATION_CODE}&redirect_uri=${REDIRECT_URI}" -d clientId=${CLIENT_ID} -d clientSecret=${CLIENT_SECRET} > token_response.json
```
The API call above assumes that the client type of the client is "confidential" and the [client authentication](https://darutk.medium.com/oauth-2-0-client-authentication-4b5f929305d4) method at the token endpoint (`token_endpoint_auth_method`) is `client_secret_basic`, which is implied by use of the `clientId` request parameter and the `clientSecret` parameter of the `/auth/token` API (cf. [TokenRequest](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/TokenRequest.html)).
## Unmet Authentication Requirements
Instead of calling the `/auth/authorization/issue` API, the implementation of the authorization endpoint may call the `/auth/authorization/fail` API to indicate that the authorization request in question failed.
The `/auth/authorization/fail` API requires the `reason` request parameter that indicates the reason of the error (cf. [AuthorizationFailRequest](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationFailRequest.html)). When `[ACR_NOT_SATISFIED](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationFailRequest.Reason.html#ACR_NOT_SATISFIED)` is specified as the value of the `reason` request parameter, Authlete 2.3 and newer versions prepare an error response whose `error` is `unmet_authentication_requirements` to conform to "[OpenID Connect Core Error Code unmet\_authentication\_requirements](https://openid.net/specs/openid-connect-unmet-authentication-requirements-1_0.html)".
```shell theme={null}
curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/authorization/fail \
-H 'Content-Type: application/json' \
-u 'Authorization: Bearer ' \
-d ticket=$TICKET -d reason=ACR_NOT_SATISFIED > authorization_fail_response.json
```
The `responseContent` parameter in the response from the `/auth/authorization/fail` API is a URL that points to the **redirection endpoint** of the client application with the `error` and other parameters (cf. [AuthorizationFailResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/AuthorizationFailResponse.html)).
```shell theme={null}
$ jq -r .responseContent authorization_fail_response.json
http://localhost:4000/api/mock/redirection/4803170471?error=unmet_authentication_requirements&error_description=%5BA060305%5D+The+authorization+request+requests+%27acr%27+as+essential%2C+but+the+authentication+performed+for+the+end-user+satisfies+none+of+the+requested+ACRs.&error_uri=https%3A%2F%2Fdocs.authlete.com%2F%23A060305&state=917bdc1ef38ba6f6c297b4e31ac84007&iss=https%3A%2F%2Fauthlete.com
```
The URL contains the `error_description` parameter unless the `errorDescriptionOmitted` property of the `Service` is `true` (cf. `Service.[isErrorDescriptionOmitted()](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#isErrorDescriptionOmitted--)`). The following is the URL-decoded value of the `error_description` parameter.
> \[A060305] The authorization request requests 'acr' as essential, but the authentication performed for the end-user satisfies none of the requested ACRs.
When the authorization request contains `response_mode=form_post` or `response_mode=form_post.jwt`, the value of the `responseContent` parameter in the response from the `/auth/authorization/fail` API is not URL but HTML.
## JWT Access Token
The format of access tokens issued by Authlete is configurable. When the `accessTokenSignAlg` property of `Service` is set (cf. `Service.[getAccessTokenSignAlg()](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#getAccessTokenSignAlg--)`), the format of access tokens is JWT (cf. [Enabling JWT-based access tokens](/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens)). Authlete 2.3 and newer versions embed the `acr` and `auth_time` claims into the payload of JWT access tokens as defined in the specification.
When the service is configured to generate JWT access tokens, the `jwtAccessToken` response parameter in responses from the `/auth/token` API holds a JWT access token (cf. [TokenResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/TokenResponse.html)).
```shell theme={null}
$ JWT_AT=`jq -r .jwtAccessToken token_response.json`
```
If you decode the payload part of the JWT access token,
```shell theme={null}
$ echo $JWT_AT | ruby -paF\\. -rbase64 -e '$_=Base64.urlsafe_decode64 $F[1]' | jq .
```
you will find that the `acr` claim and the `auth_time` claim are embedded there.
```json theme={null}
{
"sub": "taka",
"acr": "acr1",
"scope": "openid",
"auth_time": 1667321595,
"iss": "https://authlete.com",
"exp": 1667408271,
"iat": 1667321871,
"client_id": "5575687621",
"jti": "A1pURs-TMuyvueCoP2mSsntQX4-0IvYxlvectI4E2h8"
}
```
## Token Introspection (Standard)
Authlete-powered authorization servers can implement a token introspection endpoint that conforms to [RFC 7662](https://www.rfc-editor.org/rfc/rfc7662.html) by using Authlete's `/auth/introspection/standard` API. Implementations of protected resource endpoints may use the token introspection endpoint to get detailed information about access tokens.
The `/auth/introspection/standard` API receives a token introspection request as `parameters` and returns JSON (cf. [StandardIntrospectionResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/StandardIntrospectionResponse.html)).
```shell theme={null}
$ AT=`jq -r .accessToken token_response.json`
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/introspection/standard \
-H "Content-Type:application/json" \
-u 'Authorization: Bearer ' \
--data-urlencode parameters="token=${AT}" > introspection_standard_response.json
```
The JSON contains the `responseContent` parameter. The value of the parameter is a JSON string that conforms to [RFC 7662](https://www.rfc-editor.org/rfc/rfc7662.html).
The following command line extracts the value of `responseContent` and parses it as JSON.
```shell theme={null}
$ jq -r .responseContent introspection_standard_response.json | jq .
```
The result of the command line above will look like below. You can find the `acr` property and the `auth_time` property there.
```json theme={null}
{
"sub": "taka",
"acr": "acr1",
"scope": "openid",
"auth_time": 1667321595,
"iss": "https://authlete.com",
"active": true,
"token_type": "Bearer",
"exp": 1667408271,
"client_id": "5575687621"
}
```
## Token Introspection (Authlete)
Authlete provides the [/auth/introspection](https://docs.authlete.com/en/shared/latest#post-/api/-serviceId-/auth/introspection) API, which is also for access token introspection but different from the standard introspection endpoint.
Major advantages of the `/auth/introspection` API over the standard introspection endpoint are as follows.
1. Can perform validation that the standard introspection endpoint cannot.
2. Can prepare an error message that conforms to [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750.html).
The `/auth/introspection` API accepts the following request parameters as of this writing (cf. [IntrospectionRequest](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/IntrospectionRequest.html)).
| Parameter | Category | Version | Description |
| :- | :- | :-: | :- |
| `token` | Common | 1.1 | An access token to introspect. |
| `scopes` | Common | 1.1 | Scopes that should be covered by the access token. |
| `subject` | Common | 1.1 | The subject (user identifier) that should be associated with the access token. |
| `clientCertificate` | [RFC 8705](https://www.rfc-editor.org/rfc/rfc8705.html) | 2.0 | The client certificate used in the mutual TLS connection established between the client application and the protected resource endpoint. |
| `dpop` | [DPoP](https://www.rfc-editor.org/rfc/rfc9449.html) | 2.2 | The value of the DPoP HTTP header. |
| `htm` | [DPoP](https://www.rfc-editor.org/rfc/rfc9449.html) | 2.2 | The HTTP method of the request to the protected resource endpoint. |
| `htu` | [DPoP](https://www.rfc-editor.org/rfc/rfc9449.html) | 2.2 | The URL of the protected resource endpoint. |
| `resources` | [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) | 2.2 | Resource indicators that should be covered by the access token. |
| `targetUri` | [HTTP Msg Sig](https://www.rfc-editor.org/rfc/rfc9421.html) | 2.3 | The full URL of the request. |
| `headers` | [HTTP Msg Sig](https://www.rfc-editor.org/rfc/rfc9421.html) | 2.3 | The HTTP headers to be included in processing the signature. |
| `requestBodyContained` | [HTTP Msg Sig](https://www.rfc-editor.org/rfc/rfc9421.html) | 2.3 | Whether the request contains a message body. |
| `acrValues` | [Step Up Auth](https://www.rfc-editor.org/rfc/rfc9470.html) | 2.3 | The list of Authentication Context Class Reference values one of which the user authentication performed during the course of issuing the access token must satisfy. |
| `maxAge` | [Step Up Auth](https://www.rfc-editor.org/rfc/rfc9470.html) | 2.3 | The maximum authentication age which is the maximum allowable elapsed time since the user authentication was performed during the course of issuing the access token. |
Request parameters except `token` are related to access token validation. Ones to pay attention to here are the `acrValues` request parameter and the `maxAge` request parameter.
### ACR validation
If the `acrValues` request parameter is supplied, the `/auth/introspection` API checks whether the ACR bound to the given access token is included in the `acrValues` array. If the ACR is not included, the API prepares an error response that conforms to the OAuth 2.0 Step Up Authentication Challenge Protocol specification.
The following command line lets the `/auth/introspection` API validate ACR.
```shell theme={null}
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/introspection \
-H 'Content-Type:application/json' \
-u 'Authorization: Bearer ' \
-d token=$AT -d acrValues="acrX acrY" > introspection_response-acrValues.json
```
The `responseContent` parameter in the response from the API (cf. [IntrospectionResponse](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/IntrospectionResponse.html)) holds a string that should be used as the value of the `WWW-Authenticate` HTTP header. You will see that (a) the value of `error` is `insufficient_user_authentication` and (b) `acr_values` is included.
```shell theme={null}
$ jq -r .responseContent introspection_response-acrValues.json
Bearer error="insufficient_user_authentication",error_description="[A341302] The authentication context class 'acr1' of the user authentication that the authorization server performed during the course of issuing the access token is insufficient. User authentication of an access token must satisfy one of [acrX acrY] to access the protected resource.",error_uri="https://docs.authlete.com/#A341302",acr_values="acrX acrY"
```
### Max Age Validation
If the `maxAge` request parameter is supplied, the `/auth/introspection` API checks whether the specified time has elapsed since the user authentication time bound to the given access token. If the time has elapsed, the API prepares an error response that conforms to the OAuth 2.0 Step Up Authentication Challenge Protocol specification.
The following command line lets the `/auth/introspection` API validate authentication time.
```shell theme={null}
$ curl -v -X POST https://{api-cluster}.authlete.com/api/{serviceId}/auth/introspection \
-H 'Content-Type:application/json' \
-u 'Authorization: Bearer ' \
-d token=$AT -d maxAge=600 > introspection_response-maxAge.json
```
In this case, the string for the `WWW-Authenticate` HTTP header contains `max_age`.
```shell theme={null}
$ jq -r .responseContent introspection_response-maxAge.json
Bearer error="insufficient_user_authentication",error_description="[A340301] The time of the user authentication that the authorization server performed during the course of issuing the access token is too old, so re-authentication is needed. The maximum authentication age (max_age) required by the protected resource is 600.",error_uri="https://docs.authlete.com/#A340301",max_age="600"
```
# Finally
We released Authlete 3.0 in November 2024 ([announcement](https://www.authlete.com/news/20241106_authlete3/)). Naturally, it supports the OAuth 2.0 Step Up Authentication Challenge Protocol. Please take full advantage of it!
# RFC 8693 OAuth 2.0 Token Exchange
Source: https://developers.authlete.com/protocols-and-flows/advanced-flows/oauth-2-0-token-exchange-rfc-8693
Explanation about the "RFC 8693 OAuth 2.0 Token Exchange" specification and Authlete's implementation.
This page is for Authlete 3.0. For 2.x, see [this page](/v2/protocols-and-flows/grant-types/token-exchange).
# Introduction
[RFC 8693 OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html) (hereinafter "Token Exchange") is a technical standard that defines a way to get a new token by presenting an existing token and optionally one more existing token at the token endpoint.
The specification is very flexible. To put it the other way around, the specification does not define details that are necessary for secure token exchange. Therefore, you have to consider further details in the undefined part to implement the specification in an authorization server.
This article describes some key details to implement the Token Exchange specification and Authlete's support of this specification.
## Specification
The diagram below illustrates the token exchange flow.
The following sections explain token types and token exchange request/response.
### Tokens
There are two kinds of input tokens; a subject token and an actor token.
### Subject Token
"Subject Token" is a mandatory input token. It *"represents the identity of the party on behalf of whom the request is being made."* (excerpt from [Section 2.1. Request](https://www.rfc-editor.org/rfc/rfc8693.html#section-2.1) of [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html))
The specification expects that the subject identified by the subject token is used as the subject of the newly issued token.
### Actor Token
"Actor Token" is an optional input token. It *"represents the identity of the acting party."* (excerpt from [Section 2.1. Request](https://www.rfc-editor.org/rfc/rfc8693.html#section-2.1) of [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html))
If there is a need to distinguish the subject of the newly issued token from the acting party that uses the token, authorization server implementations may utilize the actor token and embed information about the acting party in the token to issue. [Section 4.1](https://www.rfc-editor.org/rfc/rfc8693.html#section-4.1) and [Section 4.4](https://www.rfc-editor.org/rfc/rfc8693.html#section-4.4) of [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html) define JWT claims related to the acting party.
### Token Types
As possible types of the tokens above, the specification lists the following **token types** and assigns token type identifiers to them. The identifiers of token types are registered at [OAuth URI](https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#uri) of [OAuth Parameters](https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml) of IANA (Internet Assigned Numbers Authority).
| Token Type | Token Type Identifier |
| :- | :- |
| JWT | `urn:ietf:params:oauth:token-type:jwt` |
| Access Token | `urn:ietf:params:oauth:token-type:access_token` |
| Refresh Token | `urn:ietf:params:oauth:token-type:refresh_token` |
| ID Token | `urn:ietf:params:oauth:token-type:id_token` |
| SAML 1.1 Assertion | `urn:ietf:params:oauth:token-type:saml1` |
| SAML 2.0 Assertion | `urn:ietf:params:oauth:token-type:saml2` |
The token type identifier for JWT is defined in [RFC 7519 JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519.html). Others are defined in [RFC 8693 OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html).
### Token Exchange Request
A **token exchange request** is a kind of token requests.
### Grant Type
To distinguish token exchange requests from other token requests, a new grant type `urn:ietf:params:oauth:grant-type:token-exchange` is defined in the specification. The value is used as the value of the `grant_type` request parameter of a token request.
### Client Identification and Authentication
The specification does not require **[client authentication](https://darutk.medium.com/oauth-2-0-client-authentication-4b5f929305d4)** and even **client identification** at the token endpoint. [Section 2.1. Request](https://www.rfc-editor.org/rfc/rfc8693.html#section-2.1) of [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html) states it as follows.
> The supported methods of client authentication and whether or not to allow **unauthenticated** or **unidentified** clients are deployment decisions that are at the discretion of the authorization server.
Technically speaking, "unauthenticated clients" means public clients (cf. [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) [Section 2.1. Client Types](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.1)) and "unidentified clients" means that a token request does not contain information whereby to identify the client making the request.
Although [Appendix 1.1](https://www.rfc-editor.org/rfc/rfc8693.html#section-a.1.1) of [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html) shows an example of token exchange request from an unidentified client, such cases are appropriate only in limited conditions, such as a closed network within a service. In other circumstances, you have to define further details, which are out of the scope of [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html), for secure token exchange.
### Request Parameters
The following table shows parameters related to token exchange requests.
| Request Parameter | Necessity | Description |
| :- | :- | :- |
| `grant_type` | REQUIRED | The value `urn:ietf:params:oauth:grant-type:token-exchange` indicates that the token request is a token exchange request. |
| `resource` | OPTIONAL | Resources to be tied to the newly issued token. This request parameter may be specified multiple times. See [RFC 8707 Resource Indicators for OAuth 2.0](https://www.rfc-editor.org/rfc/rfc8707.html) for details. |
| `audience` | OPTIONAL | Audience of the newly issued token. This request parameter also may be specified multiple times. |
| `scope` | OPTIONAL | A list of space-delimited names of scopes that are to be tied to the newly issued token. |
| `requested_token_type` | OPTIONAL | The token type of the newly issued token that the client wishes. One of the registered token type identifiers. |
| `subject_token` | REQUIRED | The value of the subject token. |
| `subject_token_type` | REQUIRED | The token type of the subject token. One of the registered token type identifiers. |
| `actor_token` | OPTIONAL | The value of the actor token. |
| `actor_token_type` | OPTIONAL | The token type of the actor token. One of the registered token type identifiers. When the `actor_token` request parameter is given, this request parameter is REQUIRED. On the contrary, when the `actor_token` request parameter is not given, this request parameter must not be present. |
[RFC 6749 The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html) states *"Request and response parameters MUST NOT be included more than once."* The `resource` request parameter and `audience` request parameter are exceptions.
### Token Exchange Response
A **token exchange response** is a kind of token responses.
### Response Parameters
The following table shows parameters related to token exchange responses.
| Response Parameter | Necessity | Description |
| :- | :- | :- |
| `access_token` | REQUIRED | Regardless of the token type, the value of the newly issued token is set to this response parameter. |
| `issued_token_type` | REQUIRED | The token type of the newly issued token. One of the registered token type identifier. |
| `token_type` | REQUIRED | When the token type of the newly issued token is `urn:ietf:params:oauth:token-type:access_token`, this response parameter has the same meaning as defined in [RFC 6749 The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html). In other cases, `"N_A"` is set. |
| `expires_in` | OPTIONAL | The lifetime of the newly issued token in seconds. |
| `scope` | OPTIONAL | Scopes tied to the newly issued token. When the actual set of scopes tied to the newly issued token is different from the set requested by the token exchange request, this response parameter is REQUIRED. |
| `refresh_token` | OPTIONAL | A refresh token (cf. [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) [Section 6. Refreshing an Access Token](https://www.rfc-editor.org/rfc/rfc6749.html#section-6)). |
## Support in Authlete
This section describes Authlete's support for Token Exchange.
[RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html) is supported by Authlete 2.3 onwards.
### Token Exchange Request
### "action" value of /auth/token API response
On receiving a token request from a client, an authorization server will send the request content to Authlete's [`/auth/token`](https://docs.authlete.com/en/shared/latest#post-/api/auth/token) API as one of request parameters. This API will make a response that includes `TOKEN_EXCHANGE` as a value of `action` parameter if the following conditions are met:
* The value of the `grant_type` request parameter of the token request is `urn:ietf:params:oauth:grant-type:token-exchange`
* The request passes basic validation steps which are performed on Authlete server side
The following table lists response parameters related to Token Exchange in a response from the `/auth/token` API.
| Response Parameter | Type | Description |
| :- | :- | :- |
| `resources` | string array | The values of the `resource` request parameters. |
| `audiences` | string array | The values of the `audience` request parameters. |
| `scopes` | string array | The scopes listed in the `scope` request parameter. |
| `requestedTokenType` | string | A string representing the `requested_token_type` request parameter such as `"ACCESS_TOKEN"`. (cf. [TokenType.java](https://authlete.github.io/authlete-java-common/com/authlete/common/types/TokenType.html)) |
| `subjectToken` | string | The value of the `subject_token` request parameter. |
| `subjectTokenType` | string | A string representing the value of the `subject_token_type` request parameter such as `"ACCESS_TOKEN"`. (cf. [TokenType.java](https://authlete.github.io/authlete-java-common/com/authlete/common/types/TokenType.html)) |
| `subjectTokenInfo` | object | Information about the subject token. Available only when the token type is either access token or refresh token. |
| `actorToken` | string | The value of the `actor_token` request parameter. |
| `actorTokenType` | string | A string representing the value of the `actor_token_type` request parameter such as `"ACCESS_TOKEN"`. (cf. [TokenType.java](https://authlete.github.io/authlete-java-common/com/authlete/common/types/TokenType.html)) |
| `actorTokenInfo` | object | Information about the actor token. Available only when the token type is either access token or refresh token. |
### Token Validation
Regarding validation on input tokens, the specification states as follows.
> In processing the request, the authorization server MUST perform the appropriate validation procedures for the indicated token type and, if the actor token is present, also perform the appropriate validation procedures for its indicated token type. The validity criteria and details of any particular token are beyond the scope of this document and are specific to the respective type of token and its content.
It means that an authorization server MUST perform an appropriate validation, but the RFC 8693 doesn't define any details about token validation and leaves them to authorization server implementations. To reduce the burden of authorization servers, Authlete's `/auth/token` API performs the following validation.
| No. | Validation |
| :-: | :- |
| 1 | Confirm that the value of the `requested_token_type` request parameter is one of the registered token type identifiers if the request parameter is given and its value is not empty. |
| 2 | Confirm that the `subject_token` request parameter is given and its value is not empty. |
| 3 | Confirm that the `subject_token_type` request parameter is given and its value is one of the registered token type identifiers. |
| 4 | Confirm that the `actor_token_type` request parameter is given and its value is one of the registered token type identifiers if the `actor_token` request parameter is given and its value is not empty. |
| 5 | Confirm that the `actor_token_type` request parameter is not given or its value is empty when the `actor_token` request parameter is not given or its value is empty. |
Furthermore, Authlete performs additional validation on the tokens specified by the `subject_token` request parameter and the `actor_token` request parameter according to their respective token types as shown below.
### Token type: JWT
| No. | Validation |
| :-: | :- |
| 1 | Confirm that the format conforms to the JWT specification ([RFC 7519 JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519.html)). |
| 2 | Check if the JWT is encrypted and if it is encrypted, then (a) reject the token exchange request when the `tokenExchangeEncryptedJwtRejected` flag of the service is `true` or (b) skip remaining validation steps when the flag is `false`. |
| 3 | Confirm that the current time has not reached the time indicated by the `exp` claim if the JWT contains the claim. |
| 4 | Confirm that the current time is equal to or after the time indicated by the `iat` claim if the JWT contains the claim. |
| 5 | Confirm that the current time is equal to or after the time indicated by the `nbf` claim if the JWT contains the claim. |
| 6 | Check if the JWT is signed and if it is not signed, then (a) reject the token exchange request when the `tokenExchangeUnsignedJwtRejected` flag of the service is `true` or (b) finish validation on the input token. |
| 7 | (The signature of the JWT is not verified.) |
Authlete does not validate an encrypted JWT because there is no standard way to obtain the key to decrypt the JWT with. This means that **you must validate an encrypted JWT by yourself** when one is used as an input token with the token type `urn:ietf:params:oauth:token-type:jwt` (if `tokenExchangeEncryptedJwtRejected` of your service is `false`).
Authlete does not verify the signature of the JWT because there is no standard way to obtain the key to verify the signature of a JWT with. This means that **you must verify the signature by yourself** when a signed JWT is used as an input token with the token type `urn:ietf:params:oauth:token-type:jwt`.
### Token type: Access Token
| No. | Validation |
| :-: | :- |
| 1 | Confirm that the token is an access token that has been issued by the Authlete server of your service. |
| 2 | Confirm that the access token has not expired. |
| 3 | Confirm that the access token belongs to the service. |
The validation steps above imply that access tokens issued by other systems cannot be used as a subject token or an actor token with the token type `urn:ietf:params:oauth:token-type:access_token`. Authlete may provide a configuration option to disable the validation steps in the future, but it is not planned at the time of this writing.
### Token type: Refresh Token
| No. | Validation |
| :-: | :- |
| 1 | Confirm that the token is a refresh token that has been issued by the Authlete server of your service. |
| 2 | Confirm that the refresh token has not expired. |
| 3 | Confirm that the refresh token belongs to the service. |
The validation steps above imply that refresh tokens issued by other systems cannot be used as a subject token or an actor token with the token type `urn:ietf:params:oauth:token-type:refresh_token`. Authlete may provide a configuration option to disable the validation steps in the future, but it is not planned at the time of this writing.
### Token type: ID Token
| No. | Validation |
| :-: | :- |
| 1 | Confirm that the format conforms to the JWT specification ([RFC 7519 JSON Web Token (JWT)](https://www.rfc-editor.org/rfc/rfc7519.html)). |
| 2 | Check if the ID Token is encrypted and if it is encrypted, then (a) reject the token exchange request when the `tokenExchangeEncryptedJwtRejected` flag of the service is `true` or (b) skip remaining validation steps when the flag is `false`. |
| 3 | Confirm that the ID Token contains the `exp` claim and the current time has not reached the time indicated by the claim. |
| 4 | Confirm that the ID Token contains the `iat` claim and the current time is equal to or after the time indicated by the claim. |
| 5 | Confirm that the current time is equal to or after the time indicated by the `nbf` claim if the ID Token contains the claim. |
| 6 | Confirm that the ID Token contains the `iss` claim and its value is a valid URI. In addition, confirm that the URI has the `https` scheme, no query component and no fragment component. |
| 7 | Confirm that the ID Token contains the `aud` claim and its value is a JSON string or an array of JSON strings. |
| 8 | Confirm that the value of the `nonce` claim is a JSON string if the ID Token contains the claim. |
| 9 | Check if the ID Token is signed and if it is not signed, then (a) reject the token exchange request when the `tokenExchangeUnsignedJwtRejected` flag of the service is `true` or (b) finish validation on the input token. |
| 10 | Confirm that the signature algorithm of the ID Token is asymmetric. |
| 11 | Verify the signature of the ID Token. |
Authlete does not validate an encrypted ID Token because there is no standard way to obtain the key to decrypt the ID Token with in the context of token exchange where the client ID for the encrypted ID Token cannot be determined. This means that **you must validate an encrypted ID Token by yourself** when one is used as an input token with the token type `urn:ietf:params:oauth:token-type:id_token` (if `tokenExchangeEncryptedJwtRejected` of your service is `false`).
The 10th validation step above implies that ID Tokens whose signature algorithm is symmetric (`HS256`, `HS384` or `HS512`) cannot be used as a subject token or an actor token with the token type `urn:ietf:params:oauth:token-type:id_token`. It is because the symmetric key to verify the signature with (i.e. a client secret) cannot be determined in the context of token exchange.
Signature verification is performed even in the case where the issuer of the ID Token is not your service. But in that case, the issuer must support the discovery endpoint defined in [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html). Otherwise, signature verification fails.
### Token type: SAML 1.1 Assertion
| No. | Validation |
| :-: | :- |
| 1 | (Authlete does not perform any validation for this token type.) |
### Token type: SAML 2.0 Assertion
| No. | Validation |
| :-: | :- |
| 1 | (Authlete does not perform any validation for this token type.) |
### Token Exchange Response
### /auth/token/create API
The authorization server performs necessary validation based on the response from the `/auth/token` API to determine what kind of token is issued, and make a token exchange response to the client.
The server uses Authlete's [/auth/token/create](https://docs.authlete.com/en/shared/latest#post-/api/auth/token/create) API to create the token exchange response, by specifying `TOKEN_EXCHANGE` as a value of `grantType`, which is one of request parameters to the API.
## Related Configuration Items
As already mentioned, you have to define details that are out of the scope of [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html), to make token exchange secure. For that purpose, Authlete provides the following configuration options so that you can utilize them to build an authorization server with Authlete.
### Service Configuration
| Configuration Option | Type | Description |
| :- | :-: | :- |
| Identifiable Clients Only | boolean | Whether to reject token exchange requests by unidentifiable clients - `Service.tokenExchangeByIdentifiableClientsOnly` ([JavaDoc](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#isTokenExchangeByIdentifiableClientsOnly--)) |
| Confidential Clients Only | boolean | Whether to reject token exchange requests by public clients - `Service.tokenExchangeByConfidentialClientsOnly` ([JavaDoc](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#isTokenExchangeByConfidentialClientsOnly--)) |
| Permitted Clients Only | boolean | Whether to reject token exchange requests by clients that have no explicit permission - `Service.tokenExchangeByPermittedClientsOnly` ([JavaDoc](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#isTokenExchangeByPermittedClientsOnly--)) |
| Encrypted JWT Rejected | boolean | Whether to reject token exchange requests which use encrypted JWTs as input tokens - `Service.tokenExchangeEncryptedJwtRejected` ([JavaDoc](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#isTokenExchangeEncryptedJwtRejected--)) |
| Unsigned JWT Rejected | boolean | Whether to reject token exchange requests which use unsigned JWTs as input tokens - `Service.tokenExchangeUnsignedJwtRejected` ([JavaDoc](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#isTokenExchangeUnsignedJwtRejected--)) |
### Client Extension Configuration
| Configuration | Type | Description |
| :- | :-: | :- |
| Explicit Permission for Token Exchange | boolean | Whether to have an explicit permission for token exchange - `ClientExtension.tokenExchangePermitted` ([JavaDoc](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/ClientExtension.html#isTokenExchangePermitted--)) |
## Examples
### Authorization Server Implementation
### Processing the TOKEN\_EXCHANGE Action
Authorization server implementations have to process the `TOKEN_EXCHANGE` action to support Token Exchange. The `switch` statement below excerpted from [TokenRequestHandler.java](https://github.com/authlete/authlete-java-jaxrs/blob/master/src/main/java/com/authlete/jaxrs/TokenRequestHandler.java) in the [authlete-java-jaxrs](https://github.com/authlete/authlete-java-jaxrs/) library is an example of processing the `TOKEN_EXCHANGE` action. The `switch` statement has a `case` entry for `TOKEN_EXCHANGE`.
```java theme={null}
// Dispatch according to the action.
switch (action)
{
case INVALID_CLIENT:
// 401 Unauthorized
return ResponseUtil.unauthorized(content, CHALLENGE);
case INTERNAL_SERVER_ERROR:
// 500 Internal Server Error
return ResponseUtil.internalServerError(content);
case BAD_REQUEST:
// 400 Bad Request
return ResponseUtil.badRequest(content);
case PASSWORD:
// Process the token request whose flow is "Resource Owner Password Credentials".
return handlePassword(response);
case OK:
// 200 OK
return ResponseUtil.ok(content);
case TOKEN_EXCHANGE:
// Process the token exchange request (RFC 8693)
return handleTokenExchange(response);
case JWT_BEARER:
// Process the token request which uses the grant type
// urn:ietf:params:oauth:grant-type:jwt-bearer (RFC 7523).
return handleJwtBearer(response);
default:
// This never happens.
throw getApiCaller().unknownAction("/api/auth/token", action);
}
```
### Processing token exchange requests
[TokenExchanger.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/TokenExchanger.java) in **[java-oauth-server](https://github.com/authlete/java-oauth-server)**, an open-source sample implementation of authorization server written in Java, is a sample implementation of processing a token exchange request.
Note that the implementation is just an example, and not intended for production use.
### Request and Response Example
### 1. Prepare an ID Token in some way or other.
```shell theme={null}
$ ID_TOKEN=eyJraWQiOiJhdXRobGV0ZS1mYXBpZGV2LWFwaS0yMDE4MDUyNCIsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJodHRwczovL2ZhcGlkZXYtYXMuYXV0aGxldGUubmV0LyIsInN1YiI6IjEwMDQiLCJhdWQiOlsiNTg5OTQ2MzYxNDQ0ODA2MyJdLCJleHAiOjE2NTg2NzExMzAsImlhdCI6MTY1ODY3MDgzMCwiYXV0aF90aW1lIjoxNjU4NjcwODMwLCJub25jZSI6IjEyMzQ1Njc4OSIsInZlcmlmaWVkX2NsYWltcyI6eyJ2ZXJpZmljYXRpb24iOnsidHJ1c3RfZnJhbWV3b3JrIjoibmlzdF84MDBfNjNBIn0sImNsYWltcyI6eyJnaXZlbl9uYW1lIjoiSW5nYSIsImZhbWlseV9uYW1lIjoiU2lsdmVyc3RvbmUiLCJiaXJ0aGRhdGUiOiIxOTkxLTExLTA2IiwiOmFnZV8xOF9vcl9vdmVyIjpudWxsfX19.mxE8FQaDb0edY_rWasSQ7pEMXbFon7oWr-Ccv1dB15q8eh2MaKRGrgvwPw_XjAdXlMNzkcV6iEUjRUvLGTvzm7_45cdoOxRX1xWzQw-vwvRbM46xd3Yht3EVjyRUUBJ_92J1yBmu7Nn93rygcnCE-fC_bSTSIJWgEnoC7dpxHYnoJ2QHrIOYFMBAA_3ZYCLGpgiWbIZnB2D1ib2eqwJ9zoJqeFNEBhXo9ThYkASHYaG-ZWofy7364lgeV4Rqy1r4XqzchFRW4yzWs_IM72bTtXTUkstlNOxZU12KEz50uVhtcOXv06iI71I9vceRP-ZVICpq7Knt0vEKWTM41E3ziw
```
### 2. Make a token exchange request.
```shell theme={null}
$ curl http://localhost:8080/api/token -d grant_type=urn:ietf:params:oauth:grant-type:token-exchange -d subject_token=$ID_TOKEN -d subject_token_type=urn:ietf:params:oauth:token-type:id_token -d client_id=5908895171 -d scope=email
```
### 3. Receive a token exchange response.
```json theme={null}
{
"access_token":"NEdL-q9EfOI4S5XzaMeimXAXVqS139Jm9DTYeLUAd5o",
"issued_token_type":"urn:ietf:params:oauth:token-type:access_token",
"token_type":"Bearer",
"expires_in":86400,
"scope":"email",
"refresh_token":"pK9f5OWIrx58_XWrE_SpCLLN-BM673ljliTSffjqwao"
}
```
# OAuth 2.0 and OpenID Connect Authorization Endpoints
Source: https://developers.authlete.com/protocols-and-flows/basic-oauth-oidc-flows/oauth-2-0-and-openid-connect-authorization-endpoints
How the OAuth 2.0 and OpenID Connect specifications define an authorization endpoint's behavior.
[OAuth 2.0](https://tools.ietf.org/html/rfc6749) and [OpenID Connect](https://openid.net/connect/) each specify requirements that an [authorization endpoint](https://tools.ietf.org/html/rfc6749#section-3.1) must satisfy to interoperate with client applications. This document explains those requirements, focusing on the differences between the two specifications.
If you are familiar with the specifications, you can jump straight to [Implementing an Authorization Endpoint with Authlete](/configuration-reference/endpoints/implementing-an-authorization-endpoint-with-authlete).
## 1. Path
The only constraint imposed by the OAuth 2.0 specfication on the authorization endpoint's URL path is that "The endpoint URI MUST NOT include a fragment component". (see [3.1 Authorization Endpoint](https://tools.ietf.org/html/rfc6749#section-3.1)). As long as this requirement is satisfied, a service can name its authorization endpoint freely. For instance, `/auth/authorization` is a valid authorization endpoint path. An example of an entire URI with the path is `https://example.com/auth/authorization`.
## 2. Security
OAuth 2.0 requires that the authorization endpoint use TLS (Transport Layer Security).
## 3. HTTP methods
According to section [3.1. Authorization Endpoint](https://tools.ietf.org/html/rfc6749#section-3.1) of the OAuth 2.0 specification, the authorization endpoint must support the HTTP `GET` method; the HTTP `POST` method is optional. However, [OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) requires that the authorization endpoint supports the HTTP `POST` method. In the case of `POST`, the request parameters must be formatted as `application/x-www-form-urlencoded`.
Authorization Endpoint HTTP Methods
| HTTP method | OAuth 2.0 | OpenID Connect |
| - | - | - |
| GET | MUST | MUST |
| POST | MAY | MUST |
## 4. Authorization Request Parameters
OAuth 2.0 defines 4 grant types (*flows* to get an access token) in [1.3. Authorization Grant](https://tools.ietf.org/html/rfc6749#section-1.3). Among the four, [Authorization Code Grant](https://tools.ietf.org/html/rfc6749#section-4.1) (a.k.a. Authorization Code Flow) and [Implicit Grant](https://tools.ietf.org/html/rfc6749#section-4.2) (a.k.a. Implicit Flow) access the authorization endpoint. Both grant types take the same request parameters, shown in the table below:
OAuth 2.0 Authorization Request Parameters
| Name | Requirement | Description |
| - | - | - |
| `response_type` | REQUIRED | `code` for Authorization Code Grant and `token` for Implicit Grant. |
| `client_id` | REQUIRED | The client ID of the client application making the authorization request. A client ID is an opaque number or string issued by a service. |
| `redirect_uri` | OPTIONAL | The URL to which the client application requests the result of the authorization request to be reported. If the client application has registered multiple redirect URIs or has not registered any redirect URI (this is allowed when the client type of the client application is "confidential"), this request parameter is REQUIRED. |
| `scope` | OPTIONAL | A space-delimited list of scopes (permissions) that the client application requires. The service defines a set of valid scope values. Some implementations of OAuth 2.0 (e.g. Facebook) do not correctly implement the specification, requiring a comma-delimited list. |
| `state` | RECOMMENDED | An opaque value that the client application may use. If this request parameter is contained in the authorization request, it is returned to the redirect URI as a query parameter. |
OpenID Connect adds many more authorization request parameters and values to the set defined by OAuth 2.0. Most of them are described in [OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). Here is a combined list of the request parameters defined in OAuth 2.0, OpenID Connect and other specifications.
OpenID Connect Request Parameters
| Name | Requirement | Description |
| - | - | - |
| `response_type` | REQUIRED | OAuth 2.0 defined only two values for this parameter: `code` and `token`. OpenID Connect also allows `none` and combinations of `code`, `id_token` and `token`. As a result, the complete set of valid values is:
`none`
`code`
`token`
`id_token`
`code token`
`code id_token`
`id_token token`
`code id_token token`
See [OAuth 2.0 Multiple Response Type Encoding Practices](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html) for details. |
| `client_id` | REQUIRED | The same requirement as OAuth 2.0. |
| `redirect_uri` | REQUIRED | In OAuth 2.0, this request parameter is OPTIONAL. But in OpenID Connect, it is REQUIRED. |
| `scope` | REQUIRED | In OAuth 2.0, this request parameter is OPTIONAL. But in OpenID Connect, it is REQUIRED and must include `openid`. |
| `state` | RECOMMENDED | The same requirement as OAuth 2.0. |
| `response_mode` | OPTIONAL | A new request parameter defined in [OAuth 2.0 Multiple Response Type Encoding Practices](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html). This request parameter specifies how the result of the authorization request should be formatted. |
| `nonce` | OPTIONAL | A new request parameter. An opaque string value that will be embedded in an ID token. If `response_type` request parameter contains `id_token` (meaning that an ID token is issued using Implicit Flow), `nonce` is REQUIRED. |
| `display` | OPTIONAL | A new request parameter to specify how the user interface should be displayed to the end-user. |
| `prompt` | OPTIONAL | A new request parameter to specify whether the service should prompt the end-user for re-authentication and consent. |
| `max_age` | OPTIONAL | A new request parameter to specify the maximum authentication age. |
| `ui_locales` | OPTIONAL | A new request parameter to specify preferred languages and scripts for the user interface. |
| `id_token_hint` | OPTIONAL | A new request parameter to specify the ID token previously issued by the service. |
| `login_hint` | OPTIONAL | A new request parameter to specify a hint about the login identifier that the end-user may use. |
| `acr_values` | OPTIONAL | A new request parameter to specify ACRs (Authentication Context Class References) one of which the client application requests to be satisfied. |
| `claims_locales` | OPTIONAL | A new request parameter to specify the end-user's preferred locales for ID token claims. This request parameter is defined in [OpenID Connect Core 1.0, 5.2. Claims Languages and Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts). |
| `claims` | OPTIONAL | A new request parameter to specify specific claims that the client application requests to be embedded in the ID token returned. This request parameter is defined in [OpenID Connect Core 1.0, 5.5. Requesting Claims using the "claims" Request Parameter](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter). |
| `request` | OPTIONAL | A new request parameter to specify a request object, which is a [JWT](https://tools.ietf.org/html/rfc7519) packing other request parameters and being signed and optionally encrypted. This request parameter is defined in [OpenID Connect Core 1.0, 6. Passing Request Parameters as JWTs](https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests). |
| `request_uri` | OPTIONAL | A new request parameter to specify the location of a request object. This request parameter is defined in [OpenID Connect Core 1.0, 6. Passing Request Parameters as JWTs](https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests). |
| `registration` | OPTIONAL | A new request parameter to provide additional registration information about the client application itself. This request parameter is defined in [OpenID Connect Core 1.0, 7.2.1. Providing Information with the "registration" Request Parameter](https://openid.net/specs/openid-connect-core-1_0.html#RegistrationParameter). |
| `code_challenge` | CONDITIONALLY REQUIRED | A new request parameter to specify a code challenge as a countermeasure against the code interception attack. This request parameter is defined in [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth Public Clients). Authorization server implementations may always require this parameter for authorization requests using [Authorization Code Flow](https://tools.ietf.org/html/rfc6749#section-4.1). See "Proof Key for Code Exchange (RFC 7636)" for details. |
| `code_challenge_method` | OPTIONAL | A new request parameter to tell the method used to generate a code challenge. This request parameter is defined in [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth Public Clients). Valid values are `plain` (default) and `S256`. See "Proof Key for Code Exchange (RFC 7636)" for details. |
# Authorization Response
## 1. Response Parameters
OAuth 2.0 specifies that a successful authorization results in the authorization endpoint issuing either an **authorization code** or an **access token**.
OpenID Connect adds another parameter that may be returned from the authorization endpoint (and/or the token endpoint): the **ID token**. [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) explains, "The primary extension that OpenID Connect makes to OAuth 2.0 to enable End-Users to be Authenticated is the ID Token data structure." (see "[OpenID Connect Core 1.0, 2. ID Token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)").
Authorization Response Parameters
| Parameter | Description |
| - | - |
| Authorization Code | An opaque string to be exchanged for an access token at the token endpoint. The authorization code is valid for a short period of time. |
| --- | --- |
| Access Token | An opaque string, issued by the authorization endpoint, representing the end-user and the permissions (scopes) they granted to a particular client application. (Note that an access token issued using [Client Credentials Flow](https://tools.ietf.org/html/rfc6749#section-4.4) contains no information about the end-user.) |
| --- | --- |
| ID Token | A [JSON Web Token](https://tools.ietf.org/html/rfc7519) (JWT) containing information about the end-user. |
Before OpenID Connect, the authorization endpoint could return either an authorization code or an access token, but not both. However, with OpenID Connect, the authorization endpoint can return all three objects depending on the value of the `response_type` request parameter:
`response_type` and Response Parameters
In a response from the authorization endpoint, an authorization code, an access token and an ID token are embedded using the `code` key, `access_token` key and `id_token` key, respectively. For example, `code=SplxlOBe` in a response means that the value of the authorization code is `SplxlOBe`.
The following response parameters may be returned from the authorization endpoint:
Authorization Endpoint Response Parameters
| |
| - |
| An authorization code issued to the client application. This is contained in a successful response when the `response_type` request parameter of the authorization request contained `code`. |
| An access token issued to the client application. This is contained in a successful response when the `response_type` request parameter of the authorization request contained `token`. |
| An ID token issued to the client application. This is contained in a successful response when the `response_type` request parameter of the authorization request contained `id_token`. |
| When an authorization request contains the `state` request parameter, the authorization endpoint includes this parameter in the authorization response with the same value provided by the client. |
| An error code. This is contained in a response when an error occurred. |
| The short description of the error which happened. This may be contained in a response when an error occurred. |
| The URI where the detailed description of the error can be found. This may be contained in a response when an error occurred. |
| The token type of the access token. This is contained in a successful response when the `response_type` request parameter of the authorization request contained `token`. |
| The lifetime of the access token in seconds. This may be contained in a successful response when the `response_type` request parameter of the authorization request contained `token`. |
| A space-delimited scope list of the access token. This may be contained in a successful response when the `response_type` request parameter of the authorization request contained `token`. |
## 2. Response Format
For both Authorization Code Flow and Implicit Flow, OAuth 2.0 specifies that a successful response from the authorization endpoint is HTTP status "[302 Found](https://tools.ietf.org/html/rfc2616#section-10.3.3)", redirecting the user agent (the end-user's web browser) to another location. In OAuth 2.0, the destination location is called **redirect URI**.
Response parameters are returned to the client application as a part of the redirect URI. For example, for an Authorization Code Flow with redirect URI `https://client.example.org/callback` and authorization code `ap8uacb2`, the response from the authorization endpoint to the client application will look like:
```text theme={null}
HTTP/1.1 302 Found
Location: https://client.example.org/callback?code=ap8uacb2
Cache-Control: no-store
Pragma: no-cache
```
If the flow is Implicit Flow, on the other hand, and the value of the access token is `pqb8u3t`, the response will look like:
```text theme={null}
HTTP/1.1 302 Found
Location: https://client.example.org/callback#access_token=pqb8u3t
&token_type=Bearer
&expires_in=3600
Cache-Control: no-store
Pragma: no-cache
```
(Line breaks added for clarity)
You might have noticed that the response parameters in the Authorization Code Flow are embedded in the query component (the part after '?') but those in Implicit Flow are embedded in the fragment component (the part after '#'). This difference is a requirement of the OAuth 2.0 specification. In either case, "302 Found" is used. The following table summarizes the relationship between `response_type` and the response parameters' location.
`response_type` And Response Parameter Location
| response\_type | HTTP Status | Response Parameter Location |
| - | - | - |
| code | 302 Found | Embedded in the query component of the redirect URI in the Location header. |
| token | 302 Found | Embedded in the fragment component of the redirect URI in the Location header. |
OpenID Connect, specifically the [OAuth 2.0 Form Post Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html), is more complex. It introduces a mechanism to control the response format and adds "[200 OK](https://tools.ietf.org/html/rfc2616#section-10.2.1)" with an HTML as a new response format. This format is used when an authorization request includes the `response_mode` parameter with a value of `form_post`. Here's an example from the specification (line breaks added for clarity).
```text theme={null}
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
Submit This Form
```
In the HTML above, the redirect URI is the value of the `action` attribute in the `form` tag; the response parameters are included in the form as hidden fields, `state` and `id_token`. (In this example, neither `code` nor `access_token` is embedded.)
After the HTML above is loaded by the user agent, the JavaScript written in the onload attribute of the body tag is executed. As a result, the browser is redirected to the redirect URI.
The table below illustrates the relationship between combinations of `response_type` & `response_mode` and the HTTP status & response parameters' location. Note that the query component is not usable when an access token and/or an ID token are contained in the response.
`response_type`/`response_mode` Combinations And HTTP Status/Response Parameters' Location
| | | | |
| - | - | - | - |
| | | | |
| | | | |
| Query component | Query component | Fragment component | Hidden parameters |
| | | | |
| Fragment component | NOT ALLOWED | Fragment component | Hidden parameters |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
## 3. Error Response
When an error occurs while a service is processing an authorization request, the service returns an error response to the client application. If the redirect URI to which the error should be reported had been determined before the error occurred, the redirect URI can be used.
When a redirect URI can be used, the `error` response parameter is always embedded. In addition, the `error_description` response parameter and the `error_uri` response parameter may optionally be embedded. For example, an error response looks like the following:
```text theme={null}
HTTP/1.1 302 Found
Location: https://client.example.org/callback?
error=access_denied&
error_description=The%20end-user%20denied%20the%20authorization%20request.
Cache-Control: no-store
Pragma: no-cache
```
(line breaks added for clarity)
OAuth 2.0 defines error response parameter values which may be returned from the authorization endpoint in [4.1.2.1. Error Response](https://tools.ietf.org/html/rfc6749#section-4.1.2.1) (for Authorization Code Flow) and in [4.2.2.1. Error Response](https://tools.ietf.org/html/rfc6749#section-4.2.2.1) (for Implicit Flow). OpenID [Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) adds values in [3.1.2.6. Authentication Error Response](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). The table below collects the error codes in alphabetical order.
Authorization Endpoint Error Values
| Value | RFC 6749 | OpenID Connect |
| - | :-: | :-: |
| `access_denied` | | |
| `account_selection_required` | | |
| `consent_required` | | |
| `interaction_required` | | |
| `invalid_request` | | |
| `invalid_request_object` | | |
| `invalid_request_uri` | | |
| `invalid_scope` | | |
| `login_required` | | |
| `registration_not_supported` | | |
| `request_not_supported` | | |
| `request_uri_not_supported` | | |
| `server_error` | | |
| `temporarily_unavailable` | | |
| `unauthorized_client` | | |
| `unsupported_response_type` | | |
## 4. Returning Errors when the Redirect URI is Unavailable
Errors may occur before the redirect URI is determined. For example, if the specified client ID (`client_id`) is invalid, it is impossible to check whether the specified redirect URI (`redirect_uri`) has been registered or not, so the error cannot be reported to the redirect URI.
Section [3.1.2.4. Invalid Endpoint](https://tools.ietf.org/html/rfc6749#section-3.1.2.4) of the OAuth 2.0 specification says:
> If an authorization request fails validation due to a missing, invalid, or mismatching redirection URI, the authorization server SHOULD inform the resource owner of the error and MUST NOT automatically redirect the user-agent to the invalid redirection URI.
but the mechanism with which to inform the resource owner (end-user) of the error is not described anywhere. The authorization endpoint behavior is, therefore, determined by its implementer. Some candidate response formats:
| HTTP Status | Content-Type | Comment |
| - | - | - |
| 400 Bad Request | `text/html` | The error is described in HTML format and shown in the user agent. This is friendly to end-users and testers. |
| 400 Bad Request | `application/json` | The error is described in JSON format in the same way as it is for token endpoint (OAuth 2.0, [section 5.2. Error Response](https://tools.ietf.org/html/rfc6749#section-5.2)). This is friendly to client application developers. |
Considering that OpenID Connect has added a use case (`prompt=none`) where no user interaction is performed, `application/json` might be better.
# Authorization Interaction
## 1. Purpose Of Authorization Endpoint
The primary task of an authorization endpoint is to let an end-user grant authorization to a client application. In the normal case, this is achieved by displaying one or more HTML pages that
1. Show information about the client application and the requested permissions (scopes).
2. Provide a login form to authenticate the end-user.
3. Include buttons for the end-user to decide to "authorize" or "deny" the authorization request.
Here is a typical minimum set of UI components that an authorization endpoint can display:
Remember, OAuth 2.0 is a framework for **authorization**, not for **authentication**. As stated in [section 3.1. Authorization Endpoint](https://tools.ietf.org/html/rfc6749#section-3.1) of the specification, "The way in which the authorization server authenticates the resource owner (e.g., username and password login, session cookies) is beyond the scope of \[OAuth 2.0]". Be that as it may, the end-user must be authenticated at the authorization endpoint because an access token must be associated with a resource owner (except the case of [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4)).
Since OAuth 2.0 makes almost no mention of end-user authentication, implementers have implemented it as they liked. However, OpenID Connect adds mechanisms to control end-user authentication.
## 2. `prompt` Request Parameter
The optional `prompt` request parameter specifies "whether the Authorization Server prompts the End-User for reauthentication and consent". ([OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest))
Its value `none` or a space-delimited combination of `login`, `consent` and `select_account`:
| Value | Description |
| - | - |
| `login` | When `prompt` contains `login`, the end-user must be authenticated even when they have already logged in. |
| `consent` | When `prompt` contains `consent`, consent must be obtained from the end-user even when the authorization endpoint implementation knows that the consent was obtained in the past. |
| `select_account` | When `prompt` contains `select_account`, the authorization endpoint implementation should prompt the end-user to select a user account. |
| `none` | When `prompt` is `none`, the authorization endpoint implementation must process the authorization request without displaying any UI (i.e., without user interaction). |
The simplest implementation for a combination of `login`, `consent` and `select_account` is to always display a form having input fields for login ID and password. However, this is not appropriate if the authentication method at the authorization endpoint is different from the typical ID & password mechanism, for example, biometric authentication by fingerprints.
## 3. Authentication Context Class Reference
**Authentication Context Class Reference**, which is also referred to as **ACR** in OpenID Connect specifications, is a string representing a set of context, level and/or other attributes of an authentication method. For example, `urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport` represents the authentication method which is performed by presenting a password over a protected (i.e. TLS) session. (This example is an excerpt from [Authentication Context for the OASIS Security Assertion Markup Language (SAML) V2.0.](https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf))
[OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) does not specify any concrete ACR values other than "0". Instead, it just states that parties using ACR values (i.e. the OAuth server and the client application) *"need to agree upon the meanings of the values used"*. ([OpenID Connect Core 1.0, 2. ID Token, acr](https://openid.net/specs/openid-connect-core-1_0.html#IDToken))
### 3.1 `acr_values` Request Parameter
An authorization request can include the `acr_values` request parameter ([OpenID Connect Core 1.0, 3.1.2.1. Authentication Request, acr\_values](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)) to specify a list of ACRs in a preferred order. When this request parameter is present, the authorization endpoint implementation should satisfy one of them in authenticating the end-user.
### 3.2 `acr` Claim In `claims` Request Parameter
Another way to present a list of ACRs is by including the `acr` claim in the value of the `claims` request parameter. The following JSON is an example of a value of the `claims` request parameter (excerpt from [OpenID Connect Core 1.0, 5.5. Requesting Claims using the "claims" Request Parameter](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter)):
```text theme={null}
{
"userinfo":
{
"given_name": {"essential": true},
"nickname": null,
"email": {"essential": true},
"email_verified": {"essential": true},
"picture": null,
"http://example.info/claims/groups": null
},
"id_token":
{
"auth_time": {"essential": true},
"acr": {"values": ["urn:mace:incommon:iap:silver"] }
}
}
```
The requirement for ACR can be marked as "essential" only via the `claims` request parameter.
```text theme={null}
{
"id_token":
{
"acr":
{
"essential": true,
"values": ["urn:mace:incommon:iap:silver"]
}
}
}
```
If the `acr` claim is requested as essential, one of the ACRs listed in `values` must be satisfied. If none of them can be satisfied, the authorization endpoint implementation must return an error response to the client application. See [OpenID Connect Core 1.0, 5.5.1.1. Requesting the "acr" Claim](https://openid.net/specs/openid-connect-core-1_0.html#acrSemantics) for details.
### 3.3 `acr` Claim In ID Token
The `acr` claim is an optional claim that may be embedded in an ID token. See "[OpenID Connect Core 1.0, 2. ID Token, acr](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)" for details.
### 3.4 Supported ACRs
"[OpenID Connect Discovery 1.0, 3. OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata)" lists attributes of an OpenID provider. Among them, the `acr_values_supported` metadata contains a list of ACRs supported by the OpenID provider. In Authlete, the equivalent is the `supportedAcrs` property of [Service](/api-reference/service-management/get-service).
### 3.5 Default ACRs
"[OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata)" lists attributes of a client application. Among them, the `default_acr_values` metadata contains a list of the default ACRs of the client application that should be used when an authorization request from the client application does not have ACR values explicitly (by the `acr_values` request parameter or by the values of the `acr` claim in the `claims` request parameter). In Authlete, the equivalent is the `defaultAcrs` property of [Client](https://docs.authlete.com/#client).
## 4 Maximum Authentication Age
**Maximum Authentication Age** is "the allowable elapsed time in seconds since the last time the End-User was actively authenticated" ([OpenID Connect Core 1.0, 3.1.2.1. Authentication Request, max\_age](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)). If the elapsed time is greater than the maximum authentication age, the end-user must be re-authenticated even if he/she has already logged in.
### 4.1 `max_age` Request Parameter
An authorization request can include the `max_age` request parameter to specify the maximum authentication age.
### 4.2 Default Maximum Authentication Age
The `default_max_age` attribute listed in "[OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata)" is the maximum authentication age which is used when an authorization request from the client application does not have the `max_age` request parameter. In Authlete, the equivalent is the `defaultMaxAge` property of [Client](https://docs.authlete.com/#client).
## 5. `sub` claim
A client application can request a specific subject (an end-user identifier assigned by the service) from whom the client application wants to be granted authorization by specifying the value for the `sub` claim. The following is an example of a value of the `claims` request parameter that contains the `sub` claim with a value:
```text theme={null}
{
"id_token":
{
"sub": {"value": "248289761001"}
}
}
```
When an authorization request specifies a subject, the corresponding end-user must be authenticated. If this is not satisfied, the authorization endpoint implementation must return an error response to the client application. See [OpenID Connect Core 1.0, 3.1.2.2. Authentication Request Validation](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation) for details.
## 6. `login_hint` Request Parameter
A client application can give a hint about the login identifier to the authorization endpoint by using the `login_hint` request parameter. For example, an email address may be specified as the value.
## 7. `id_token_hint` Request Parameter
A client application can make an authorization request with the `id_token_hint` request parameter whose value is the ID token previously issued by the authorization server. The authorization server should return an error response when the end-user identified by the ID token is different from the end-user who is authenticated already or as a result of the request.
## 8. No Interaction
OpenID Connect introduced a mechanism for the authorization endpoint to return a response without user interaction. A client application can request it by including `prompt=none` in the authorization request.
An authorization request with `prompt=none` can be processed successfully only when all the following conditions are satisfied:
1. The end-user has already logged in.
2. If the maximum authentication age is specified by either the `max_age` request parameter or the `default_max_age` property of the client metadata, the elapsed time since the last authentication of the end-user does not exceed the maximum authentication age.
3. If a specific subject is requested by the `sub` claim in the value of the `claims` request parameter, the login ID of the end-user matches the subject.
4. If the `acr` claim is marked as essential in the value of the `claims` request parameter, the authentication method satisfies one of the authentication context class references which are listed in the values property of the `acr` claim).
5. If `claims` are requested, the end-user has consented to them in advance. (Means to obtain consent are beyond the specification of OpenID Connect.)
# Comprehensive API Protection with Standard Specifications
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/comprehensive-api-protection-with-standard-specifications
This article introduces how to fully leverage the standards developed since RFC 6749 to protect APIs.
## Introduction
The Internet Engineering Task Force ([IETF][IETF]) published [RFC 6749][RFC_6749],
the core specification of OAuth 2.0, on October 13, 2012. Since then, the
[OAUTH Working Group][IETF_OAUTH_WG] of the IETF has continued its work and
remains actively engaged in developing new standards.
As time has passed, the number of RFCs developed by the working group has
increased, now exceeding 30.
| | Date | RFC | Title |
| :-: | :-: | :-: | :- |
| 1 | 2012-10 | [RFC 6749][RFC_6749] | [The OAuth 2.0 Authorization Framework][RFC_6749]★★★ |
| 2 | 2012-10 | [RFC 6750][RFC_6750] | [The OAuth 2.0 Authorization Framework: Bearer Token Usage][RFC_6750]★★★ |
| 3 | 2012-10 | [RFC 6755][RFC_6755] | [An IETF URN Sub-Namespace for OAuth][RFC_6755] |
| 4 | 2013-01 | [RFC 6819][RFC_6819] | [OAuth 2.0 Threat Model and Security Considerations][RFC_6819] |
| 5 | 2013-08 | [RFC 7009][RFC_7009] | [OAuth 2.0 Token Revocation][RFC_7009] |
| 6 | 2015-05 | [RFC 7519][RFC_7519] | [JSON Web Token (JWT)][RFC_7519]★★★ |
| 7 | 2015-05 | [RFC 7521][RFC_7521] | [Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants][RFC_7521] |
| 8 | 2015-05 | [RFC 7522][RFC_7522] | [Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants][RFC_7522] |
| 9 | 2015-05 | [RFC 7523][RFC_7523] | [JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants][RFC_7523]★★ |
| 10 | 2015-07 | [RFC 7591][RFC_7591] | [OAuth 2.0 Dynamic Client Registration Protocol][RFC_7591]★ |
| 11 | 2015-07 | [RFC 7592][RFC_7592] | [OAuth 2.0 Dynamic Client Registration Management Protocol][RFC_7592] |
| 12 | 2015-09 | [RFC 7636][RFC_7636] | [Proof Key for Code Exchange by OAuth Public Clients][RFC_7636]★★ |
| 13 | 2015-10 | [RFC 7662][RFC_7662] | [OAuth 2.0 Token Introspection][RFC_7662]★★ |
| 14 | 2016-04 | [RFC 7800][RFC_7800] | [Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)][RFC_7800] |
| 15 | 2017-06 | [RFC 8176][RFC_8176] | [Authentication Method Reference Values][RFC_8176] |
| 16 | 2017-10 | [RFC 8252][RFC_8252] | [OAuth 2.0 for Native Apps][RFC_8252] |
| 17 | 2018-06 | [RFC 8414][RFC_8414] | [OAuth 2.0 Authorization Server Metadata][RFC_8414]★ |
| 18 | 2019-08 | [RFC 8628][RFC_8628] | [OAuth 2.0 Device Authorization Grant][RFC_8628] |
| 19 | 2020-01 | [RFC 8693][RFC_8693] | [OAuth 2.0 Token Exchange][RFC_8693] |
| 20 | 2020-02 | [RFC 8705][RFC_8705] | [OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens][RFC_8705]★★ |
| 21 | 2020-02 | [RFC 8707][RFC_8707] | [Resource Indicators for OAuth 2.0][RFC_8707] |
| 22 | 2020-02 | [RFC 8725][RFC_8725] | [JSON Web Token Best Current Practices][RFC_8725] |
| 23 | 2021-10 | [RFC 9068][RFC_9068] | [JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens][RFC_9068] |
| 24 | 2021-08 | [RFC 9101][RFC_9101] | [The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR)][RFC_9101]★★ |
| 25 | 2021-08 | [RFC 9126][RFC_9126] | [OAuth 2.0 Pushed Authorization Requests][RFC_9126]★★ |
| 26 | 2022-03 | [RFC 9207][RFC_9207] | [OAuth 2.0 Authorization Server Issuer Identification][RFC_9207] |
| 27 | 2022-08 | [RFC 9278][RFC_9278] | [JWK Thumbprint URI][RFC_9278] |
| 28 | 2023-05 | [RFC 9396][RFC_9396] | [OAuth 2.0 Rich Authorization Requests][RFC_9396]★ |
| 29 | 2023-09 | [RFC 9449][RFC_9449] | [OAuth 2.0 Demonstrating Proof of Possession (DPoP)][RFC_9449]★★ |
| 30 | 2023-09 | [RFC 9470][RFC_9470] | [OAuth 2.0 Step Up Authentication Challenge Protocol][RFC_9470] |
| 31 | 2025-01 | [RFC 9700][RFC_9700] | [Best Current Practice for OAuth 2.0 Security][RFC_9700] |
| 32 | 2025-01 | [RFC 9701][RFC_9701] | [JSON Web Token (JWT) Response for OAuth Token Introspection][RFC_9701] |
| 33 | 2025-04 | [RFC 9728][RFC_9728] | [OAuth 2.0 Protected Resource Metadata][RFC_9728]★ |
| | | | |
| As of July 2025 | | | |
| | | | |
On the other hand, the OpenID Foundation ([OIDF][OIDF]) published the core
specification of OpenID Connect, [OpenID Connect Core 1.0][OIDC_CORE], in 2014.
The Foundation’s working groups have continued to actively develop new standards
ever since.
There are also a fair number of specifications that have been developed or
are currently being developed by the OIDF working groups.
| WG | Status | Title |
| :-: | :-: | :- |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect Core 1.0][OIDC_CORE]★★★ |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect Discovery 1.0][OIDC_DISCOVERY]★★ |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect Dynamic Client Registration 1.0][OIDC_DCR]★ |
| [AB/Connect][OIDF_ABC_WG] | Final | [OAuth 2.0 Multiple Response Type Encoding Practices][MULTI_RES_TYPES]★★ |
| [AB/Connect][OIDF_ABC_WG] | Final | [OAuth 2.0 Form Post Response Mode][FORM_POST] |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID 2.0 to OpenID Connect Migration 1.0][OIDC_MIGRATION] |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect RP-Initiated Logout 1.0][OIDC_RP_LOGOUT] |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect Session Management 1.0][OIDC_SESSION] |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect Front-Chanel Logout 1.0][OIDC_FRONT_LOGOUT] |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect Back-Channel Logout 1.0][OIDC_BACK_LOGOUT] |
| [AB/Connect][OIDF_ABC_WG] | Final | [OpenID Connect Core Error Code unmet\_authentication\_requirements][UNMET_AUTH_REQ] |
| [AB/Connect][OIDF_ABC_WG] | Final | [Initiating User Registration via OpenID Connect 1.0][PROMPT_CREATE] |
| [AB/Connect][OIDF_ABC_WG] | I/D | [OpenID Federation 1.0][OIDFED]★ |
| [AB/Connect][OIDF_ABC_WG] | I/D | [Self-Issued OpenID Provider v2][SIOPv2] |
| [AB/Connect][OIDF_ABC_WG] | I/D | [OpenID Connect Native SSO for Mobile Apps][NATIVESSO] |
| [AB/Connect][OIDF_ABC_WG] | Draft | [OpenID Connect Claims Aggregation][OIDC_CLAIMS] |
| [AB/Connect][OIDF_ABC_WG] | Draft | [OpenID Federation Extended Subordinate Listing 1.0][OIDFED_LISTING] |
| [AB/Connect][OIDF_ABC_WG] | Draft | [OpenID Federation Wallet Architectures 1.0][OIDFED_WALLET] |
| [AB/Connect][OIDF_ABC_WG] | Draft | [OpenID Connect Relying Party Metadata Choices 1.0][RP_META_CHOICES] |
| [AB/Connect][OIDF_ABC_WG] | Draft | [OpenID Provider Commands 1.0][PROVIDER_COMMANDS] |
| [AB/Connect][OIDF_ABC_WG] | Draft | [OpenID Connect Enterprise Extensions 1.0][OIDC_ENTERPRISE] |
| [AB/Connect][OIDF_ABC_WG] | Draft | [OpenID Connect Ephemeral Subject Identifier 1.0][OIDC_EPHEMERAL] |
| [AuthZEN][OIDF_AUTHZ_WG] | I/D | [Authorization API 1.0 - version 01](https://openid.net/specs/authorization-api-1_0-01.html) |
| [DCP][OIDF_DCP_WG] | Final | [OpenID for Verifiable Presentations 1.0][OID4VP]★★ |
| [DCP][OIDF_DCP_WG] | I/D | [OpenID for Verifiable Credential Issuance 1.0 - ID1][OID4VCI_ID1]★★ |
| [DCP][OIDF_DCP_WG] | Draft | [OpenID4VC High Assurance Interoperability Profile (HAIP) - WG Draft][HAIP_WG_DRAFT] |
| [DCP][OIDF_DCP_WG] | Draft | Security and Trust in OpenID for Verifiable Credentials |
| [DCP][OIDF_DCP_WG] | Draft | OpenID for Verifiable Presentations over BLE |
| [EAP][OIDF_EAP_WG] | Final | [OpenID Connect Extended Authentication Profile (EAP) ACR Values 1.0][EAP_ACR_VALUES] |
| [EAP][OIDF_EAP_WG] | Draft | OpenID Connect Token Bound Authentication 1.0 |
| [eKYC & IDA][OIDF_IDA_WG] | Final | [OpenID Identity Assurance Schema Definition 1.0][OPENID_IDA_SCHEMA] |
| [eKYC & IDA][OIDF_IDA_WG] | Final | [OpenID Connect for Identity Assurance Claims Registration 1.0][OIDC4IDA_CLAIMS] |
| [eKYC & IDA][OIDF_IDA_WG] | Final | [OpenID Connect for Identity Assurance 1.0][OIDC4IDA]★★ |
| [eKYC & IDA][OIDF_IDA_WG] | Final | [OpenID Attachments 1.0][OPENID_ATTACHMENTS] |
| [eKYC & IDA][OIDF_IDA_WG] | Draft | [OpenID Connect Authority claims extension][OIDC_AUTHORITY] |
| [eKYC & IDA][OIDF_IDA_WG] | Draft | [OpenID Connect Advanced Syntax for Claims (ASC) 1.0][OIDC_ASC] |
| [FAPI][OIDF_FAPI_WG] | Final | [FAPI 2.0 Security Profile][FAPI2_SECURITY]★★★ |
| [FAPI][OIDF_FAPI_WG] | Final | [FAPI 2.0 Attacker Model][FAPI2_ATTACKER] |
| [FAPI][OIDF_FAPI_WG] | Final | [Financial-grade API Security Profile (FAPI) 1.0 – Part 1: Baseline][FAPI1_BASELINE]★ |
| [FAPI][OIDF_FAPI_WG] | Final | [Financial-grade API Security Profile (FAPI) 1.0 – Part 2: Advanced][FAPI1_ADVANCED]★ |
| [FAPI][OIDF_FAPI_WG] | Final | [JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)][JARM]★★ |
| [FAPI][OIDF_FAPI_WG] | I/D | [Financial-grade API: Client Initiated Backchannel Authentication Profile][FAPI_CIBA] |
| [FAPI][OIDF_FAPI_WG] | I/D | [Grant Management for OAuth 2.0][GRANT_MANAGEMENT] |
| [FAPI][OIDF_FAPI_WG] | I/D | [FAPI 2.0 Message Signing][FAPI2_MESSAGE_SIGNING]★★ |
| [FAPI][OIDF_FAPI_WG] | Draft | [FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES]★ |
| [FastFed][OIDF_FASTFED_WG] | I/D | [FastFed Core 1.0 - ID1][FASTFED_CORE_ID1] |
| [FastFed][OIDF_FASTFED_WG] | I/D | [FastFed Enterprise SAML Profile 1.0 - ID1][FASTFED_SAML_ID1] |
| [FastFed][OIDF_FASTFED_WG] | I/D | [FastFed Enterprise SCIM Profile 1.0 - ID1][FASTFED_SCIM_ID1] |
| [HEART][OIDF_HEART_WG] | I/D | [Health Relationship Trust Profile for OAuth 2.0][HEART_OAUTH] |
| [HEART][OIDF_HEART_WG] | I/D | [Health Relationship Trust Profile for Fast Healthcare Interoperability Resources (FHIR) OAuth 2.0 Scopes][HEART_FHIR_OAUTH] |
| [HEART][OIDF_HEART_WG] | I/D | [Health Relationship Trust Profile for User-Managed Access 2.0][HEART_UMA] |
| [HEART][OIDF_HEART_WG] | I/D | [Health Relationship Trust Profile for Fast Healthcare Interoperability Resources (FHIR) UMA 2 Resources][HEART_FHIR_UMA] |
| [iGov][OIDF_IGOV_WG] | I/D | [International Government Assurance Profile (iGov) for OAuth 2.0][IGOV_OAUTH] |
| [iGov][OIDF_IGOV_WG] | I/D | [International Government Assurance Profile (iGov) for OpenID Connect][IGOV_OIDC] |
| [iGov][OIDF_IGOV_WG] | Draft | [International Government Assurance Profile (iGov) Use Cases][IGOV_USE_CASES] |
| [MODRNA][OIDF_MODRNA_WG] | Final | [OpenID Connect Client-Initiated Backchannel Authentication Flow – Core 1.0][CIBA]★★ |
| [MODRNA][OIDF_MODRNA_WG] | I/D | [OpenID Connect MODRNA Authentication Profile 1.0][MODRNA_AUTH] |
| [MODRNA][OIDF_MODRNA_WG] | I/D | [OpenID Connect Account Porting][ACCOUNT_PORTING] |
| [MODRNA][OIDF_MODRNA_WG] | I/D | [OpenID Connect User Questioning API 1.0][USER_QUESTIONING] |
| [MODRNA][OIDF_MODRNA_WG] | Draft | [OpenID Connect MODRNA Discovery Profile 1.0][MODRNA_DISCOVERY] |
| [MODRNA][OIDF_MODRNA_WG] | Draft | [OpenID Connect Mobile Registration Profile 1.0][MOBILE_REGISTRATION] |
| [Shared Signals][OIDF_SS_WG] | I/D | [OpenID Shared Signals Framework Specification 1.0][SSF]★★ |
| [Shared Signals][OIDF_SS_WG] | I/D | [OpenID Continuous Access Evaluation Profile 1.0][CAEP] |
| [Shared Signals][OIDF_SS_WG] | I/D | [OpenID RISC Profile Specification 1.0][RISC] |
| | | |
| I/D = Implementer’s Draft As of July 2025 | | |
| | | |
In addition to these, standards developed elsewhere are also referenced in the
context of OAuth and OpenID. For example, the
[OpenID Shared Signals Framework Specification 1.0][SSF] relies on the following
RFCs developed by the now-closed IETF’s [SECEVENT Working Group][IETF_SECEVENT_WG]:
| Date | RFC | Title |
| :-: | :-: | :- |
| 2018-07 | [RFC 8417][RFC_8417] | [Security Event Token (SET)][RFC_8417] |
| 2020-11 | [RFC 8935][RFC_8935] | [Push-Based Security Event Token (SET) Delivery Using HTTP][RFC_8935] |
| 2020-11 | [RFC 8936][RFC_8936] | [Poll-Based Security Event Token (SET) Delivery Using HTTP][RFC_8936] |
| 2023-12 | [RFC 9493][RFC_9493] | [Subject Identifiers for Security Event Tokens][RFC_9493] |
Similarly, [FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES] relies on the
following RFCs developed by the IETF’s [HTTPBIS Working Group][IETF_HTTPBIS_WG]:
| Date | RFC | Title |
| :-: | :-: | :- |
| 2021-02 | [RFC 8941][RFC_8941] | [Structured Field Values for HTTP][RFC_8941]★ |
| 2024-02 | [RFC 9421][RFC_9421] | [HTTP Message Signatures][RFC_9421]★ |
| 2024-02 | [RFC 9530][RFC_9530] | [Digest Fields][RFC_9530] |
As a discerning reader might suspect, once we start discussing dependencies,
there’s no end to it. This is because, in almost all cases, new specifications
are built on top of existing ones. As a result, newer specifications generally
require a greater amount of prerequisite knowledge to fully understand.
The problem with this situation is that, even when new standards for API
protection are published, the high barrier to investigating and evaluating
their contents hinders their adoption and use. As a result, the API security of
systems that are not proactively updated tends to decline over time in relative
terms. Given that attackers continue to evolve in increasingly sophisticated
ways, this is a serious concern.
This article introduces how to fully leverage the standards developed since
[RFC 6749][RFC_6749] to protect APIs. It begins with the basics—using
access tokens ([RFC 6750][RFC_6750]) and retrieving their information
([RFC 7662][RFC_7662])—and goes on to cover topics such as audience-restricted
access tokens using the `resource` parameter ([RFC 8707][RFC_8707]),
sender-constrained access tokens using MTLS ([RFC 8705][RFC_8705]) and DPoP
([RFC 9449][RFC_9449]), HTTP message signatures ([RFC 9421][RFC_9421]), and
[Cedar][CEDAR].
As Authlete is always committed to supporting developers, this article also
covers implementation-oriented considerations, such as:
* How to determine access token expiration while accounting for clock
skew between systems
* How to receive the client certificate used in mutual TLS between the
reverse proxy and the client
* How to compute the value of the derived component `@target-uri`
([RFC 9421][RFC_9421]) when operating behind a reverse proxy
* How to obtain the public key used to verify HTTP message signatures
Let’s get started!
## Access Token Usage
[RFC 6750][RFC_6750] (Bearer Token Usage) defines three methods for presenting
an access token to a resource server:
1. Using the `Authorization` header ([Section 2.1][RFC_6750_2_1])
2. Using a form parameter ([Section 2.2][RFC_6750_2_2])
3. Using a query parameter ([Section 2.3][RFC_6750_2_3])
However, the second method is rarely used because it restricts the format
of the HTTP request body to
`application/x-www-form-urlencoded`,
and the third method is generally avoided due to security concerns.
As a result, the first method is used in most cases.
In the method that uses the `Authorization` header, the value of the header
is formatted as follows:
```text theme={null}
Bearer Access Token
```
Here, the `Bearer` part is a fixed string, and the *Access Token* should
be replaced with the actual access token value.
The following is an example of accessing
`https://rs.example.com/resource/1`
with the access token
`MaSp49VXQnAmg_Si2i02PsvKlTSHMuxp3zJH00OA5mc`.
```text theme={null}
GET https://rs.example.com/resource/1
Authorization: Bearer MaSp49VXQnAmg_Si2i02PsvKlTSHMuxp3zJH00OA5mc
```
When the access token is secured using a mechanism called DPoP
([RFC 9449][RFC_9449]), the fixed string `Bearer` is replaced with `DPoP`.
In addition, a `DPoP` header must be included. The value of the `DPoP` header
is a type of JWT ([RFC 7519][RFC_7519]) known as a *DPoP proof JWT*.
```text theme={null}
Authorization: DPoP Access Token
DPoP: DPoP proof JWT
```
The following is an example of accessing a resource using a DPoP-bound
access token.
```text theme={null}
GET https://rs.example.com/resource/1
Authorization: DPoP MaSp49VXQnAmg_Si2i02PsvKlTSHMuxp3zJH00OA5mc
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiYWxnIjoiRVMyNTYiLCJjcnYiOiJQLTI1NiIsIngiOiIxQW1WcjRHb0hkUGdrNDhMV2RTM1Q5bTZtMW1QNFZUY2o5dXNvU0JuQ1FrIiwieSI6InRlLVdJdVVJcTJ3OHRYbVh5ZGxFWDRwZTlsTmUtUEJjb3pBOG43eThYVEUifX0.eyJqdGkiOiJvY1A3ZVc0d3l1bG0yWmMzIiwiaHRtIjoiR0VUIiwiaHR1IjoiaHR0cHM6Ly9ycy5leGFtcGxlLmNvbS9yZXNvdXJjZS8xIiwiaWF0IjoxNzU0MDMxMzU1LCJhdGgiOiJFWVlRSk9vWDlYLUt6TVBBNTI3NnBjelR0cHpVMjhMRzltQWRVeXdIa2dVIn0.Jg8sTKhaZvPJY0p--NaFEHJhkuM0SN4CHUbPe5xaxuZBLHZNfTtRJjpSb_6P-utmNYqQRuQ1rEPUq8ivKPP1MQ
```
Further details about DPoP will be provided later.
## Access Token Information Retrieval
To validate a presented access token, the resource server must first obtain
information about the token.
If the information is embedded within the access token itself, it can be
extracted by reading the contents of the token. On the other hand, if the
information is not embedded, the resource server must query the authorization
server’s introspection endpoint ([RFC 7662][RFC_7662]) to obtain it.
At first glance, embedding information directly into the access token may
appear to reduce the burden on the resource server. However, in this format,
the server must perform additional work to ensure that the contents of the
token have not been tampered with.
When information is embedded in the access token itself, the token format is
typically JWT ([RFC 7519][RFC_7519]), since it allows for tamper detection.
By verifying the JWT’s signature using the public key of the issuer (in this
context, the authorization server that issued the access token), the resource
server can confirm that the contents of the JWT have not been altered.
### JWT Access Token Signature Verification
Let’s now walk through the steps for verifying the signature of an access token
in JWT format.
First, read the value of the `iss` claim ([RFC 7519 Section 4.1.1][RFC_7519_4_1_1])
in the payload of the JWT. This claim identifies the issuer of the JWT. Since
access tokens are issued by the authorization server—or by an OpenID Provider
that also serves as the authorization server—the value of the `iss` claim in
a JWT access token represents the identifier of the authorization server or
OpenID Provider.
According to the relevant specifications ([RFC 8414][RFC_8414],
[OIDC Core][OIDC_CORE], and [OIDC Discovery][OIDC_DISCOVERY]), the identifier
of the authorization server or OpenID Provider must be a URL starting with
`https`. As a result, the value of the `iss` claim is a URL that identifies
the authorization server or OpenID Provider (i.e., an HTTP server) that issued
the access token.
Once the issuer identifier of the access token has been obtained, it can be
used to determine where the issuer’s metadata is published.
If the authorization server supports [RFC 8414][RFC_8414] (OAuth 2.0
Authorization Server Metadata), its metadata is published at the location
obtained by appending
`/.well-known/oauth-authorization-server`
to the issuer identifier. At this location, the server’s metadata can be
retrieved in JSON format.
However, if the issuer identifier includes a path component, the metadata
is published at the location obtained by inserting
`/.well-known/oauth-authorization-server`
between the host and the path. For details, see
[RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html),
[Section
3.1. Authorization Server Metadata Request](https://www.rfc-editor.org/rfc/rfc8414.html#section-3.1).
Alternatively, if the server supports
[OpenID Connect Discovery 1.0][OIDC_DISCOVERY], its metadata is published
at the location obtained by appending
`/.well-known/openid-configuration`
to the issuer identifier.
Server metadata contains a variety of information, but the most important
field here is the `jwks_uri` metadata. This field indicates the location
where the server’s JWK Set document is published. The JWK Set document can
be downloaded from this location.
The contents of the JWK Set document follow the format defined in
[RFC 7517][RFC_7517], [Section 5. JWK Set Format][RFC_7517_5]. This format is
a JSON object that contains a single top-level property called `keys`. The
value of the `keys` property is a JSON array of JWKs ([RFC 7517][RFC_7517]).
Among the JWKs, there should be a public key for verifying the signature of
the JWT access token. If the JWT’s JWS header includes the `kid` parameter
([RFC 7515 Section 4.1.4][RFC_7515_4_1_4]), the verification key can be
identified by looking for a JWK with the same `kid`
([RFC 7517 Section 4.5][RFC_7517_4_5]).
If the JWT does not include a `kid`, the method for identifying the
verification key will depend on the implementation. For example, the key may
be identified by filtering based on parameters such as `alg`
([RFC 7515 Section 4.1.1][RFC_7515_4_1_1], [RFC 7517 Section 4.4][RFC_7517_4_4])
and `use` ([RFC 7517 Section 4.2][RFC_7517_4_2]).
Once the verification key has been identified, it is used to verify the JWT’s
signature. If the verification succeeds, it can be concluded that the JWT
access token has not been tampered with.
Summary:
## Time
An access token can have up to three time-related attributes:
1. issuance time
2. validity start time
3. validity end time
Regardless of whether the access token information is obtained from an
introspection endpoint or from the payload of a JWT, these values are
referred to by the names `iat` (Issued At), `nbf` (Not Before), and `exp`
(Expiration Time), respectively.
| | Name | Introspection Response | JWT Payload |
| :- | :-: | :-: | :-: |
| issuance time | `iat` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.6][RFC_7519_4_1_6] |
| validity start time | `nbf` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.5][RFC_7519_4_1_5] |
| validity end time | `exp` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.4][RFC_7519_4_1_4] |
These attribute values determine the validity period of the access token.
The following diagram illustrates the validity period of an access token
when all three attributes—`iat`, `nbf`, and `exp`—are specified.
The resource server checks whether the current time falls within the validity
period of the access token. Specifically, it performs the following checks:
1. The issuance time is in the past (i.e., not after the current time).
2. The not-before time is in the past (i.e., not after the current time).
3. The expiration time is in the future (i.e., after the current time).
However, in real-world systems, these checks are performed with clock skew
in mind—i.e., the potential for time discrepancies between systems.
This is because, in practice, valid access tokens are often incorrectly
judged as invalid due to such time differences. For example, if the system
receiving the access token (the resource server) has a clock that lags behind
the system that issued the token (the authorization server), it may mistakenly
determine that the access token’s validity period has not yet started (i.e.,
the current time is earlier than the time specified by `nbf`).
You may hear opinions such as, *“That kind of issue shouldn’t occur if time
synchronization is done properly using NTP (Network Time Protocol).”*
However, in reality, such issues do occur. Insights gained from years of
operating open banking ecosystems in various countries are documented in the
[FAPI 2.0 Security Profile][FAPI2_SECURITY], which we will introduce here.
> NOTE 3: Clock skew is a cause of many interoperability issues. Even a few
> hundred milliseconds of clock skew can cause JWTs to be rejected for being
> “issued in the future”. The DPoP specification \[[RFC9449][RFC_9449]]
> suggests that JWTs are accepted in the reasonably near future (on the order
> of seconds or minutes). This document goes further by requiring authorization
> servers to accept JWTs that have timestamps up to 10 seconds in the future.
> 10 seconds was chosen as a value that does not affect security while greatly
> increasing interoperability. Implementers are free to accept JWTs with a
> timestamp of up to 60 seconds in the future. Some ecosystems have found that
> the value of 30 seconds is needed to fully eliminate clock skew issues. To
> prevent implementations switching off `iat` and `nbf` checks completely this
> document imposes a maximum timestamp in the future of 60 seconds.
Technically, when accounting for clock skew, there are two approaches:
tightening the validation (making tokens less likely to be considered valid),
and loosening the validation (making tokens more likely to be considered valid).
What is typically required in real-world system operations is the latter.
With this in mind, when comparing `iat` and `nbf` to the current time, the
maximum acceptable clock skew (e.g., 10 seconds) should be subtracted from
the `iat` or `nbf` value before comparison. Likewise, when comparing `exp`
to the current time, the clock skew should be added to the `exp` value
before comparison.
## Scope
One of the fundamental attributes of an access token defined in
[RFC 6749][RFC_6749] is the scope ([RFC 6749 Section 3.3][RFC_6749_3_3]).
The scope represents the set of permissions granted to the access token.
API sets may require that an access token possess specific scopes, depending
on their respective purposes. Which scopes are required is entirely up to the
API provider to define. Before proceeding with any processing, the API
implementation checks whether the presented access token includes the required
scopes.
Although the term "API" is used here, other terms such as "protected resource"
or "endpoint" are also used. There is no strict rule regarding which term to use.
A client application must first determine which scopes are required by the APIs
it intends to use, and then include those scopes as part of its request when
asking the authorization server to issue an access token. Specifically, for
example, when using the authorization code flow
([RFC 6749 Section 4.1][RFC_6749_4_1]) to request the issuance of an access
token, the client lists the individual scope names, separated by spaces, as the
value of the `scope` request parameter in the authorization request
([RFC 6749 Section 4.1.1][RFC_6749_4_1_1]).
Let’s look at a concrete example.
Suppose a client application wants to use the `GET /ssf/status` API and the
`POST /ssf/status` API provided by a certain server. Assume that the
`GET /ssf/status` API requires the `ssf:status:read`
scope, and the `POST /ssf/status` API requires the
`ssf:status:update` scope.
The client application sends an authorization request to the authorization
server, including those scope names in the value of the `scope` request
parameter.
After obtaining the user's consent, the authorization server issues an access
token to the client application with the requested scopes attached.
The client application accesses the API with the access token attached.
Summary:
## Audience Restriction
There is a method to restrict the audience of an access token.
By using this method, for example, it is possible to designate only a specific
resource server as the audience, or to specify only a group of endpoints under
a certain path hierarchy as the intended audience. This helps reduce the risk
of misuse or abuse of the access token.
The audience of an access token is represented by the `aud` parameter, both in
introspection responses ([RFC 7662][RFC_7662]) and in JWT access tokens.
| | Name | Introspection Response | JWT Payload |
| :- | :-: | :-: | :-: |
| audience | `aud` | [RFC 7662 Section 2.2][RFC_7662_2_2] | [RFC 7519 Section 4.1.3][RFC_7519_4_1_3] |
For example, if an introspection response includes `https://rs.example.com` in
the value of its `aud` property, it means that the access token can be used
only at `https://rs.example.com`.
```json theme={null}
{
"aud": [
"https://rs.example.com"
]
}
```
If that access token is presented to
`https://rs.example.org`, and if the APIs on
`https://rs.example.org` are correctly validating
the `aud` property of the access token, the token will be rejected.
Restricting the audience of an access token is done using the `resource`
request parameter defined in [Section 2][RFC_8707_2] of [RFC 8707][RFC_8707]
(Resource Indicators for OAuth 2.0).
When a `resource` request parameter is included in an authorization or token
request, its value is set as the audience of the access token. Below is an
example of how to use the `resource` parameter, excerpted from
[Section 2.1][RFC_8707_2_1] of [RFC 8707][RFC_8707]. In this example,
`https://cal.example.com` and
`https://contacts.example.com` are specified as
the intended audiences of the access token.
```text theme={null}
GET /as/authorization.oauth2?response_type=code
&client_id=s6BhdRkqt3
&state=tNwzQ87pC6llebpmac_IDeeq-mCR2wLDYljHUZUAWuI
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&scope=calendar%20contacts
&resource=https%3A%2F%2Fcal.example.com%2F
&resource=https%3A%2F%2Fcontacts.example.com%2F HTTP/1.1
Host: authorization-server.example.com
```
[Section 3.1](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1)
of [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) states
that *“Request and response parameters MUST NOT be included more than once.”*
However, the `resource` request parameter defined in
[RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html) is not
subject to this restriction.
[RFC 8707][RFC_8707] refers to access tokens with restricted recipients as
"**audience-restricted**" access tokens.
## Sender Constraint
Traditional access tokens, if leaked, can be used by unauthorized parties to
access protected APIs. This is similar to a situation in which a lost train
ticket can be picked up and used by someone else to board the train.
One way to mitigate this vulnerability is to verify, at the time of API access,
that the entity using the access token is the same as the entity to which the
token was originally issued. This is analogous to the check performed when
boarding an international flight, where passengers are required to present both
a ticket and a passport to ensure that the ticket holder is indeed the rightful
user.
The proof that shows the presenter is the legitimate holder of the access token
is referred to as **Proof of Possession** (**PoP**). By requiring client
applications to present a PoP along with the access token, resource servers can
ensure that merely possessing the token is not sufficient to gain access,
thereby preventing unauthorized use even if the token is stolen.
By requiring PoP, the sender of the access token is effectively constrained.
For this reason, access tokens that must be accompanied by PoP at the time of
use are referred to as "**sender-constrained**"
access tokens.
There are two methods for implementing sender-constrained access tokens:
MTLS ([RFC 8705 Section 3][RFC_8705_3]) and DPoP ([RFC 9449][RFC_9449]).
The following two sections describe these methods.
## MTLS
[RFC 8705][RFC_8705] [Section 3][RFC_8705_3] defines a method (hereafter
referred to as MTLS) for implementing PoP using an X.509 certificate
([RFC 5280][RFC_5280]) of the client application (hereafter referred to
as the client certificate).
For information on X.509 certificates, please refer to the explanation in
"[Illustrated
X.509 Certificate](https://darutk.medium.com/illustrated-x-509-certificate-84aece2c5c2e)".
### Mutual TLS Connection
When using MTLS, it is a prerequisite that the TLS connection between the
client application and the authorization server’s token endpoint, as well
as the TLS connection between the client application and the resource
server’s API, are mutual TLS connections. In addition, the client
application is required to present the same certificate in both of these
TLS connections.
A mutual TLS connection is a TLS connection in which both parties (the client
and the server) authenticate each other’s legitimacy. In a standard TLS
connection, the server presents its own certificate, and the client verifies
that certificate. In contrast, in a mutual TLS connection, the client also
presents its own certificate, and the server verifies the client’s certificate
as well.
By definition of the protocol, a client will not present a client certificate
during the establishment of a TLS connection unless requested by the server.
In other words, the client cannot initiate a mutual TLS connection on its own.
Therefore, to enable a mutual TLS connection, it must be configured on the
server side.
### Certificate Binding
The basic idea behind MTLS is to associate the access token with "the client
certificate used in the mutual TLS connection of the token request" and
remember it. Later, during a resource request, the server checks whether
"the client certificate used in the mutual TLS connection of the resource
request" is the same as the one associated with the access token included in
the request.
If they match, the client application making the resource request is considered
to be the same as the one that made the token request. If they do not match,
the client application making the resource request is deemed not to be the
legitimate holder of the access token, and the resource request is rejected.
Now, let’s walk through the procedure by which a sender-constrained access
token is achieved using MTLS.
The client application establishes a mutual TLS connection with the
authorization server’s token endpoint and sends a token request. Since it is
a mutual TLS connection, the client presents its client certificate to the
server.
In the token endpoint implementation, if the token request is valid, the
authorization server generates an access token and stores it in the database.
However, if the access token is in a format that embeds the necessary
information within itself (such as a JWT access token), it is also possible
to implement it without writing data to the database.
At this point, the authorization server extracts the client certificate from
the mutual TLS connection, calculates its hash value, and associates it with
the access token. Here, the hash value is the SHA-256 hash
([NIST FIPS 180-4][NIST_FIPS_180_4]) of the DER-encoded ([X.690][X_690])
client certificate.
An access token that is associated with a certificate in this way is called
a “**certificate-bound**” access token.
The token endpoint returns a token response containing the generated access
token to the client application.
After obtaining the access token, the client application establishes a mutual
TLS connection with the resource server’s API, using the same client
certificate that was used for the token request.
After the connection is established, the client application sends the resource
request along with the access token.
The API implementation extracts the access token from the request and sends it
to the authorization server’s introspection endpoint for obtaining information
about it. However, if the access token contains its own information, the
implementation reads the contents of the access token instead of querying the
introspection endpoint.
The introspection endpoint implementation retrieves the information of the
presented access token from the database, formats it into an introspection
response, and returns it to the resource server. At this time, the hash value
of the client certificate associated with the access token is base64url-encoded
([RFC 4648][RFC_4648]) and embedded in the introspection response as the value
of the `x5t#S256` sub-property within the top-level `cnf` property.
The `x5t#S256` property is defined in
[RFC 8705](https://www.rfc-editor.org/rfc/rfc8705.html),
while the `cnf` property is defined in
[RFC 7800](https://www.rfc-editor.org/rfc/rfc7800.html)
(Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)).
Next, the API implementation extracts the client certificate from the mutual
TLS connection with the client application and computes its hash value.
Then, it verifies whether the computed hash value matches the hash value
included in the introspection response. If they do not match, the client
application making the resource request is considered not to be the
legitimate holder of the access token, and the resource request is denied.
Summary:
### Client Certificate Forwarding
When deploying a TLS-protected application server, it is common to place a
reverse proxy between the client and the application server. The reverse proxy
handles the TLS connection with the client, terminates it there, and then
forwards the client’s requests to the application server sitting behind it.
In such a setup, the application server does not establish a TLS connection
directly with the client. Therefore, even if the client establishes a mutual
TLS connection with the reverse proxy, the application server cannot access
the client certificate presented during that connection. If the application
server’s logic needs to refer to the client certificate (for example, to
generate or validate a certificate-bound access token), the certificate must
somehow be forwarded from the reverse proxy to the application server.
For this purpose, a common practice has long been to add a custom HTTP
header—containing the client certificate as its value (for example,
`X-Ssl-Cert`)—to the forwarded request. [RFC 9440][RFC_9440] defines and
standardizes the use of `Client-Cert` as the name of this HTTP header.
[RFC 9440][RFC_9440] also standardized the format used when embedding a client
certificate as the value of an HTTP header. Previously, many implementations
used the PEM format ([RFC 7468][RFC_7468]), but there were variations—such as
whether line breaks were present, or whether `BEGIN` / `END` boundaries were
included—resulting in low interoperability. [RFC 9440][RFC_9440] specifies the
format as “a byte sequence ([RFC 8941 Section 3.3.5][RFC_8941_3_3_5])
representing the DER-encoded ([X.690][X_690]) client certificate.”
To briefly describe the format of a
[Byte Sequence](https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3.5)
defined in [RFC 8941](https://www.rfc-editor.org/rfc/rfc8941.html),
it is expressed as `:{base64}:`—the original data is
encoded in Base64 ([RFC 4648](https://www.rfc-editor.org/rfc/rfc4648.html))
and enclosed by colons (`:`) on both sides.
Summary:
## DPoP
[RFC 9449][RFC_9449], commonly referred to as DPoP, is one method for
implementing sender-constrained access tokens.
In DPoP, at the time of access token issuance, the public key from a key pair
prepared by the client application is bound to the access token. Then, when
the access token is used, the client is also required to present “proof” that
it possesses the private key corresponding to the public key bound to the
access token, thereby enforcing the sender constraint.
This proof is generated by signing data—into which the public key is
embedded—with the corresponding private key. In other words, it is a
self-signed token.
The recipient of the self-signed token extracts the embedded public key and
verifies the token’s signature using that public key. If the verification
succeeds, it can be concluded that the entity that generated the token
possesses the corresponding private key.
Let us now take a closer look at the detailed steps of DPoP.
### DPoP Binding
The client application first generates a key pair. The public key of this key
pair will be bound to the access token generated in the subsequent process.
Next, the client creates a PoP. In [RFC 9449][RFC_9449], this PoP is called a
**DPoP proof JWT**, and its structure is precisely defined. The key points are
as follows:
* The format is JWT.
* The `typ` header parameter value is `dpop+jwt`.
* The `jwk` header parameter contains the public key to be bound to the access token.
* The `htm` claim value is the HTTP method of the request to be sent.
* The `htu` claim value is the target URI of the request to be sent (excluding query and fragment parameters).
* The `jti` claim and `iat` claim are also required.
* The proof is signed with the private key that pairs with the public key specified in the `jwk` header parameter.
The client sends a token request to the authorization server’s token endpoint.
At this time, it includes the previously created DPoP proof JWT in the `DPoP`
header of the request.
The implementation of the authorization server’s token endpoint extracts the
DPoP proof JWT from the token request.
It retrieves the public key from the header of the DPoP proof JWT and verifies
the JWT’s signature using that public key.
It then generates an access token if the token request is valid and stores it
in the database. However, if the access token is self-contained (e.g., a JWT
access token), it is possible to implement the system without writing data to
the database.
At this point, the authorization server computes the JWK Thumbprint
([RFC 7638][RFC_7638]) of the public key with the hash function SHA-256
([NIST FIPS 180-4][NIST_FIPS_180_4]), and associates it with the access token.
An access token that is bound to the public key embedded in the DPoP proof JWT
in this way is called a “**DPoP-bound**”
access token.
The token endpoint returns a token response containing the generated access
token to the client application.
Before making a resource request, the client application creates a DPoP proof JWT.
When using a DPoP proof JWT together with an access token, the SHA-256 hash of
the access token, encoded in base64url ([RFC 4648][RFC_4648]), must be included
as the value of the `ath` claim in the JWT payload.
Once the DPoP proof JWT is prepared, the client makes the resource request.
The access token is included in the `Authorization` header, and the DPoP proof
JWT is included in the `DPoP` header. Note that the scheme part of the
`Authorization` header value should be `DPoP` (not `Bearer` as specified in
[RFC 6750][RFC_6750]).
The resource server receiving the resource request extracts the DPoP proof JWT
from the request and verifies its signature using the public key embedded in
the JWT header.
The resource server queries the authorization server's introspection endpoint
to obtain information about the access token. If the access token is DPoP-bound,
the introspection endpoint includes the JWK Thumbprint of the associated public
key in the introspection response, as the value of the `jkt` sub-property
within the `cnf` property ([RFC 7800][RFC_7800]).
The resource server calculates the JWK Thumbprint of the public key contained
in the DPoP proof JWT and verifies whether it matches the value in the
introspection response. If they do not match, the resource server determines
that the client application making the resource request is not the legitimate
holder of the access token and rejects the resource request.
Summary:
### DPoP Nonce
A server may require that a nonce value it provides be included in the DPoP
proof JWT. By setting a lifetime for the nonce value, the server can control
the validity period of the DPoP proof JWT ([RFC 9449 Section 8][RFC_9449_8]);
by making the nonce value unpredictable, the server can make it more difficult
to pre-generate a DPoP proof JWT and use it elsewhere
([RFC 9449 Section 11.2][RFC_9449_11_2]). Requiring a nonce has security
advantages like these.
If a DPoP proof JWT without a `nonce` claim is sent to a resource server that
requires a nonce, a `use_dpop_nonce` error will be returned with a `DPoP-Nonce`
header.
```text theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: DPoP error="use_dpop_nonce"
DPoP-Nonce: ZgmFr7UWLrJX0fHB
```
Upon receiving this error, the client application extracts the value from the
`DPoP-Nonce` header and creates a DPoP proof JWT that includes this value as
the `nonce` claim. It then resends the resource request with the newly created
DPoP proof JWT attached.
The client application continues to use the same nonce value in subsequent
resource requests. However, the nonce will eventually expire. Therefore, if
the same nonce is used continuously, the server will ultimately return an
`invalid_dpop_proof` error.
```text theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: DPoP error="invalid_dpop_proof"
DPoP-Nonce: HDoSFcMWFrfcWdr3
```
If the error response contains a `DPoP-Nonce` header whose value differs from
the nonce value previously used, it is possible that the error was caused by
the nonce being outdated. In this case, it is worth regenerating the DPoP proof
JWT and attempting to resend the resource request.
## Target URI
If a resource server supports DPoP, it must ensure that the value of the `htu`
claim in a received DPoP proof JWT matches the target URI of the request
(excluding the query and fragment parameters). To perform this check, the
resource server needs to know the absolute URI of the request.
Similarly, if the resource server supports HTTP Message Signatures
([RFC 9421][RFC_9421]), it needs the absolute URI of the request in order to
use it as the value of the `@target-uri` derived component
([RFC 9421 Section 2.2.2][RFC_9421_2_2_2]).
However, if the resource server is running behind a reverse proxy, it cannot
directly know the absolute URI of the request. This is because the HTTP request
visible to the resource server is not the one originally sent by the client
application, but rather the one forwarded by the reverse proxy. In most cases,
the scheme, host name, and port number differ from those of the original
request (the request that the client application sent to the reverse proxy).
Since the request received by the resource server differs from the one sent by
the client, comparing the target URI of the resource server’s received request
with the `htu` claim in the DPoP proof JWT will not match, causing all resource
requests containing DPoP proof JWTs to be rejected. Likewise, the value of the
`@target-uri` derived component as seen by the client application will not
match the `@target-uri` as recognized by the resource server, causing HTTP
Message Signature verification to fail and all resource requests to be rejected.
Therefore, verification of DPoP proof JWTs and HTTP Message Signatures at the
resource server must be based on the target URI of the request as received by
the reverse proxy, rather than the target URI of the request as received by the
resource server itself.
### Target URI Resolution
When a reverse proxy forwards an HTTP request, it is common practice to add
custom HTTP headers containing information about the original HTTP request.
For this purpose, the `Forwarded` HTTP header was defined by the
[RFC 7239 Forwarded HTTP Extension][RFC_7239]. For example, to convey that the
scheme of the original request was `https` and the host name was
`rs.example.com`, the reverse proxy would add a
`Forwarded` header like the following:
```text theme={null}
Forwarded: proto=https;host=rs.example.com
```
The `Forwarded` HTTP header standardized what had previously been achieved
using non-standard HTTP fields such as `X-Forwarded-For`, `X-Forwarded-By`,
and `X-Forwarded-Proto`. Therefore, the use of the `Forwarded` header is
recommended.
However, the syntax of the `Forwarded` header value is unexpectedly complex.
While it is not impossible, parsing it with regular expressions is difficult,
and since it does not conform to the general syntax defined in
[RFC 8941][RFC_8941], generic libraries cannot be used either. In the end,
you need to implement a dedicated parser specifically for the `Forwarded` HTTP
header (e.g., [http-field-parser][HTTP_FIELD_PARSER]).
Therefore, custom HTTP headers that are easier to process are still widely used.
Below are examples of such custom HTTP headers.
| Header Name | Reference |
| :- | :- |
| `Front-End-Https` | \[Microsoft] [Helping to Secure Communication: Client to Front-End Server][FRONT_END_HTTPS] |
| `X-Forwarded-For` | MDN Web Docs / [X-Forwarded-For][X_FORWARDED_FOR] |
| `X-Forwarded-Host` | MDN Web Docs / [X-Forwarded-Host][X_FORWARDED_HOST] |
| `X-Forwarded-Port` | \[AWS] HTTP headers and Classic Load Balancers # [X-Forwarded-Port][X_FORWARDED_PORT] |
| `X-Forwarded-Proto` | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO] |
| `X-Forwarded-Protocol` | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO] |
| `X-Forwarded-Ssl` | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO] |
| `X-Url-Scheme` | MDN Web Docs / [X-Forwarded-Proto][X_FORWARDED_PROTO] |
However, the standardized `Forwarded` HTTP header or widely used custom HTTP
headers are incomplete as a means of determining the target URI, since they
do not include information about the path or query components.
To use the `@target-uri` derived component in HTTP Message Signatures, query
component information is required. Unfortunately, as of now, there is no
standardized header or widely adopted custom header that conveys the absolute
URL including the path and query components.
It would be desirable to see the adoption of some kind of custom HTTP header
that represents the absolute URL (e.g., `X-Forwarded-URL`), the definition of
a standardized HTTP header (e.g., `Target-URI`), or the addition of a new
[HTTP Forwarded Parameter][IANA_HTTP_FORWARDED_PARAMETERS].
## HTTP Message Signature
It is common for HTTP messages to be delivered via multiple intermediary
servers. During this relay process, operations such as TLS termination or the
addition, consolidation, and modification of HTTP headers are often performed.
Under such conditions, methods to ensure the integrity and authenticity of HTTP
messages end-to-end between the sender and the recipient have long been studied.
In general, digital signatures are the means to achieve integrity and
authenticity. However, in the case of HTTP messages, since parts of the message
may be modified during transit, simply signing the entire HTTP message does not
work. Therefore, it is necessary to select only the portions meaningful to both
the sender and the recipient, normalize them so as not to be affected by
intermediary modifications, and then apply a signature to that subset.
For this purpose, numerous competing proposals emerged, some of which were
actually implemented and deployed. Ultimately, the specification adopted by the
IETF became [RFC 9421 HTTP Message Signatures][RFC_9421].
### HTTP Message Component
In RFC 9421, the portions of an HTTP message that can be part of the signature
input are referred to as HTTP message components.
The most straightforward HTTP message components are HTTP fields. For example,
the `Content-Type` HTTP field and the `Date` HTTP field are both HTTP message
components.
There are also HTTP message components derived from attributes other than HTTP
fields. These are called derived components. The HTTP method, target URI, and
HTTP status code are all examples of derived components.
The choice of which components to include in a signature is left up to the
application, depending on its particular purpose. For instance, the
[FAPI 2.0 HTTP Signatures][FAPI2_HTTP_SIGNATURES] specification requires that
the following components be included in the signature when signing an HTTP
request:
1. HTTP method
2. Target URI
3. `Authorization` HTTP field
4. `DPoP` HTTP field (if present)
5. `Content-Digest` HTTP field (if the message has a body)
### Signature Base
In HTTP message signatures, the string that serves as the input to the digital
signature is called the signature base. The signature base consists of a list
of identifier–value pairs of HTTP message components to be signed, followed by
metadata about the signature itself.
Before generating or verifying an HTTP message signature, the signature base
must first be constructed. For the precise syntax of the signature base, see
[RFC 9421][RFC_9421], [Section 2.5. Creating the Signature Base][RFC_9421_2_5].
Here is an example of a signature base excerpted from [RFC 9421][RFC_9421]:
```text theme={null}
"@method": POST
"@authority": example.com
"@path": /foo
"content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
"content-length": 18
"content-type": application/json
"@signature-params": ("@method" "@authority" "@path" "content-digest" "content-length" "content-type");created=1618884473;keyid="test-key-rsa-pss"
```
In this example of a signature base, lines 1 through 3 consist of
identifier–value pairs of derived components. The component names of derived
components begin with `@`, such as `@method`. Lines 4 through 6, on the other
hand, consist of HTTP fields and their values.
The final line of the signature base (line 7 in this example) contains metadata
about the signature. It always begins with `"@signature-params":`, followed by
an Inner List ([RFC 8941 Section 3.1.1][RFC_8941_3_1_1]). This Inner List
enumerates the component identifiers of the HTTP message components included in
the signature. In this example, the following six are listed:
* `"@method"`
* `"@authority"`
* `"@path"`
* `"content-digest"`
* `"content-length"`
* `"content-type"`
The portion that follows the list of component identifiers in parentheses,
beginning with a semicolon (`;`), represents the optional parameters of the
Inner List. In this example, the `created` parameter and the `keyid` parameter
are included. These parameters are defined in
[RFC 9421 Section 2.3][RFC_9421_2_3].
### Signing Method
The signing method abstractly represented as `HTTP_SIGN` in
[RFC 9421 Section 3.3][RFC_9421_3_3] takes the signature base (`M`) and the
signing key (`Ks`) as input and produces the signature (`S`) as output.
```
HTTP_SIGN (M, Ks) -> S
```
[RFC 9421 Section 3.3][RFC_9421_3_3] lists the signature algorithms, and the
[HTTP Signature Algorithms registry][IANA_HTTP_SIGNATURE_ALGORITHMS] maintained
by IANA contains the following set of algorithms.
| Algorithm Name | Description | Reference |
| :- | :- | :- |
| `rsa-pss-sha512` | RSASSA-PSS using SHA-512 | [RFC 9421, Section 3.3.1][RFC_9421_3_3_1] |
| `rsa-v1_5-sha256` | RSASSA-PKCS1-v1\_5 using SHA-256 | [RFC 9421, Section 3.3.2][RFC_9421_3_3_2] |
| `hmac-sha256` | HMAC using SHA-256 | [RFC 9421, Section 3.3.3][RFC_9421_3_3_3] |
| `ecdsa-p256-sha256` | ECDSA using curve P-256 DSS and SHA-256 | [RFC 9421, Section 3.3.4][RFC_9421_3_3_4] |
| `ecdsa-p384-sha384` | ECDSA using curve P-384 DSS and SHA-384 | [RFC 9421, Section 3.3.5][RFC_9421_3_3_5] |
| `ed25519` | EdDSA using curve edwards25519 | [RFC 9421, Section 3.3.6][RFC_9421_3_3_6] |
Regarding the set of JWS algorithms familiar in the context of OAuth 2.0 and
OpenID Connect, they are explicitly mentioned in
[RFC 9421 Section 3.3.7. JSON Web Signature (JWS) Algorithms][RFC_9421_3_3_7].
When these algorithms are used, it is specified that the signature base must
be used as the "JWS Signing Input" ([RFC 7515][RFC_7515]).
Since HTTP message signatures do not have anything equivalent to a JWS header,
the signature algorithm must be conveyed to the verifier by some other means.
For this purpose, it seems appropriate to use the `alg` parameter defined in
[RFC 9421 Section 2.3][RFC_9421_2_3]. However, because the final paragraph of
[RFC 9421 Section 3.3.7][RFC_9421_3_3_7] explicitly states otherwise, the `alg`
parameter is not used.
> JSON Web Algorithm (JWA) values from the "JSON Web Signature and Encryption
> Algorithms" registry are not included as signature parameters. Typically, the
> JWS algorithm can be signaled using JSON Web Keys (JWKs) or other mechanisms
> common to JOSE implementations. In fact, JWA values are not registered in the
> "HTTP Signature Algorithms" registry (Section 6.2), and so the explicit `alg`
> signature parameter is not used at all when using JOSE signing algorithms.
### Verification Method
The verification method abstractly represented as `HTTP_VERIFY` in
[RFC 9421 Section 3.3][RFC_9421_3_3] takes the signature base (`M`), the
verification key (`Kv`), and the signature (`S`) as inputs, and produces the
verification result (`V`) as output.
```
HTTP_VERIFY (M, Kv, S) -> V
```
Among the inputs to the verification process, the verification key is not
included in the HTTP message and therefore must be obtained by some means.
However, [RFC 9421][RFC_9421] does not define how it should be obtained.
When a resource server attempts to verify an HTTP message signature on an HTTP
request, at first glance it may appear possible to obtain the verification key
through the following steps:
1. Extract the access token contained in the HTTP request
2. Identify the client application associated with the access token
3. Retrieve the client application’s `jwks_uri` metadata information
4. Obtain the JWK Set from the location indicated by the `jwks_uri`
5. Based on the value of the `keyid` signature parameter, identify the
verification key within the JWK Set
However, upon closer examination, it becomes clear that in the context of a
resource server, the standard specifications alone do not provide a way to
obtain the value of the client application’s `jwks_uri` metadata. Excluding
individual proposed specifications, the only standard specification that could
be used is [OpenID Federation][OIDFED] ([explanatory article][OIDFED_ARTICLE]).
However, implementing and operating this specification is a heavy undertaking,
and it cannot be said that it is widely deployed; therefore, it would not be
a general-purpose solution for obtaining verification keys.
As a result, at present, in order for a resource server to verify the HTTP
message signature of an HTTP request, it must obtain the verification key using
non-standard, implementation-specific methods.
It should be noted that, if limited to the context of FAPI 2.0, there is a
method to determine the verification key using only standard specifications
Details will be provided later.
### Signature Inclusion
HTTP message signatures are added to HTTP messages using the `Signature` HTTP
field and the `Signature-Input` HTTP field.
The value format of the `Signature` HTTP field is a Dictionary
([RFC 8941 Section 3.2][RFC_8941_3_2]). Each key–value pair consists of an
arbitrary label and a signature represented as a Byte Sequence
([RFC 8941 Section 3.3.5][RFC_8941_3_3_5]).
```text theme={null}
Signature: label=:base64-encoded-signature:, label=:base64-encoded-signature:, ...
```
The value format of the `Signature-Input` HTTP field is also a Dictionary
([RFC 8941 Section 3.2][RFC_8941_3_2]). Each key–value pair consists of an
arbitrary label and signature metadata represented as an Inner List
([RFC 8941 Section 3.1.1][RFC_8941_3_1_1]).
```text theme={null}
Signature-Input: label=(component identifiers)optional parameters, label=(component identifiers)optional parameters, ...
```
Labels can be assigned arbitrarily; however, any label present in the
`Signature` HTTP field MUST also be present in the `Signature-Input`
HTTP field.
The following example, excerpted from [RFC 9421 Section 4.3][RFC_9421_4_3],
shows an HTTP request containing both the `Signature` and `Signature-Input`
HTTP fields.
```text theme={null}
POST /foo?param=Value&Pet=dog HTTP/1.1
Host: example.com
Date: Tue, 20 Apr 2021 02:07:55 GMT
Content-Type: application/json
Content-Length: 18
Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "content-type" "content-length");created=1618884475;keyid="test-key-ecc-p256"
Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp+kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:
{"hello": "world"}
```
## FAPI 2.0 Http Signatures
As [RFC 9421][RFC_9421] itself states, the specification is designed as a tool,
and therefore additional requirements need to be defined according to the
purposes of the application or profile when it is actually used.
[RFC 9421 Section 1.4][RFC_9421_1_4] provides examples of such requirements.
For instance, the following items are listed:
* The set of components to be signed
* The set of signature metadata parameters
* The method of obtaining signing and verification keys
* The signature algorithm
[FAPI 2.0 HTTP Signatures][FAPI2_HTTP_SIGNATURES], one of the FAPI 2.0
specification family, profiles [RFC 9421][RFC_9421] in order to apply HTTP
message signatures to resource requests to the resource server and to resource
responses from the resource server, and defines concrete requirements.
A summary of the specification is as follows. Note that, as the specification
is still under development, it may be subject to change in the future.
**Request Signing**
| | Item | Condition |
| :- | :- | :- |
| Components | `"@method"` | |
| | `"@target-uri"` | |
| | `"authorization"` | |
| | `"dpop"` | If DPoP is used. |
| | `"content-digest"` | If the request has a message body. |
| Metadata Parameters | `created` | |
| | `tag` | The value is fixed to `"fapi-2-request"`. |
**Response Signing**
| | Item | Condition |
| :- | :- | :- |
| Components | `"@method";req` | |
| | `"@target-uri";req` | |
| | `"authorization";req` | |
| | `"dpop";req` | If DPoP is used. |
| | `"content-digest";req` | If the request has a message body. |
| | `"@status"` | |
| | `"content-digest"` | If the response has a message body. |
| Metadata Parameters | `created` | |
| | `tag` | The value is fixed to `"fapi-2-response"`. |
The `Content-Digest` HTTP field is defined in
[RFC 9530 Digest Fields](https://www.rfc-editor.org/rfc/rfc9530.html),
[Section 2.
The `Content-Digest` Field](https://www.rfc-editor.org/rfc/rfc9530.html#section-2).
### Verification Key for Request
When using the [FAPI 2.0 HTTP Signatures][FAPI2_HTTP_SIGNATURES] specification,
the [FAPI 2.0 Security Profile][FAPI2_SECURITY] specification also applies,
and therefore sender-constrained access tokens using either MTLS or DPoP are
employed for resource requests.
In both the MTLS and DPoP cases, the client application’s public key is
conveyed to the resource server. With this technical property in mind, by
introducing the convention that “the HTTP request is signed with the private
key paired with the public key bound to the sender-constrained access token,”
the verification key retrieval problem mentioned in the
"[Verification Method](#http-message-signature-verification-method)" can be
solved.
The advantage of this solution is that the verification key can be obtained
solely through approved standard specifications ([RFC 8705][RFC_8705] and
[RFC 9449][RFC_9449]). This avoids reliance on non-standard,
implementation-specific methods, thereby improving interoperability.
Please note that “the HTTP request is signed with the private key paired with
the public key bound to the sender-constrained access token” is not part of
the [FAPI
2.0 HTTP Signatures](https://openid.bitbucket.io/fapi/fapi-2_0-http-signatures.html) specification, but merely one possible approach. The
specification explicitly states: *"This specification doesn't specify the
exact means by which a resource server can retrieve the key for the client."*
### Verification Key for Response
In order for a client application to verify the HTTP message signature of a
resource response, it must obtain the verification key provided by the resource
server.
Compared to the client application providing the verification key, the
provision of the verification key by the resource server can be realized in a
straightforward manner. Just as authorization servers and OpenID providers do,
the resource server can publish a JWK Set document ([RFC 7517][RFC_7517]) and
include within it the verification key used for verifying HTTP message
signatures.
The location of the JWK Set document can be indicated by the `jwks_uri`
property within the resource server metadata ([RFC 9728][RFC_9728]) published
at `/.well-known/oauth-protected-resource`.
When providing verification keys via a JWK Set document, it is advisable to
assign the same key identifier to both the key (JWK) and the HTTP message
signature, so that the verification key can be identified among multiple keys.
The key can be associated with a key identifier using the `kid` property
([RFC 7517 Section 4.5][RFC_7517_4_5]), and the HTTP message signature can be
associated with it using the `keyid` parameter
([RFC 9421 Section 2.3][RFC_9421_2_3]).
## RAR
The scope ([RFC 6749 Section 3.3][RFC_6749_3_3]) of an access token represents
the coarse-grained permissions associated with that access token.
In order to express more fine-grained permissions, some authorization server
implementations introduced a mechanism in which scope names are given structure,
with parts of the scope name treated as variables. Such mechanisms are referred
to as "parameterized scopes" or "dynamic scopes". Of course, there is no
interoperability across implementations.
That said, packing information into scope names is, frankly, a bad practice.
To improve this situation,
[RFC 9396 OAuth 2.0 Rich Authorization Requests][RFC_9396] (commonly known as
RAR) was developed. The RAR specification defines the
`authorization_details` JSON array as a general mechanism for
representing permissions in a structured way.
Each element of the `authorization_details` JSON array is a
JSON object (hereafter referred to as a RAR object). The only mandatory
property that a RAR object must have is `type`. This `type` property indicates
the structure of the RAR object.
[RFC 9396][RFC_9396] itself does not define values for the `type` property.
The values to be used are determined independently by applications or profiles.
For example, the [OpenID for Verifiable Credential Issuance][OID4VCI] (OID4VCI)
specification defines the value `openid_credential`. Below is
an example of an `authorization_details` array excerpted from
that specification.
```json theme={null}
[
{
"type": "openid_credential",
"credential_configuration_id": "UniversityDegreeCredential"
}
]
```
The RAR specification defines, in addition to the `type` property, the
following properties. Whether or not to use them is optional:
* `locations`
* `actions`
* `datatypes`
* `identifier`
* `privileges`
As illustrated in the usage example from the OID4VCI specification mentioned
earlier, applications and profiles may also add their own custom properties to
RAR objects. Below is an example of an `authorization_details`
array excerpted from [RFC 9396][RFC_9396], which makes use of top-level
properties such as `geolocation` and `currency` that are not defined in
[RFC 9396][RFC_9396].
```json theme={null}
[
{
"type": "photo-api",
"actions": [
"read",
"write"
],
"locations": [
"https://server.example.net/",
"https://resource.local/other"
],
"datatypes": [
"metadata",
"images"
],
"geolocation": [
{
"lat": -32.364,
"lng": 153.207
},
{
"lat": -35.364,
"lng": 158.207
}
]
},
{
"type": "financial-transaction",
"actions": [
"withdraw"
],
"identifier": "account-14-32-32-3",
"currency": "USD"
}
]
```
According to the RAR specification, the `authorization_details`
parameter can be used anywhere the `scope` parameter is available (provided the
implementation supports it). For resource servers, the most important
occurrences of the `authorization_details` parameter are in
introspection responses and in the payload of JWT access tokens.
The following is an excerpt from
[RFC 9396 Section 9.2. Token Introspection][RFC_9396_9_2], showing an example
of an introspection response.
```json theme={null}
{
"active": true,
"sub": "24400320",
"aud": "s6BhdRkqt3",
"exp": 1311281970,
"acr": "psd2_sca",
"txn": "8b4729cc-32e4-4370-8cf0-5796154d1296",
"authorization_details": [
{
"type": "https://scheme.example.com/payment_initiation",
"actions": [
"initiate",
"status",
"cancel"
],
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorName": "Merchant123",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
}
],
"debtorAccount": {
"iban": "DE40100100103307118608",
"user_role": "owner"
}
}
```
## Cedar
[Cedar][CEDAR] is a language for writing authorization policies
([Cedar Policy Language Reference Guide][CEDAR_REFERENCE]).
Authorization policies written in Cedar (hereafter, Cedar policies) can be used
to determine whether to allow access to a resource.
One major advantage of Cedar is that it enables you to separate authorization
logic from business logic. Cedar policies can be externalized and managed
independently from business logic. In fact, there are commercial services
available for managing Cedar policies (see [Integrations][CEDAR_INTEGRATIONS]).
Another major advantage of Cedar is that it allows authorization logic to be
expressed with fine granularity. For example, you can write an authorization
rule such as *“If someone is Jane’s friend, they are allowed to view and
comment on photos in Jane’s trip album.”* Below is an example of a Cedar
policy that represents this authorization logic, excerpted from the
[Example scenario][CEDAR_EXAMPLE_SCENARIO] in the
[Cedar Policy Language Reference Guide][CEDAR_REFERENCE].
```text theme={null}
permit (
principal in Group::"janeFriends",
action in [Action::"view", Action::"comment"],
resource in Album::"janeTrips"
);
```
Cedar policies can include `when` and `unless` clauses to specify the
conditions under which a policy applies. Within these conditions, you can use
comparison operators, logical operators, arithmetic operators, and more, giving
Cedar a high level of expressiveness.
```text theme={null}
permit (
principal,
action == Action::"read",
resource
)
when {
resource.owner == principal ||
resource.tag == "public"
};
```
### Cedar in RAR Object
By associating scopes with an access token
([RFC 6749 Section 3.3][RFC_6749_3_3]), it is possible to represent the
coarse-grained permissions of that access token. Similarly, if we could
associate Cedar policies with an access token, wouldn’t it be possible to
represent the fine-grained permissions of that token?
One promising approach to realizing this idea is to represent Cedar policies
as RAR objects.
When determining the details of how to represent them, the following
considerations arise:
* The original syntax of Cedar policies is unique. Should we embed the policy
in its original syntax within the RAR object, or should we convert it to
JSON format ([JSON Policy Format][CEDAR_JSON_FORMAT]) before embedding?
While the JSON format is highly compatible with RAR, it tends to be verbose
(particularly in expressing `when` / `unless` conditions), and most members
of the Cedar community are likely to prefer the original syntax.
* Should both the original Cedar syntax and the JSON format be supported, or
should we limit support to one of them?
* If both formats are supported, how should the format in use be indicated?
Should the information be embedded in the value of the `type` property, or
should a separate property (e.g., `format`) be introduced?
* Should a policy set be represented as a single RAR object, or should each
RAR object represent only a single policy? In the JSON representation of a
policy set shown in
["Representing a policy set with JSON"][CEDAR_POLICY_SET_FORMAT], a single
JSON object contains `staticPolicies`, `templates`, and `templateLinks`.
Should we follow this approach? And in the first place, is support for
`templates` necessary in the RAR context?
* Might there be cases where something other than Cedar policies (e.g., Cedar
schemas) would need to be represented? If so, how should the value of `type`
be determined in anticipation of that possibility?
The following is an example of the "Cedar in RAR Object" specification.
**Properties**
| Property | Requirement | Description |
| :- | :- | :- |
| `type` | REQUIRED | The value is fixed to `"cedar-policy"`. |
| `format` | OPTIONAL | `"cedar"` (default) or `"json"`. |
| `policy_id` | OPTIONAL | Policy ID. JSON string. |
| `policy` | REQUIRED | Cedar Policy. If the value of the `format` property is `"cedar"`, this contains a single JSON string representing a Cedar policy written in the original Cedar syntax. If the value of the `format` property is `"json"`, this contains a JSON object representing a single Cedar policy written in the [JSON Policy Format](https://docs.cedarpolicy.com/policies/json-format.html). |
**Entities**
| Entity | Description |
| :- | :- |
| `principal` | If a subject is associated with the access token (i.e., if the introspection response or the JWT access token payload contains a `sub` claim), then the principal entity is `User`; otherwise, it is `Client`.
For a `User` entity, the value of the access token's `sub` claim is treated as its unique identifier. For a `Client` entity, the value of the access token's `client_id` is treated as its unique identifier.
The elements of the access token's `groups`, `roles`, and `entitlements` claims ([RFC 9068 Section 2.2.3](https://www.rfc-editor.org/rfc/rfc9068.html#section-2.2.3)) are mapped to `Group`, `Role`, and `Entitlement` entities, respectively, and are set as parent entities of the `principal`. If they follow [RFC 7643 Section 2.4](https://www.rfc-editor.org/rfc/rfc7643.html#section-2.4), each element is a JSON object that includes a `value` property (e.g., `"groups":[{"value":"group1"}]`). The value of this `value` property is treated as the unique identifier of the entity. If they do not follow [RFC 7643 Section 2.4](https://www.rfc-editor.org/rfc/rfc7643.html#section-2.4), they are assumed to be JSON strings (e.g., `"groups":["group1"]`), and the string value itself is treated as the unique identifier of the entity. |
| `action` | `action` entities are defined by the resource server. |
| `resource` | `resource` entities are defined by the resource server. |
**Context**
| Property | Description |
| :- | :- |
| `time` | The current time expressed as the number of milliseconds elapsed since the Unix epoch. |
| `access_token` | Information about the access token. It has the sub-properties `iss`, `sub`, and `client_id`. |
| `ip_address` | Client's IP address. |
The following is an example of an `authorization_details`
array written based on this specification. The first RAR object describes
a Cedar policy using the original Cedar syntax, while the second RAR object
describes a Cedar policy using the [JSON Policy Format][CEDAR_JSON_FORMAT].
```json theme={null}
[
{
"type": "cedar-policy",
"format": "cedar",
"policy_id": "policy-0",
"policy": "permit (principal == User::\"12UA45\", action == Action::\"view\", resource);"
},
{
"type": "cedar-policy",
"format": "json",
"policy_id": "policy-1",
"policy": {
"effect": "permit",
"principal": { "op": "==", "entity": { "type": "User", "id": "12UA45" } },
"action": { "op": "==", "entity": { "type": "Action", "id": "view" } },
"resource": { "op": "All" }
}
}
]
```
Please note that this proposed specification has not been agreed upon within
the industry. However, at Authlete we have confirmed that this specification
is implementable and works as intended.
In our upcoming product, Authlete Arena, for the
[Shared Signals Framework](https://openid.net/specs/openid-sharedsignals-framework-1_0.html), we will support "Cedar in RAR Object" in
addition to advanced security specifications such as MTLS, DPoP, and FAPI
2.0 Http Signatures.
## Step Up Authentication
Some of the APIs provided by a resource server may impose stricter security
requirements than others. For example, it is common for a funds transfer API
to require higher security than a balance inquiry API.
Among such security requirements, one possible requirement is that the user
must undergo stronger authentication than usual when obtaining the access
token.
If the user authentication performed when the presented access token was
obtained does not meet the required criteria, the API would likely want to
notify the client application of this fact and prompt it to re-obtain the
access token. In such cases, the
[RFC 9470 OAuth 2.0 Step Up Authentication Challenge Protocol][RFC_9470]
can be used.
The API can notify that the user authentication at the time of token
issuance did not meet the requirements by using the error code
`insufficient_user_authentication`. The error response
can also include information about the requirements by using parameters such
as `acr_values` and `max_age`.
Below is an example error response excerpted from [RFC 9470][RFC_9470].
```text theme={null}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="insufficient_user_authentication",
error_description="A different authentication level is required.",
acr_values="myACR"
```
For more details on [RFC 9470][RFC_9470], please refer to
“[RFC 9470 OAuth 2.0 Step Up Authentication Challenge Protocol][STEPUP_AUTHN_ARTICLE].”
## Authorization API
At the [OIDF][OIDF], the [AuthZEN Working Group][OIDF_AUTHZ_WG] is currently
developing the [Authorization API 1.0][AUTHZEN] specification.
At the core of this specification is the Access Evaluation API. When you
provide this API with the necessary information to determine whether access
should be granted, it returns the evaluation result as a boolean value.
Below is an example request and response for this API.
```json theme={null}
{
"subject": {
"type": "user",
"id": "alice@acmecorp.com"
},
"resource": {
"type": "account",
"id": "123"
},
"action": {
"name": "can_read",
"properties": {
"method": "GET"
}
},
"context": {
"time": "1985-10-26T01:22-07:00"
}
}
```
```json theme={null}
{
"decision": true
}
```
The input parameters are the subject, resource, action, and context, while
the output parameter is the evaluation result. Although these input and output
components seem correct, my intuition is that the Access Evaluation API might
only be implementable by a server that centrally manages all the input
elements—handling both user management and resource management. It will be
interesting to see what kinds of implementations emerge in the future.
## Conclusion
At the time of the publication of [RFC 6749][RFC_6749] in 2012, which is the
core specification of OAuth 2.0, the only mechanism available for API access
control was scopes. Since then, various additional features have been
introduced.
Among these, sender-constrained access tokens using MTLS or DPoP are
particularly important, and FAPI 2.0 requires the implementation of either one.
In April 2022, an incident occurred in which access tokens stolen from
Heroku / Travis-CI were abused elsewhere (GitHub), highlighting the importance
of sender-constraining mechanisms.
Authlete has contributed to the development of advanced security specifications
such as [RFC 9449][RFC_9449] (DPoP), [FAPI 2.0 Security Profile][FAPI2_SECURITY],
and [FAPI 2.0 Http Signatures][FAPI2_HTTP_SIGNATURES] (its authorship is listed
in the specifications). Authlete also provides commercial implementations of
these specifications.
For those seeking expertise in API protection or commercial implementations,
please [contact us][CONTACT].
[IETF]: https://www.ietf.org/
[IETF_HTTPBIS_WG]: https://datatracker.ietf.org/wg/httpbis/about/
[IETF_OAUTH_WG]: https://datatracker.ietf.org/wg/oauth/about/
[IETF_SECEVENT_WG]: https://datatracker.ietf.org/wg/secevent/about/
[RFC_4648]: https://www.rfc-editor.org/rfc/rfc4648.html
[RFC_5280]: https://www.rfc-editor.org/rfc/rfc5280.html
[RFC_6749]: https://www.rfc-editor.org/rfc/rfc6749.html
[RFC_6749_3_3]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.3
[RFC_6749_4_1]: https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1
[RFC_6749_4_1_1]: https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1
[RFC_6750]: https://www.rfc-editor.org/rfc/rfc6750.html
[RFC_6750_2_1]: https://www.rfc-editor.org/rfc/rfc6750.html#section-2.1
[RFC_6750_2_2]: https://www.rfc-editor.org/rfc/rfc6750.html#section-2.2
[RFC_6750_2_3]: https://www.rfc-editor.org/rfc/rfc6750.html#section-2.3
[RFC_6755]: https://www.rfc-editor.org/rfc/rfc6755.html
[RFC_6819]: https://www.rfc-editor.org/rfc/rfc6819.html
[RFC_7009]: https://www.rfc-editor.org/rfc/rfc7009.html
[RFC_7239]: https://www.rfc-editor.org/rfc/rfc7239.html
[RFC_7468]: https://www.rfc-editor.org/rfc/rfc7468.html
[RFC_7515]: https://www.rfc-editor.org/rfc/rfc7515.html
[RFC_7515_4_1_1]: https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.1
[RFC_7515_4_1_4]: https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.4
[RFC_7517]: https://www.rfc-editor.org/rfc/rfc7517.html
[RFC_7517_4_2]: https://www.rfc-editor.org/rfc/rfc7517.html#section-4.2
[RFC_7517_4_4]: https://www.rfc-editor.org/rfc/rfc7517.html#section-4.4
[RFC_7517_4_5]: https://www.rfc-editor.org/rfc/rfc7517.html#section-4.5
[RFC_7517_5]: https://www.rfc-editor.org/rfc/rfc7517.html#section-5
[RFC_7519]: https://www.rfc-editor.org/rfc/rfc7519.html
[RFC_7519_4_1_1]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1
[RFC_7519_4_1_3]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3
[RFC_7519_4_1_4]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4
[RFC_7519_4_1_5]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5
[RFC_7519_4_1_6]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6
[RFC_7521]: https://www.rfc-editor.org/rfc/rfc7521.html
[RFC_7522]: https://www.rfc-editor.org/rfc/rfc7522.html
[RFC_7523]: https://www.rfc-editor.org/rfc/rfc7523.html
[RFC_7591]: https://www.rfc-editor.org/rfc/rfc7591.html
[RFC_7592]: https://www.rfc-editor.org/rfc/rfc7592.html
[RFC_7636]: https://www.rfc-editor.org/rfc/rfc7636.html
[RFC_7638]: https://www.rfc-editor.org/rfc/rfc7638.html
[RFC_7662]: https://www.rfc-editor.org/rfc/rfc7662.html
[RFC_7662_2_2]: https://www.rfc-editor.org/rfc/rfc7662.html#section-2.2
[RFC_7800]: https://www.rfc-editor.org/rfc/rfc7800.html
[RFC_8176]: https://www.rfc-editor.org/rfc/rfc8176.html
[RFC_8252]: https://www.rfc-editor.org/rfc/rfc8252.html
[RFC_8414]: https://www.rfc-editor.org/rfc/rfc8414.html
[RFC_8417]: https://www.rfc-editor.org/rfc/rfc8417.html
[RFC_8628]: https://www.rfc-editor.org/rfc/rfc8628.html
[RFC_8693]: https://www.rfc-editor.org/rfc/rfc8693.html
[RFC_8705]: https://www.rfc-editor.org/rfc/rfc8705.html
[RFC_8705_3]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3
[RFC_8707]: https://www.rfc-editor.org/rfc/rfc8707.html
[RFC_8707_2]: https://www.rfc-editor.org/rfc/rfc8707.html#section-2
[RFC_8707_2_1]: https://www.rfc-editor.org/rfc/rfc8707.html#section-2.1
[RFC_8725]: https://www.rfc-editor.org/rfc/rfc8725.html
[RFC_8935]: https://www.rfc-editor.org/rfc/rfc8935.html
[RFC_8936]: https://www.rfc-editor.org/rfc/rfc8936.html
[RFC_8941]: https://www.rfc-editor.org/rfc/rfc8941.html
[RFC_8941_3_2]: https://www.rfc-editor.org/rfc/rfc8941.html#section-3.2
[RFC_8941_3_1_1]: https://www.rfc-editor.org/rfc/rfc8941.html#section-3.1.1
[RFC_8941_3_3_5]: https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3.5
[RFC_9068]: https://www.rfc-editor.org/rfc/rfc9068.html
[RFC_9101]: https://www.rfc-editor.org/rfc/rfc9101.html
[RFC_9126]: https://www.rfc-editor.org/rfc/rfc9126.html
[RFC_9207]: https://www.rfc-editor.org/rfc/rfc9207.html
[RFC_9278]: https://www.rfc-editor.org/rfc/rfc9278.html
[RFC_9396]: https://www.rfc-editor.org/rfc/rfc9396.html
[RFC_9396_9_2]: https://www.rfc-editor.org/rfc/rfc9396.html#section-9.2
[RFC_9421]: https://www.rfc-editor.org/rfc/rfc9421.html
[RFC_9421_1_4]: https://www.rfc-editor.org/rfc/rfc9421.html#section-1.4
[RFC_9421_2_2_2]: https://www.rfc-editor.org/rfc/rfc9421.html#section-2.2.2
[RFC_9421_2_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-2.3
[RFC_9421_2_5]: https://www.rfc-editor.org/rfc/rfc9421.html#section-2.5
[RFC_9421_3_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3
[RFC_9421_3_3_1]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.1
[RFC_9421_3_3_2]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.2
[RFC_9421_3_3_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.3
[RFC_9421_3_3_4]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.4
[RFC_9421_3_3_5]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.5
[RFC_9421_3_3_6]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.6
[RFC_9421_3_3_7]: https://www.rfc-editor.org/rfc/rfc9421.html#section-3.3.7
[RFC_9421_4_3]: https://www.rfc-editor.org/rfc/rfc9421.html#section-4.3
[RFC_9440]: https://www.rfc-editor.org/rfc/rfc9440.html
[RFC_9449]: https://www.rfc-editor.org/rfc/rfc9449.html
[RFC_9449_8]: https://www.rfc-editor.org/rfc/rfc9449.html#section-8
[RFC_9449_11_2]: https://www.rfc-editor.org/rfc/rfc9449.html#section-11.2
[RFC_9470]: https://www.rfc-editor.org/rfc/rfc9470.html
[RFC_9493]: https://www.rfc-editor.org/rfc/rfc9493.html
[RFC_9530]: https://www.rfc-editor.org/rfc/rfc9530.html
[RFC_9700]: https://www.rfc-editor.org/rfc/rfc9700.html
[RFC_9701]: https://www.rfc-editor.org/rfc/rfc9701.html
[RFC_9728]: https://www.rfc-editor.org/rfc/rfc9728.html
[OIDF]: https://openid.net/
[OIDF_ABC_WG]: https://openid.net/wg/connect/
[OIDF_AUTHZ_WG]: https://openid.net/wg/authzen/
[OIDF_DCP_WG]: https://openid.net/wg/digital-credentials-protocols/
[OIDF_EAP_WG]: https://openid.net/wg/eap/
[OIDF_IDA_WG]: https://openid.net/wg/ekyc-ida/
[OIDF_FAPI_WG]: https://openid.net/wg/fapi/
[OIDF_FASTFED_WG]: https://openid.net/wg/fastfed/
[OIDF_HEART_WG]: https://openid.net/wg/heart/
[OIDF_IGOV_WG]: https://openid.net/wg/igov/
[OIDF_MODRNA_WG]: https://openid.net/wg/modrna/
[OIDF_SS_WG]: https://openid.net/wg/sharedsignals/
[ACCOUNT_PORTING]: http://openid.net/specs/openid-connect-account-porting-1_0.html
[AUTHZEN]: https://openid.github.io/authzen/
[CAEP]: https://openid.github.io/sharedsignals/openid-caep-1_0.html
[CIBA]: https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html
[FAPI_CIBA]: https://openid.net/specs/openid-financial-api-ciba.html
[FAPI1_ADVANCED]: https://openid.net/specs/openid-financial-api-part-2-1_0-final.html
[FAPI1_BASELINE]: https://openid.net/specs/openid-financial-api-part-1-1_0-final.html
[FAPI2_ATTACKER]: https://openid.net/specs/fapi-attacker-model-2_0-final.html
[FAPI2_HTTP_SIGNATURES]: https://openid.bitbucket.io/fapi/fapi-2_0-http-signatures.html
[FAPI2_MESSAGE_SIGNING]: https://openid.bitbucket.io/fapi/fapi-2_0-message-signing.html
[FAPI2_SECURITY]: https://openid.net/specs/fapi-security-profile-2_0-final.html
[FASTFED_CORE_ID1]: https://openid.net/specs/fastfed-core-1_0-ID1.html
[FASTFED_SAML_ID1]: https://openid.net/specs/fastfed-saml-1_0-ID1.html
[FASTFED_SCIM_ID1]: https://openid.net/specs/fastfed-scim-1_0-ID1.html
[FORM_POST]: https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
[GRANT_MANAGEMENT]: https://openid.bitbucket.io/fapi/oauth-v2-grant-management.html
[HEART_FHIR_OAUTH]: https://openid.net/specs/openid-heart-fhir-oauth2-1_0.html
[HEART_FHIR_UMA]: https://openid.net/specs/openid-heart-fhir-uma2-1_0.html
[HEART_OAUTH]: https://openid.net/specs/openid-heart-oauth2-1_0.html
[HEART_UMA]: https://openid.net/specs/openid-heart-uma2-1_0.html
[IGOV_OAUTH]: https://openid.net/specs/openid-igov-oauth2-1_0.html
[IGOV_OIDC]: https://openid.net/specs/openid-igov-openid-connect-1_0.html
[IGOV_USE_CASES]: https://openid.net/specs/openid-igov-use-cases-1_0.html
[JARM]: https://openid.net/specs/oauth-v2-jarm-final.html
[EAP_ACR_VALUES]: https://openid.net/specs/openid-connect-eap-acr-values-1_0.html
[HAIP_WG_DRAFT]: https://openid.github.io/oid4vc-haip/openid4vc-high-assurance-interoperability-profile-wg-draft.html
[MOBILE_REGISTRATION]: https://openid.net/wordpress-content/uploads/2014/04/draft-mobile-registration-01.html
[MODRNA_AUTH]: http://openid.net/specs/openid-connect-modrna-authentication-1_0.html
[MODRNA_DISCOVERY]: https://openid.net/specs/openid-connect-modrna-discovery-1_0.html
[MULTI_RES_TYPES]: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
[NATIVESSO]: https://openid.net/specs/openid-connect-native-sso-1_0.html
[OID4VCI]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
[OID4VCI_ID1]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html
[OID4VP]: https://openid.net/specs/openid-4-verifiable-presentations-1_0-final.html
[OIDC_ASC]: https://openid.bitbucket.io/ekyc/openid-connect-advanced-syntax-for-claims.html
[OIDC_AUTHORITY]: https://openid.bitbucket.io/ekyc/openid-authority.html
[OIDC_BACK_LOGOUT]: https://openid.net/specs/openid-connect-backchannel-1_0.html
[OIDC_CLAIMS]: https://openid.net/specs/openid-connect-claims-aggregation-1_0.html
[OIDC_CORE]: https://openid.net/specs/openid-connect-core-1_0.html
[OIDC_DCR]: https://openid.net/specs/openid-connect-registration-1_0.html
[OIDC_DISCOVERY]: https://openid.net/specs/openid-connect-discovery-1_0.html
[OIDC_ENTERPRISE]: https://openid.net/specs/openid-connect-enterprise-extensions-1_0.html
[OIDC_EPHEMERAL]: https://openid.net/specs/openid-connect-ephemeral-subject-identifier-1_0.html
[OIDC_FRONT_LOGOUT]: https://openid.net/specs/openid-connect-frontchannel-1_0.html
[OIDC_MIGRATION]: https://openid.net/specs/openid-connect-migration-1_0.html
[OIDC_RP_LOGOUT]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
[OIDC_SESSION]: https://openid.net/specs/openid-connect-session-1_0.html
[OIDC4IDA]: https://openid.net/specs/openid-connect-4-identity-assurance-1_0-final.html
[OIDC4IDA_CLAIMS]: https://openid.net/specs/openid-connect-4-ida-claims-1_0-final.html
[OIDFED]: https://openid.net/specs/openid-federation-1_0.html
[OIDFED_LISTING]: https://openid.net/specs/openid-federation-extended-listing-1_0.html
[OIDFED_WALLET]: https://openid.net/specs/openid-federation-wallet-1_0.html
[OPENID_ATTACHMENTS]: https://openid.net/specs/openid-connect-4-ida-attachments-1_0-final.html
[OPENID_IDA_SCHEMA]: https://openid.net/specs/openid-ida-verified-claims-1_0-final.html
[PROMPT_CREATE]: https://openid.net/specs/openid-connect-prompt-create-1_0.html
[PROVIDER_COMMANDS]: https://openid.net/specs/openid-provider-commands-1_0.html
[RISC]: https://openid.net/specs/openid-risc-profile-specification-1_0.html
[RP_META_CHOICES]: https://openid.net/specs/openid-connect-rp-metadata-choices-1_0.html
[SIOPv2]: https://openid.net/specs/openid-connect-self-issued-v2-1_0.html
[SSF]: https://openid.github.io/sharedsignals/openid-sharedsignals-framework-1_0.html
[UNMET_AUTH_REQ]: https://openid.net/specs/openid-connect-unmet-authentication-requirements-1_0.html
[USER_QUESTIONING]: http://openid.net/specs/openid-connect-user-questioning-api-1_0.html
[IANA_HTTP_FORWARDED_PARAMETERS]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#forwarded
[IANA_HTTP_SIGNATURE_ALGORITHMS]: https://www.iana.org/assignments/http-message-signature/http-message-signature.xhtml#signature-algorithms
[NIST_FIPS_180_4]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
[X_690]: https://www.itu.int/rec/T-REC-X.690/
[CEDAR]: https://www.cedarpolicy.com/
[CEDAR_EXAMPLE_SCENARIO]: https://docs.cedarpolicy.com/overview/scenario.html
[CEDAR_INTEGRATIONS]: https://www.cedarpolicy.com/en/integrations
[CEDAR_JSON_FORMAT]: https://docs.cedarpolicy.com/policies/json-format.html
[CEDAR_POLICY_SET_FORMAT]: https://docs.cedarpolicy.com/policies/json-format.html#policy-set-format
[CEDAR_REFERENCE]: https://docs.cedarpolicy.com/
[HTTP_FIELD_PARSER]: https://github.com/authlete/http-field-parser
[FRONT_END_HTTPS]: https://learn.microsoft.com/en-us/previous-versions/tn-archive/aa997519\(v=exchg.65\)
[X_FORWARDED_FOR]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For
[X_FORWARDED_HOST]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host
[X_FORWARDED_PORT]: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html#x-forwarded-port
[X_FORWARDED_PROTO]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto
[OIDFED_ARTICLE]: ../oidcfed/
[STEPUP_AUTHN_ARTICLE]: ../stepup_authn/
[CONTACT]: ../../contact/
# Configuring FAPI 1.0
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/fapi-1-0-configuration
The service and client settings the FAPI 1.0 Baseline and Advanced profiles require, and where their request requirements are documented.
## Overview
This page lists the settings a service and a client need for the FAPI 1.0 profiles — **Baseline** ([Part 1](https://openid.net/specs/openid-financial-api-part-1-1_0-final.html)) and **Advanced** ([Part 2](https://openid.net/specs/openid-financial-api-part-2-1_0.html)).
Configuring them is not on its own enough: Authlete also has to be told which profile applies to a request. See [How to use FAPI feature](/protocols-and-flows/compliance-profiles/how-to-use-fapi) for that, and [Financial-grade API (FAPI) Basics](/protocols-and-flows/compliance-profiles/fapi-basics) for a tutorial that walks the whole setup end to end.
## Service configurations
This section explains how to configure a service to support each FAPI 1.0 profile.
### Configurations for Baseline profile
Configure a service as below.
| Property | Configuration |
| - | - |
| FAPI Profile | **Enable** it. |
| Supported Client Authentication Methods | Choose at least one of the followings: TLS\_CLIENT\_AUTH, SELF\_SIGNED\_TLS\_CLIENT\_AUTH, CLIENT\_SECRET\_JWT, PRIVATE\_KEY\_JWT. Note: If all the FAPI clients communicating with this service are public clients, this configuration is not mandatory. |
| Supported Scopes | Create at least a scope that has an Baseline attribute. |
### Configurations for Advanced API profile
To support Advanced API profile, you need to configure a service according to the configurations for Baseline API profile and the following additional configurations. (Some configurations for Baseline API profile are overridden.)
| Property | Configuration |
| - | - |
| Supported Authentication Context Class References | Set an appropriate value.(e.g. urn:mace:incommon:iap:silver) |
| Supported Response Types | Choose at least one of the followings: CODE\_ID\_TOKEN, CODE\_ID\_TOKEN\_TOKEN. Note: If all the FAPI clients communicating with this service use JARM (See Appendix 2), this configuration is not mandatory. |
| Supported Client Authentication Methods | Choose at least one of the followings: TLS\_CLIENT\_AUTH, SELF\_SIGNED\_TLS\_CLIENT\_AUTH, PRIVATE\_KEY\_JWT. Note: If all the FAPI clients communicating with this service are public clients, this configuration is not mandatory. |
| TLS Client Certificate Bound Access Tokens | Choose **Supported**. |
| Supported Scopes | Create at least a scope that has an Advanced attribute. |
## Client configurations
This section explains how to configure a client to support FAPI profiles.
### Configurations for Baseline profile
Configure a client as below.
| Property | Configuration |
| - | - |
| Client Authentication Method | If this client is a confidential client, choose one of the followings: TLS\_CLIENT\_AUTH, SELF\_SIGNED\_TLS\_CLIENT\_AUTH, CLIENT\_SECRET\_JWT, PRIVATE\_KEY\_JWT. |
| Redirect Uris | Set a URI starting with https. |
| JWK Set Content | Note: this configuration is not mandatory if "JWK Set URI" is used. If this client uses PRIVATE\_KEY\_JWT as its client authentication method, the value of this property must contain a public key for verifying assertion signature and the key must satisfy the requirements in Appendix 3. |
| JWK Set URI | Note: this configuration is not mandatory if "JWK Set Content" is used. If this client uses PRIVATE\_KEY\_JWT as its client authentication method, the value of the JWK set pointed by this property must contain a public key for verifying assertion signature and the key must satisfy the requirements in Appendix 3. |
### Configurations for Advanced profile
To support Advanced profile, you need to configure a client according to the configurations for Baseline API profile and the following additional configurations. (Some configurations for Baseline API profile are overridden.)
| Property | Configuration |
| - | - |
| Response Type | Choose at least one of the followings: CODE\_ID\_TOKEN, CODE\_ID\_TOKEN\_TOKEN. Note: If this client uses JARM (See Appendix 2), this configuration is not mandatory. |
| Supported Client Authentication Methods | If this client is a confidential client, choose one of the followings: TLS\_CLIENT\_AUTH, SELF\_SIGNED\_TLS\_CLIENT\_AUTH, PRIVATE\_KEY\_JWT. |
| TLS Client Certificate Bound Access Tokens | Choose **Enabled**. |
| Authorization Response Signature Algorithm | If this client uses JARM, choose one of the followings: PS256, ES256. |
| Assertion Signature Algorithm | If this client uses PRIVATE\_KEY\_JWT as its client authentication method, choose one of the followings: PS256, ES256. |
| Request Object Signature Algorithm | Choose one of the followings: PS256, ES256. |
| ID Token Signature Algorithm | If this client asks the authorization server for ID tokens, choose one of the followings: PS256, ES256. |
| User Info Signature Algorithm | If this client uses user info endpoint, choose one of the followings: PS256, ES256. |
## Request requirements
Once a service and a client are configured, Authlete validates each request against the profile in force. Those checks — what the authorization request, the token request and the request object must carry, for Baseline and for Advanced — are documented as 23 numbered items in [Validation in FAPI mode](/protocols-and-flows/compliance-profiles/validation-in-fapi-mode), including:
| Topic | Items |
| - | - |
| Request object: presence, signature, the parameters it must carry, its `exp` and `aud` claims, and its signing algorithm | 7–12 |
| `code_challenge` and `code_challenge_method` (PKCE) | 13, 14 |
| `acr` as an essential claim | 15 |
| Client authentication and the signing algorithm of the client assertion | 18, 19 |
| Key size of the client assertion (2048 bits for RSA, 160 for EC) | 20 |
| Sender-constrained access tokens | 22 |
## Request examples
The requirements above describe what each request has to satisfy. These examples show what they look like on the wire, as the client sends them to the authorization server.
### Authorization request, Baseline profile
```text theme={null}
#
# An example request from a public client.
#
# Note:
# * scope: 'account' scope has a Baseline attribute.
# * state: Optional but recommended (OAuth 2.0).
# * nonce: Required if the 'response_type' request parameter contains
# 'id_token' (OpenID Connect).
#
GET /api/authorization?
response_type=code+id_token&
client_id=285946231596&
redirect_uri=https://my-client.com/callback&
scope=openid+accounts&
code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
code_challenge_method=S256&
state=mystate&
nonce=mynonce
```
### Authorization request, Advanced profile
The `claims` parameter has to carry `acr` as an essential claim:
```json theme={null}
{
"id_token": {
"acr": {
"essential": true,
"values": ["urn:mace:incommon:iap:silver"]
}
}
}
```
```text theme={null}
#
# An example request from a confidential client.
#
# Note:
# * scope: 'account' scope has an Advanced attribute.
# * state: Optional but recommended (OAuth 2.0).
# * nonce: Required if the 'response_type' request parameter contains
# 'id_token' (OpenID Connect).
#
GET /api/authorization?
response_type=code+id_token&
client_id=291985138172&
scope=openid+payments&
redirect_uri=https://my-client.com/callback&
state=mystate&
nonce=mynonce&
claims={"id_token":{"acr":{"essential":true,"values":["urn:mace:incommon:iap:silver"]}}}&
request=eyJh...[omitted]...nPQ
```
The `request` parameter above is a signed request object. Its payload has to carry every request parameter, an `exp` claim, and an `aud` claim whose value is the service's issuer identifier:
```json theme={null}
{
"response_type": "code id_token",
"exp": 1554973000,
"aud": "https://my-authz-server.com/",
"client_id": "291985138172",
"scope": "openid payments",
"redirect_uri": "https://my-client.com/callback",
"state": "mystate",
"nonce": "mynonce",
"claims": {
"id_token": {
"acr": {
"values": ["urn:mace:incommon:iap:silver"],
"essential": true
}
}
}
}
```
### Token request, Baseline profile
```text theme={null}
#
# An example request from a public client.
#
POST /api/token
client_id=285946231596&
grant_type=authorization_code&
code=_vaXlQ_ItUX4hiWzXgOT-Jp9-oVPKGQ6Q6QZu_P2GXw&
redirect_uri=https://my-client.com/callback&
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
```
### Token request, Advanced profile
The client also has to present a client certificate to the token endpoint, because **TLS Client Certificate Bound Access Tokens** is enabled for it.
```text theme={null}
#
# An example request from a confidential client.
#
# Note:
# * The client uses PRIVATE_KEY_JWT as its client authentication method.
# * The client needs to present a client certificate to the token endpoint,
# as 'TLS Client Certificate Bound Access Tokens' is enabled for the client.
#
POST /api/token
client_id=291985138172&
grant_type=authorization_code&
code=YG-gD9v-vmnuKaHkRHcvWq1UxlxT_9vgj28ffxIAX40&
redirect_uri=https://my-client.com/callback&
client_assertion=eyJh...[omitted]...OWg&
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
```
## Related settings
| Topic | See |
| - | - |
| Scope attributes | [Scope attributes](/configuration-reference/tokens-and-claims/scope-attributes) |
| JARM | [Enabling JARM](/configuration-reference/endpoints/enabling-jarm) |
# FAPI 2.0
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/fapi-2-0
Explanation about FAPI 2.0 and how Authlete supports the specification.
This page is for **Authlete 3.x**. For **Authlete 2.x** documentation, see [Financial-grade API (FAPI) Basics](/v2/protocols-and-flows/compliance-profiles/fapi).
## Overview
FAPI 2.0 is a set of specifications designed to enhance the security and interoperability of APIs, primarily in the financial services and banking sectors. It represents a significant evolution from FAPI 1.0, introducing advanced security measures and refined standards to meet the growing challenges across a range of industries.
## FAPI 2.0 Security Profile
[FAPI 2.0 Security Profile (Final)](https://openid.net/specs/fapi-security-profile-2_0-final.html) is an API security profile that builds upon OAuth 2.0, with a focus on providing robust security measures for APIs across various industries. For guidance on supporting FAPI 2.0 Security Profile with Authlete, see the following article:
* [Supporting FAPI 2.0 Security Profile with Authlete](/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-security-profile-with-authlete)
## FAPI 2.0 Message Signing Profile
[FAPI 2.0 Message Signing Profile](https://openid.bitbucket.io/fapi/fapi-2_0-message-signing.html) (hereinafter referred to as FAPI2 MS) is a subset of FAPI 2.0 that extends FAPI 2.0 Security Profile as its foundation and deals with message signing. FAPI2 MS Profile primarily defines four categories of requirements:
1. [Signing Authorization Requests](https://openid.net/specs/fapi-message-signing-2_0-final.html#name-signing-authorization-reque)
2. [Signing Authorization Responses](https://openid.net/specs/fapi-message-signing-2_0-final.html#name-signing-authorization-respo)
3. [Signing Introspection Responses](https://openid.net/specs/fapi-message-signing-2_0-final.html#name-signing-introspection-respo)
4. [Signing ID Tokens](https://openid.net/specs/fapi-message-signing-2_0-final.html#name-signing-id-tokens)
For more details, please refer to the following articles:
* [Supporting FAPI2 Message Signing Profile: Signing Authorization Requests with Authlete](/protocols-and-flows/compliance-profiles/fapi-2-0-message-signing-profile-signing-authorization-requests)
* [Supporting FAPI2 Message Signing Profile: Signing Authorization Responses with Authlete](/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-message-signing-profile-signing-authorization-responses)
# Supporting FAPI 2.0 Message Signing Profile: Signing Authorization Requests with Authlete
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/fapi-2-0-message-signing-profile-signing-authorization-requests
Supporting FAPI 2.0 Message Signing Profile: Signing Authorization Requests with Authlete.
This page is for **Authlete 3.x**. For 2.x documentation, see [this page](/v2/protocols-and-flows/compliance-profiles/fapi-2-0-message-signing-profile-signing-authorization-requests).
## Overview
This article explains how to support [FAPI 2.0 Message Signing Profile: Signing Authorization Requests](https://openid.net/specs/fapi-message-signing-2_0-final.html#section-5.3) (hereinafter referred to as FAPI2 authorization request signing) and in Authlete.
"FAPI 2.0 authorization request signing" is available since Authlete 2.3.
"FAPI2 authorization request signing" requires satisfying all requirements of FAPI 2.0 Security Profile.
## Preconditions
* This document uses Authorization Code Flow compliant with FAPI2 authorization request signing as an example.
* `private_key_jwt` is used for client authentication.
* DPoP is used for sender-constrained access tokens.
## Service Settings
Configure your service as follows.
### ☑️ 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];
3. shall require the request object to contain an nbf claim that is no longer than 60 minutes in the past;
### ☑️ 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 > Token > Assertion Audience Mode
Enable this setting.
> "FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements"
>
>
8. shall only accept its issuer identifier value (as defined in \[RFC8414]) as a string in the aud claim received in client authentication assertions;
### ☑️ 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;
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 > ID Token
Set an allowable clock skew value that meets the following condition:
```
10 seconds ≤ clock skew ≤ 60 seconds
```
> "FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements"
>
>
13. to accommodate clock offsets, shall accept JWTs with an iat or nbf timestamp between 0 and 10 seconds in the future but shall reject JWTs with an iat or nbf timestamp greater than 60 seconds in the future. See Note 3 for further details and rationale;
>
> ...
>
> NOTE 3: Clock skew is a cause of many interoperability issues. Even a few hundred milliseconds of clock skew can cause JWTs to be rejected for being “issued in the future”. The DPoP specification \[RFC9449] suggests that JWTs are accepted in the reasonably near future (on the order of seconds or minutes). This document goes further by requiring authorization servers to accept JWTs that have timestamps up to 10 seconds in the future. 10 seconds was chosen as a value that does not affect security while greatly increasing interoperability. Implementers are free to accept JWTs with a timestamp of up to 60 seconds in the future. Some ecosystems have found that the value of 30 seconds is needed to fully eliminate clock skew issues. To prevent implementations switching off iat and nbf checks completely this document imposes a maximum timestamp in the future of 60 seconds.
### ☑️ Tokens and Claims > Advanced > Supported Scopes
Create a scope named `myscope` and add a scope attribute with the key `fapi2` and the value `ms-authreq`.
## Client Configurations
Configure your client as follows.
### ☑️ 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];
### ☑️ Endpoints > Token > 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
>
> * adhere to \[RFC8725];
> * use `PS256`, `ES256`, or `EdDSA` (using the `Ed25519` variant) algorithms; and
> * 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"
>
>
2. RSA keys shall have a minimum length of 2048 bits.
>
>
3. Elliptic curve keys shall have a minimum length of 224 bits.
## Demonstration
This section demonstrates Authorization Code Flow in FAPI2 authorization request signing by using Authlete APIs.
### 1. Pushed Authorization Request & Response
Suppose that a client that implements FAPI2 authorization request signing 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).
### ☑️ Request Object
The request object in the request (, which is the value of the `request` parameter,) must be a **signed JWT**.
> "FAPI2 Message Signing profile, 5.3. Signing authorization requests, 5.3.1. Requirements for Authorization Servers"
>
> Authorization servers implementing FAPI2 authorization request signing
>
>
1. shall support, require use of, and verify signed request objects according to JAR \[RFC9101] at the PAR endpoint \[RFC9126];
1. shall send all authorization parameters to the PAR endpoint \[RFC9126] in a JAR \[RFC9101] signed requested object;
Claims in the request object must meet the following requirements:
#### `iss` claim
The `iss` claim must be the client ID.
> "RFC 9126, 3. The "request" Request Parameter"
>
> The authorization server MUST take the following steps beyond the processing rules defined in Section 2.1:
>
> ...
>
>
3. If the client has authentication credentials established with the authorization server, reject the request if the authenticated client\_id does not match the client\_id claim in the Request Object. Additionally, requiring the iss claim to match the client\_id is at the discretion of the authorization server.
#### `response_type` claim
The `response_type` claim must be set to `code`.
#### `scope` claim
The `scope` claim must be set to `myscope`, which is linked to an attribute `fapi2=ms-authreq` as configured above.
#### `code_challenge` claim & `code_challenge_method` claim
The `code_challenge` claim must be set to an appropriate value and the `code_challenge_method` claim 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` claim
The `redirect_uri` claim must be set and it must be a URI starting with `https`. In this demonstration, 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];
#### `aud` claim
The `aud` claim must be the authorization server's Issuer Identifier URL or an array containing it.
> "FAPI2 Message Signing profile, 5.3. Signing authorization requests, 5.3.1. Requirements for Authorization Servers"
>
> Authorization servers implementing FAPI2 authorization request signing
>
>
2. shall require the aud claim in the request object to be, or to be an array containing, the authorization server's issuer identifier URL;
2. shall send the aud claim in the request object as the authorization server's issuer identifier URL;
#### `nbf` claim & `exp` claim
The `nbf` claim must be included and must be no longer than 60 minutes in the past.
> "FAPI2 Message Signing profile, 5.3. Signing authorization requests, 5.3.1. Requirements for Authorization Servers"
>
> Authorization servers implementing FAPI2 authorization request signing
>
>
3. shall require the request object to contain an nbf claim that is no longer than 60 minutes in the past;
4. shall send an exp claim in the request object that has a lifetime of no longer than 60 minutes;
Taking all of the requirements above into account, a 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
request=eyJhbGci...&
client_id={Client ID}&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
client_assertion=eyJhbGci...
```
The payload of the request object is as follows.
```json theme={null}
{
"jti": "kcvE4Tn",
"iss": "{Client ID}",
"aud": "https://server.example.com",
"response_type": "code",
"code_challenge_method": "S256",
"code_challenge": "pr2TmSd_...",
"scope": "myscope",
"redirect_uri": "https://client.example.com/cb",
"nbf": 1701760807,
"exp": 1701760867,
"nonce": "aOLpUqqjMINQeclZ"
}
```
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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/pushed_auth_req \
-H 'Authorization: Bearer {Authlete API Access Token}' \
-H 'Content-type: application/json' \
-d '{"parameters":"request=eyJhbGci...&client_id={Client ID}&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGci..."}'
```
A successful response from the API contains a request URI. The response would look like below.
```json theme={null}
{
"resultCode": "A245001",
"resultMessage": "[A245001] Successfully registered a request object for client ({Client ID}), URI is urn:ietf:params:oauth:request_uri:H7XaM5FK2-RGcSRilYwe9FosTkk9XLMDZPEBA5uKsf8.",
"action": "CREATED",
"requestUri": "urn:ietf:params:oauth:request_uri:H7XaM5FK2-RGcSRilYwe9FosTkk9XLMDZPEBA5uKsf8",
"responseContent": "{\"expires_in\":300,\"request_uri\":\"urn:ietf:params:oauth:request_uri:H7XaM5FK2-RGcSRilYwe9FosTkk9XLMDZPEBA5uKsf8\"}"
}
```
### 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.
```bash theme={null}
GET /authorization?client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:H7XaM5FK2-RGcSRilYwe9FosTkk9XLMDZPEBA5uKsf8 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: Bearer {Authlete API Access Token}' \
-H 'Content-type: application/json' \
-d '{"parameters":"client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:H7XaM5FK2-RGcSRilYwe9FosTkk9XLMDZPEBA5uKsf8"}'
```
A successful response from the API would be like below.
```json theme={null}
{
"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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization/issue \
-H 'Authorization: Bearer {Authlete API Access Token}' \
-H 'Content-type: application/json' \
-d '{"ticket":"CBKnPeMO...","subject":"john","result":"AUTHORIZED"}'
```
A successful response from the API will include an authorization code, as shown below.
```json theme={null}
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"authorizationCode": "smseP17u...",
...
}
```
### 3. 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
>
> * adhere to \[RFC8725];
> * use `PS256`, `ES256`, or `EdDSA` (using the `Ed25519` variant) algorithms; and
> * 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/token \
-H 'Authorization: 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 will include an access token, as shown below.
```json theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "7i9xPkbk...",
...
}
```
### 4. 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/introspection \
-H 'Authorization: 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.
```json theme={null}
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
...
}
```
# Financial-grade API (FAPI) Basics
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/fapi-basics
A tutorial to configure Authlete to build a Financial-grade API (FAPI) compliant authorization server.
# Preface
This document describes overview of security provisions defined in [Financial-grade API Security Profile 1.0 - Part 2: Advanced](https://openid.net/specs/openid-financial-api-part-2-1_0.html) (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.
* [OIDC Basics](/get-started/quickstarts/oidc-basics)
# Components
In this tutorial, we assume the following components.
Note that only [Authlete's console](https://console.authlete.com) 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).
**Note**
In this tutorial, OIDC identity provider and relying party are described as authorization server and client respectively for convenience.
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 |
# Interaction between API client and API server using FAPI
In this document, you will configure Authlete to enable the following token granting process and API access flows in a FAPI-compliant manner.
### 1. Authorization request
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.
Using [Pushed Authorization Requests (PAR)](/configuration-reference/endpoints/pushed-authorization-requests-par) is recommended for passing the object by reference.
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`.
You need to configure [JARM (JWT Secured Authorization Response Mode for OAuth 2.0)](/configuration-reference/endpoints/enabling-jarm) when using `response_type=code`.
Other settings, including `claims`, are to be explained later.
### 2. Authorization response
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.
### 3. Token request
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.
### 4. Token response
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.
### 5. API request
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.
# Initial setup
## Create a Service in Authlete Management Console
1. Log in to the **Authlete Management Console** at [https://console.authlete.com/](https://console.authlete.com).
2. Go to your descired Organization. Click the **Create New Service** button. This will open the service creation page.
3. Fill in the following fields:
* **Service Name**: Provide an arbitrary name for your service, e.g., `FAPI Service`.
* **Service Description**: (Optional) Add a friendly description for your service.
4. In the **FAPI Profile** section (optional), toggle to `Enable`:
5. 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**.
1. In the **FAPI** section, enable the **FAPI Profile** option.
2. If you want to statically apply FAPI compliance, you can select the appropriate compliance level here. For this tutorial, we will dynamically apply compliance based on request scope, so leave this section unchecked.
3. Click **Save Changes** to apply the configuration.

## Adding a Scope and Scope Attribute to Enforce FAPI
To add a scope with FAPI enforcement:
1. Navigate to **Service Settings > Tokens and Claims > Advanced > Scope**.
2. In the **Supported Scopes** section, click the **Add** button to create a new scope.
3. Fill in the scope details:
* **Scope Name**: Enter the name of the scope, e.g., `payment`.
* **Description**: Provide a description, such as: `This payment scope requires FAPI compliant security`.
4. Leave **Set as Default Scope** disabled unless you want this scope to be applied by default for all request.
5. Under **Scope Attributes**, add a key-value pair to enforce FAPI:
* **Key**: `fapi`
* **Value**: `rw`
6. Click **Add** to add the scope.
7. Click **Save** to save the scope.

## Adding a New Client
In the Authlete Management Console, locate and click on your **FAPI Service**, then click the **Create Client** button.
1. Enter the following details:
* **Client Name**: Enter `FAPI Client`.
* **Client ID**: Leave this blank to allow Authlete to generate the `client_id`.
* **Client Description**: (Optional) Add a friendly description for the client.
* **Client Type**: Select **CONFIDENTIAL**.
2. Click the **Create** button to create the client.
3. 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.

### Example Configuration
| **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) |
## Testing the initial configuration
Let's check how Authlete works with the initial configuration. We will use [`/auth/authorization` API](/api-reference/authorization-endpoint/process-authorization-request) for that purpose.
### Authorization request for non-FAPI scope
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](/api-reference/authorization-endpoint/process-authorization-request). Here's a curl version of the request.
* [Linux/Mac](#non-fapi-scope-bash)
* [Windows](#non-fapi-scope-powershell)
Make sure to replace `{SERVICE ACCESS TOKEN}` and `{Client ID}` with your own values generated in the previous step.
```shell theme={null}
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 backticks (\`) to break lines.
```shell theme={null}
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).
```JSON theme={null}
{
"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.
### Authorization request for FAPI scope (Part 1)
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](/api-reference/authorization-endpoint/process-authorization-request). Here's a curl version of the request.
* [Linux/Mac](#fapi-scope-bash)
* [Windows](#fapi-scope-powershell)
```shell theme={null}
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
```
```shell theme={null}
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).
```JSON theme={null}
{
"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](https://openid.net/specs/openid-financial-api-part-2-1_0.html#rfc.section.5.2.2) states as follows.
> * shall require
> 1. the `response_type` value `code id_token`, or
> 2. the `response_type` value `code` in conjunction with the `response_mode` value `jwt`;
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.
### Authorization request for FAPI scope (Part 2)
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](/api-reference/authorization-endpoint/process-authorization-request) as follows.
* [Linux/Mac](#fapi-idtoken-bash)
* [Windows](#fapi-idtoken-powershell)
```shell theme={null}
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
```
```shell theme={null}
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.
```JSON theme={null}
{
"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](https://openid.net/specs/openid-financial-api-part-2-1_0.html#rfc.section.5.2.2) states as follows.
> * shall only use the parameters included in the signed request object passed via the `request` or `request_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.
# Making a FAPI-compliant authorization request
In this section, we will create an authorization request using a a request object.
## Request object settings
### Configuring a signing key used for 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](https://mkjwk.org/) 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"`)
```JSON theme={null}
{
"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`)
```JSON theme={null}
{
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "1",
"x": "icP8p_AigyTzwSpLRyv_bBQTSGu_NG7pMVXd-RAxwYE",
"y": "06tC0MJeBlZNYlnY8g4bCA9wJ34XN-rWfWlmmlhf-F0",
"alg": "ES256"
}
]
}
```
### Additional Client Settings
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:
#### 1. JWK Set
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.


#### 2. Request Signing Algorithm
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.
### Creating 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`
```JSON theme={null}
{
"redirect_uri":"https://client.example.org/cb/example.com",
"response_type":"code id_token",
"client_id":" e.g. 591205987816490",
"scope":"openid payment",
"nbf": e.g. 1613373232,
"exp": e.g. 1613376832,
"aud":"https://as.example.com",
"nonce":"n-0S6_WzA2Mj"
}
```
The following example shows how to generate values of `nbf` and `exp`.
1. `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`.
````text theme={null}
"nbf":1613373232,
```, 2. `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.
```text
"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](https://mkjose.org/) 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](https://smallstep.com/cli/) is as follows.
```Shell theme={null}
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.
```Text theme={null}
eyJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.ewoicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5vcmcvY2IvZXhhbXBsZS5jb20iLAoicmVzcG9uc2VfdHlwZSI6ImNvZGUgaWRfdG9rZW4iLAoiY2xpZW50X2lkIjoiNTkxMjA1OTg3ODE2NDkwIiwKInNjb3BlIjoib3BlbmlkIHBheW1lbnQiLAoiZXhwIjoxNTU0OTczMDAwMCwKImF1ZCI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLAoibm9uY2UiOiJuLTBTNl9XekEyTWoiCn0K.q_MbfV5qN-gnB93JaQGVrEXu8WvhDuUzWx6DwC50J8AiQGjXDEpw9satUAMN18rrgnGNciiFztoEFJuJjrJoyA
```
### Testing the configuration
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](/api-reference/authorization-endpoint/process-authorization-request). Here's a curl version of the request.
* [Linux/Mac](#request-object-bash)
* [Windows](#request-object-powershell)
```shell theme={null}
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="}' | jq
```
```shell theme={null}
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=\"}'
```
Authlete will make a response (folded for readability) like this.
```JSON theme={null}
{
"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](/api-reference/authorization-endpoint/issue-authorization-response) 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](/api-reference/authorization-endpoint/issue-authorization-response) with the `ticket` and the `subject`. Here's a curl version of the request.
* [Linux/Mac](#authz-issue-bash)
* [Windows](#authz-issue-powershell)
```shell theme={null}
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
```
```shell theme={null}
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).
```JSON theme={null}
{
"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](https://openid.net/specs/openid-financial-api-part-2-1_0.html#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.
# Making a FAPI-compliant authorization response
## Changing ID token signing algorithm
### Adding a signing key for ID token
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.
**Note**
The key set being generated in this section is for an authorization server to sign ID tokens,
while the other key set generated and registered in the previous section "Configuring a signing key of a request object" is to verify a signature in a request object from a client to an authorization server. Please make sure those two key sets are different.
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.

### Testing the configuration (ES256 for ID token)
Let's check if Authlete works as expected. Make the same request again to [`/auth/authorization` API](/api-reference/authorization-endpoint/process-authorization-request) to get a `ticket` at first.
* [Linux/Mac](#request-object-with-acr-bash)
* [Windows](#request-object-with-acr-powershell)
```shell theme={null}
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="}' | jq | grep ticket
```
```shell theme={null}
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=\"}'
```
You should be able to obtain a response (folded for readability) including `ticket`.
```JSON theme={null}
{
[...]
"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](/api-reference/authorization-endpoint/issue-authorization-response).
* [Linux/Mac](#authz-issue-bash)
* [Windows](#authz-issue-powershell)
```shell theme={null}
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
```
```shell theme={null}
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).
```JSON theme={null}
{
"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](/api-reference/introspection-endpoint/process-introspection-request) 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](/api-reference/introspection-endpoint/process-introspection-request).
* [Linux/Mac](#token-request-bash)
* [Windows](#token-request-powershell)
```shell theme={null}
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":"","clientSecret":"","parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code="}' | jq
```
```shell theme={null}
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\":\"\",\"clientSecret\":\"\",\"parameters\": \"grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=\"}'
```
Authlete makes the following error response (folded for readability).
```JSON theme={null}
{
"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](https://openid.net/specs/openid-financial-api-part-2-1_0.html#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):
> 1. `tls_client_auth` or `self_signed_tls_client_auth` as specified in section 2 of MTLS, or
> 2. `private_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](https://www.rfc-editor.org/rfc/rfc8705.html#name-mutual-tls-for-oauth-client) or [private\_key\_jwt defined in OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication). 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`)](https://www.rfc-editor.org/rfc/rfc8705.html#name-pki-mutual-tls-method) defined in the method.
Read [Client authentication using private\_key\_jwt method](/configuration-reference/endpoints/client-authentication-using-private-key-jwt-method) when using `private_key_jwt`.
Note that you have to configure mutual TLS to enable "Holder of Key," even if the client authentication method is employed.
# Making a FAPI-compliant token request
## TLS client authentication configuration
Let's configure both the service and the client settings to enable TLS client authentication.
### Service settings for TLS client authentication
1. Navigate to **Service Settings > Endpoints > Token** in the Authlete Management Console.
2. Locate the **Supported Client Authentication Methods** section.
3. Check the box for **TLS\_CLIENT\_AUTH** to enable it.
4. Click **Save Changes** to apply the configuration.

### Client settings for Mutual TLS (mTLS) Authentication
1. Navigate to **Client Settings > Endpoints > Token > MTLS** in the Authlete Management Console.
2. Locate the **Subject Distinguished Name** section.
* Enter the **Subject Distinguished Name** (e.g., `CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP`).
* Optionally, set **Subject Alternative Name DNS**, **Subject Alternative Name IP Address**, or **Subject Alternative Name URI** if required.
3. Click **Save Changes** to apply these settings.

4. Navigate back to **Client Settings > Endpoints > Token**.
5. Locate the **Client Authentication Method** section.
* Select **TLS\_CLIENT\_AUTH** from the dropdown.
6. Click **Save Changes** again to finalize the mTLS authentication setup.

### Generating a self-signed certificate
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.
* Generating a private key
```Shell theme={null}
$ openssl genrsa 2048 > private_key_nopass.pem
Generating RSA private key, 2048 bit long modulus
...............+++
.......................+++
e is 65537 (0x10001)
```
* Generating a CSR
* Using `CN=client.example.org, O=Client, L=Chiyoda-ku, ST=Tokyo, C=JP` as Subject DN
```Shell theme={null}
$ 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 []:
```
```shell theme={null}
$ cat server.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICpTCCAY0CAQAwYDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRva3lvMRMwEQYD
VQQHDApDaGl5b2RhLWt1MQ8wDQYDVQQKDAZDbGllbnQxGzAZBgNVBAMMEmNsaWVu
[...]
r4MUVOwPNWOM6UGYQZwjvtJ2rmKr8cQrbfvbcFiY4s6lLQGOz5yLzmO8GUdmfzUd
p5BW1iL+SpjS
-----END CERTIFICATE REQUEST-----
```
* Generating a certificate
```Shell theme={null}
$ 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`
```Text theme={null}
-----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.
```Text theme={null}
-----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
```
### Testing the configuration (Mutual TLS)
Let's check if Authlete works as expected. Run the same procedure again.
1. Make a request to [`/auth/authorization` API](/api-reference/authorization-endpoint/process-authorization-request) and obtain a value of `ticket` from a response
2. Make a request to [`/auth/authorization/issue` API](/api-reference/authorization-endpoint/issue-authorization-response) and obtain a value of `authorizationCode` from a response
3. Make a request with the following modification to [`/auth/token` API](/api-reference/introspection-endpoint/process-introspection-request)
* 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](/api-reference/introspection-endpoint/process-introspection-request).
* [Linux/Mac](#mtls-token-request-bash)
* [Windows](#mtls-token-request-powershell)
```shell theme={null}
curl -s -X POST https://api.authlete.com/api/auth/token \
-H 'Authorization: Bearer {SERVICE ACCESS TOKEN}' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "",
"parameters": "grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=",
"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
```
```shell theme={null}
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\": \"\",
\"parameters\": \"grant_type=authorization_code&redirect_uri=https://client.example.org/cb/example.com&code=\",
\"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).
```JSON theme={null}
{
"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.
# Making a FAPI-compliant token response
## Holder of Key Configuration
### Service Settings for Access Token Configuration
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.

### Client Settings for Access Token Configuration
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.
# A complete example walk through
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](/api-reference/authorization-endpoint/process-authorization-request), [`/auth/authorization/issue` API](/api-reference/authorization-endpoint/issue-authorization-response) and [`/auth/token` API](/api-reference/introspection-endpoint/process-introspection-request).
In addition to these three requests, make another request to [`/auth/introspection` API](/api-reference/introspection-endpoint/process-introspection-request) to see if the access token used for API requests to a resource server is bound with the TLS client certificate of the client.
## Authorization request
* Request to [`/auth/authorization` API](/api-reference/authorization-endpoint/process-authorization-request)
```Shell theme={null}
curl -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"}'
```
* Response (folded for readability)
```JSON theme={null}
{
[...]
"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"
[...]
```
## Authorization response
* Request to [`/auth/authorization/issue` API](/api-reference/authorization-endpoint/issue-authorization-response)
```Shell theme={null}
curl -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
```
* Response (folded for readability)
```JSON theme={null}
{
"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"
}
```
## Token request and token response
* Request to [`/auth/token` API](/api-reference/introspection-endpoint/process-introspection-request)
```Shell theme={null}
curl -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
```
* Response (folded for readability)
```JSON theme={null}
{
"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"
}
```
## API request
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](/api-reference/introspection-endpoint/process-introspection-request). 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.
* A request to [`/auth/introspection` API](/api-reference/introspection-endpoint/process-introspection-request)
- [Linux/Mac](#introspection-bash)
- [Windows](#introspection-powershell)
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/service//introspection \
-H 'Authorization: Bearer ' \
-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
```
```shell theme={null}
curl.exe -s -X POST https://us.authlete.com/api/service//introspection `
-H "Authorization: Bearer " `
-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\"}'
```
* Response (folded for readability)
```JSON theme={null}
{
"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.
# Conclusion
In this tutorial, we reviewed security provisions defined in [Financial-grade API Security Profile 1.0 - Part 2: Advanced](https://openid.net/specs/openid-financial-api-part-2-1_0.html) and configuration instructions of Authlete through steps for building a FAPI compliant authorization server.
# FAPI Basics Supplement: Integration with Reference Implementations
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/fapi-basics-supplement-integration-with-reference-implementations
A tutorial to integrate Authlete's reference implementations with an Authlete service, that has been configured with settings described in another tutorial, Financial-grade API (FAPI) Basics.
# Preface
This document describes how to evaluate FAPI-compliant authorization flow and API request using an an Authlete service, that has been configured with settings described in [Financial-grade API (FAPI) Basics](/protocols-and-flows/compliance-profiles/fapi-basics) (hereinafter “FAPI Basics”), and other components including Authlete's reference implementations.
# Prerequisites
Before doing this, you are expected to have knowledge of OpenID Connect and Authlete by running through the following tutorial.
* [OIDC Basics](/get-started/quickstarts/oidc-basics)
You also need to have an Authlete service that has been configured with settings in accordance with FAPI Basics below. Run the instructions ([A complete example walk through](/protocols-and-flows/compliance-profiles/fapi-basics#a-complete-example-walk-through)) in the document to make sure that your Authlete service works properly.
* [Financial-grade API Basics](/protocols-and-flows/compliance-profiles/fapi-basics)
The following software components are used in this tutorial. Prepare your lab environment that can execute them.
* Required for this tutorial
* [authlete/java-oauth-server](https://github.com/authlete/java-oauth-server/): A reference implementation of authorzation server
* [authlete/java-resource-server](https://github.com/authlete/java-resource-server/): A reference implementation of resource server
* Reommended, but you can choose other alternative tools
* [Apache HTTP Server](https://httpd.apache.org/) (hereinafter “Apache”): A reverse proxy for authorization server and resource server
* [curl](https://curl.haxx.se/): An API client
* [OpenSSL](https://www.openssl.org/): A tool to generate private keys and public key certificates
# Configuring an authorization server
In this section, we will cover the following tasks:
* Deploying a java-oauth-server package
* Testing connection
The diagram below illustrates components configured in this section.
A Web browser and curl send requests to an authorization server (java-oauth-server)
whose endpoint is [http://localhost:8080](http://localhost:8080).
## Configuring java-oauth-server
We will download and configure java-oauth-server.
Detailed instructions are described in [its document](https://github.com/authlete/java-oauth-server#how-to-run).
### Downloading the source
Download java-oauth-server using `git` command.
```shell theme={null}
$ git clone https://github.com/authlete/java-oauth-server.git
```
### Configuration
Edit the following entries in its configuration file, `authlete.properties`.
Put the API key and the secret of your Authlete service that has been configured in accordance with FAPI Basics.
```shell theme={null}
service.api_key =
service.api_secret =
```
### Starting the server
Start java-oauth-server using `mvn` command.
```shell theme={null}
$ mvn jetty:run
```
### Test
Connect to [http://localhost:8080](http://localhost:8080) using a Web browser and the following page should be displayed.
## Executing FAPI authorization flow (1)
Generate a request object in accordance with [instructions in FAPI Basics](/protocols-and-flows/compliance-profiles/fapi-basics#authentication-context-reference-acr-settings) and craft an authorization request using the object.
The following values are used in this tutorial.
* Payload (replace the value of `client_id` (`1756...3766`) with [yours that has been automatically generated during FAPI Basics](/protocols-and-flows/compliance-profiles/fapi-basics#adding-a-new-authlete-service-with-a-client))
```json theme={null}
{
"redirect_uri":"https://client.example.org/cb/example.com",
"response_type":"code id_token",
"client_id":"1756...3766",
"scope":"openid payment",
"exp":15549730000,
"aud":"https://as.example.com",
"claims":{
"id_token":{
"acr":{
"essential":true,
"values":["urn:example:psd2:sca"]
}
}
},
"nonce":"n-0S6_WzA2Mj"
```
* Signed JWT (use [your signing key that has been generated in FAPI Basics](/protocols-and-flows/compliance-profiles/fapi-basics#request-object-settings) for its signature)
```text theme={null}
eyJr...YifQ.ewoi...iCn0.ztl2...tjAA
```
* Authorization request (replace the values of `client_id` (`1756...3766`) and `request` (`eyJr...YifQ.ewoi...iCn0.ztl2...tjAA`) with yours determined above)
```shell theme={null}
http://as.example.com:8080/api/authorization
?redirect_uri=https://client.example.org/cb/example.com
&scope=openid+payment
&response_type=code+id_token
&client_id=1756...3766
&nonce=n-0S6_WzA2Mj
&request=eyJr...YifQ.ewoi...iCn0.ztl2...tjAA
```
Enter the crafted request string (URL) to your Web browser's location field.
An authorization page should be displayed.
Enter `john` / `john` to Login ID / Password fields and click Authorize button.
Your Web browser will attempt (and fail) to connect `https://client.example.org/cb/example.com/#code=...` and show an error page stating that the host `client.example.org` doesn't exist.
### Authorization response
Copy the URL of the error page like below.
```html theme={null}
https://client.example.org/cb/example.com#code=0Io2....sdkQ&id_token=eyJr...YifQ.eyJz...aiJ9.-EPu...Dkkw
```
Extract `code` parameter in the URL.
```shell theme={null}
code=0Io2...sdkQ
```
### Token request
Let's craft a token request that contains the value of `code` (`0Io2...sdkQ` for example)
and send it to java-oauth-server's token endpoint (/api/token).
In this section, we will try to do client authentication using `client_id` / `client_secret` as follows.
Replace values of these parameters with yours that have been automatically generated during FAPI Basics.
This tutorial uses curl, but you can use other equivalent tools.
```shell theme={null}
$ curl -s -X POST http://localhost:8080/api/token \
-u '1756...3766:EXE7...UxHg' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'redirect_uri=https://client.example.org/cb/example.com' \
--data-urlencode 'code=0Io2...sdkQ'
```
### Token response
You will receive a token response like this:
```json theme={null}
{
"error_description": "[A244308] Because the feature of certificate-bound
access tokens is enabled, the client must establish a mutual TLS connection
to the token endpoint.",
"error": "invalid_request",
"error_uri": "https://docs.authlete.com/#A244308"
}
```
That means:
* java-oauth-server was unable to extract any client certificates from the token request as no mutual TLS connection has been established between the client (`curl` command in this tutorial) and java-oauth-server
* Thus java-oauth-server didn't include any client certificates in a request to Authlete's
([/auth/token API](http://docs.authlete.com/#auth-token-api))
* So Authlete couldn't issue any certificate-bound access tokens and made a response with [A244308 error](https://docs.authlete.com/#A244308)
Authlete's error response examples described in this tutorial include
`"error_description"` and `"error_uri"`.
You can omit them by configuring Authlete service settings.
See [an article in Authlete Knowledge Base](/configuration-reference/service-settings/service-management) for details.
In the next section, we will add a reverse proxy to accept client certificates from clients to solve the error.
# Enabling TLS for the authorization server
In this section, we will cover the following tasks:
* Configuring a reverse proxy
* Configuring a client
The diagram below illustrates components configured in this section.
In the previous section, both Web browsers and clients connect directly to java-oauth-server ([http://localhost:8080](http://localhost:8080)).
The destination will be switched to Apache ([https://as.example.com:8443](https://as.example.com:8443)) that resides in front of the server as a reverse proxy.
On the client side, curl will attempt to connect to Apache using a client certificate to get authenticated.
## Configuring a reverse proxy (1)
### FQDN settings
Add the following entry to `/etc/hosts`
so that both Web browsers and clients can find Apache to be installed in your lab environment, using `as.example.com` as FQDN.
```text theme={null}
127.0.0.1 as.example.com
```
### Installing Apache
Install Apache in your lab environment.
This tutorial uses [Homebrew](https://brew.sh/index_ja) on MacOS to install Apache, but you can use other equivalent installation methods and/or reverse proxy software.
```shell theme={null}
$ brew install httpd
```
This tutorial uses `/usr/local/etc/httpd` directory as the primary location of Apache's configuration files.
### Preparing a server certificate
This tutorial uses a self-signed certificate to make configuration steps simpler.
It is not appropriate for production use.
Creating a private key and a public key certificate
Create an RSA private key and a public key certificate for Apache, using `openssl` command.
This tutorial uses OpenSSL, but you can use other equivalent tools.
```shell theme={null}
$ openssl req -x509 -newkey rsa:2048 \
-keyout server.key -out server.crt
-days 3650 -sha256 -nodes \
-subj '/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Server/CN=as.example.com'
Generating a 2048 bit RSA private key
..........................+++
....+++
writing new private key to 'server.key'
-----
```
Two files, `server.key` (a private key) and `server.crt` (a public key certificate) are created.
Deploying the private key and the public key certificate
Deploy these two files to arbitrary location in your lab environment.
This tutorial copies them under `/usr/local/etc/httpd` directory.
```shell theme={null}
$ sudo cp -p server.key server.crt /usr/local/etc/httpd/.
Password:
```
### Configuring TLS for Apache
Content of Apache's configuration files may be different from the example below, due to installation methods.
Adjust them for your lab environment.
httpd.conf
We will configure basic Apache settings to:
* Disable listening port 8080 (as java-oauth-server uses it)
* Enable mod\_proxy and mod\_ssl related modules (to get Apache working as a reverse proxy and accepting TLS connection)
Actual changes to the configuration file, `httpd.conf` are as follows.
TLS related settings are to be included from another file (`extra/httpd-ssl.conf`).
```diff theme={null}
$ diff /usr/local/etc/httpd/httpd.conf.orig \
/usr/local/etc/httpd/httpd.conf
52c52
< Listen 8080
---
> #Listen 8080
92c92
< #LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
---
> LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
131c131
< #LoadModule proxy_module lib/httpd/modules/mod_proxy.so
---
> LoadModule proxy_module lib/httpd/modules/mod_proxy.so
134c134
< #LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so
---
> LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so
150c150
< #LoadModule ssl_module lib/httpd/modules/mod_ssl.so
---
> LoadModule ssl_module lib/httpd/modules/mod_ssl.so
524c524
< #Include /usr/local/etc/httpd/extra/httpd-ssl.conf
---
> Include /usr/local/etc/httpd/extra/httpd-ssl.conf
```
extra/httpd-ssl.conf
Please be advised that the following settings of TLS are insufficient
for production use. These settings are intended to enable mutual TLS just for this tutorial.
In this tutorial, we configure TLS and reverse proxy settings to:
* Use as.example.com as its server name (while its listening port (8443) unchanged)
* Meet the FAPI provisions (by using TLS 1.2 and limited sets of cypher suites)
* Include a client certificate in X-Ssl-Cert header (so that /api/token endpoint of java-oauth-server can extract the value and include it in a request to Authlete API (/auth/token API), if it is set in the request from the reverse proxy)
* Work as a reverse proxy that forwards requests accepted at [https://as.example.com:8443/](https://as.example.com:8443/) to [http://localhost:8080/](http://localhost:8080/) (java-oauth-server)
Changes to the configuration file, `extra/httpd-ssl.conf` are as follows.
```diff theme={null}
$ diff /usr/local/etc/httpd/extra/httpd-ssl.conf.orig \
/usr/local/etc/httpd/extra/httpd-ssl.conf
125c125
< ServerName www.example.com:8443
---
> ServerName as.example.com:8443
289a290,302
> SSLEngine on
> SSLProtocol TLSv1.2
> SSLCipherSuite -ALL:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256
> SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
> SSLCACertificateFile "/usr/local/etc/httpd/server.crt"
> SSLVerifyClient optional_no_ca
> SSLOptions +StdEnvVars +ExportCertData
> SSLCompression off
> RequestHeader set X-Ssl-Cipher "%{SSL_CIPHER}e" env=SSL_CIPHER
> RequestHeader set X-Ssl-Cert "%{SSL_CLIENT_CERT}e" env=SSL_CLIENT_CERT
> ProxyPreserveHost on
> ProxyPass "/" "http://localhost:8080/"
> ProxyPassReverse "/" "http://localhost:8080/"
```
### Starting Apache
Start the Apache using `apachectl` command.
```shell theme={null}
$ apachectl start
```
Once it's started, connect to [https://as.example.com:8443](https://as.example.com:8443) using your Web browser and confirm if the content is the same as [http://localhost:8080](http://localhost:8080), the top page of java-oauth-server.
In the next section, we wlll prepare a private key and a public key certificate for a client (curl) that makes a token request.
## Settings for a client
### Preparing a client certificate
In this tutorial, we will use a self-signed certificate to make configuration steps simpler.
It is not appropriate for production use.
Creating an RSA private key and a public key certificate
Execute `openssl` command as follows to create a private key and a public key certificcate.
```shell theme={null}
$ openssl req -x509 -newkey rsa:2048 \
-keyout client.key -out client.crt
-days 3650 -sha256 -nodes \
-subj '/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Server/CN=client.example.org'
Generating a 2048 bit RSA private key
..........................+++
....+++
writing new private key to 'client.key'
-----
```
Two files, `client.key` (a private key) and `client.crt` (a public key certificate) are created.
We will use them to make token requests with curl.
##
### Authorization request and response
Make an authorization request in the same way as [Executing FAPI authorization flow (1)](#executing-fapi-authorization-flow-1).
You can use either [http://localhost:8080/api/authorization](http://localhost:8080/api/authorization) (the same as above) or
[https://as.example.com:8443/api/authorization](https://as.example.com:8443/api/authorization) (Apache) as an authorization endpoint.
Extract a value of `code` parameter from an authorization response. The value in this tutorial is as follows.
```text theme={null}
7Y5h...zp_4
```
### Token request
Craft a token request and send it to the token endpoint of java-oauth-server (/api/token).
The different parts from the previous example ([Executing FAPI authorization flow (1)](#executing-fapi-authorization-flow-1))
are:
* Endpoint URL: using `https://as.example.com:8443` instead of `http://localhost:8080`
* Client authentication method: using the private key (`client.key`) and the public key certificate (`client.crt`) instead of `client_secret`
Thus the `curl` command to be executed is like below.
(replace the values of `client_id` and `code` with appropriate ones in your lab environment)
```shell theme={null}
$ curl --insecure -X POST https://as.example.com:8443/api/token \
--key client.key --cert client.crt \
--data-urlencode 'client_id=1756...3766' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'redirect_uri=https://client.example.org/cb/example.com' \
--data-urlencode 'code=7Y5h...zp_4'
```
### Token response
You should receive a token response like below.
```json theme={null}
{
"access_token": "nbN3...LAU8",
"refresh_token": "SWUu...V8_w",
"scope": "openid payment",
"id_token": "eyJr...YifQ.eyJz...qIn0.GPJr...STPQ",
"token_type": "Bearer",
"expires_in": 86400
}
```
Now you have successfully got an access token (`nbN3...LAU8` for example) through mutual TLS connection. In the backstage, Authlete has made the access token bound to the client certificate.
In the next section, we will attempt to make an API request with an access token to a resource server.
# Configuring a resource server
In this section, we will cover the following tasks:
* Deploying a java-resource-server package
* Testing connection
The diagram below illustrates components configured in this section.
## Configuring java-resource-server
We will download and configure java-resource-server.
Instructions are described in [its document](https://github.com/authlete/java-resource-server#how-to-run).
### Downloading the source
Download java-resource-server using `git` command.
```shell theme={null}
$ git clone https://github.com/authlete/java-resource-server.git
```
### Configuration
Edit the following entries in its configuration file, `authlete.properties`.
Put the API key and the secret that are
[the same values as specified for java-oauth-server](#configuring-java-oauth-server).
```shell theme={null}
service.api_key =
service.api_secret =
```
### Starting the server
Start java-resource-server using `mvn` command.
```shell theme={null}
$ mvn jetty:run
```
### Test
Connect to [http://localhost:8081](http://localhost:8081) using a Web browser and the following page should be displayed.
## Executing an API request (1)
Let's try to make an API request with an access token to "[Country Endpoint](https://github.com/authlete/java-resource-server/blob/master/README.md#country-endpoint)" of java-resource-server, using `curl` command.
The access token can be obtained by doing the flow described in [Executing FAPI authorization flow (2)](#executing-fapi-authorization-flow-2). In this tutorial, the
following value is used.
```text theme={null}
6Ivp...LiM4
```
Make a request with the access token specified in `Authorization` header.
The examples below show the request and the subsequent response.
`-v` option is added to `curl` command so that it can enable verbose output especially header information.
```shell theme={null}
$ curl -v -X GET http://localhost:8081/api/country/JP \
-H 'Authorization: Bearer 6Ivp...LiM4'
...
> GET /api/country/JP HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.64.1
> Accept: */*
> Authorization: Bearer 6Ivp...LiM4
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 18 May 2020 02:22:32 GMT
< Cache-Control: no-store, no-transform
< Pragma: no-cache
< WWW-Authenticate: Bearer error="invalid_token",
error_description="[A065304] The client failed to present
a certificate and the access token is MTLS bound.",
error_uri="https://docs.authlete.com/#A065304"
< Content-Length: 0
< Server: Jetty(9.3.7.v20160115)
...
```
That response means:
* java-resource-server was unable to extract any client certificates from the token request as no mutual TLS connection has been established between the client (`curl` command) and java-resource-server
* Thus, java-resource-server didn't include any client certificates in a request to Authlete's [/auth/introspection API](http://docs.authlete.com/#auth-introspection-api)
* So, Authlete couldn't check the binding between the access token and its corresponding client certificate and made a response with [A065304 error](https://docs.authlete.com/#A065304)
In the next section, we will configure additional settings for the reverse proxy.
# Enabling TLS for the resource server
In this section, we will cover the following tasks that are basically the same as the settings for java-oauth-server done in the former section:
* Additional settings for the reverse proxy
The diagram below illustrates components configured in this section.
curl will attempt to connect to Apache ([https://rs.example.com:8443](https://rs.example.com:8443)) using a client certificate to get authenticated.
## Configuring a reverse proxy (2)
### FQDN settings
Modify the entry that has been added to `/etc/hosts` during [Configuring a reverse proxy (1)](#configuring-a-reverse-proxy-1) so that curl can connect to Apache using `rs.example.com`.
```text theme={null}
127.0.0.1 as.example.com rs.example.com
```
### Configuring TLS for Apache
extra/httpd-ssl.conf
Add the following entries to the `extra/httpd-ssl.conf` file.
These settings, the same as [Configuring a reverse proxy (1)](#configuring-a-reverse-proxy-1) except the server name and destinations of the reverse proxy, are to:
* Declare these settings are applied to rs.example.com:8443
* Meet the FAPI provisions (by using TLS 1.2 and limited sets of cypher suites)
* Include a client certificate in X-Ssl-Cert header (so that /api/country endpoint of java-resource-server can extract the value and include it in a request to Authlete API (/auth/introspection API), if it is set in the request from the reverse proxy)
* Work as a reverse proxy that forwards requests accepted at [https://rs.example.com:8443/](https://rs.example.com:8443/) to [http://localhost:8081/](http://localhost:8081/) (java-resource-server)
The additional entries to the configuration file, `extra/httpd-ssl.conf` are as follows.
```apache theme={null}
DocumentRoot "/usr/local/var/www"
ServerName rs.example.com:8443
ServerAdmin you@example.com
ErrorLog "/usr/local/var/log/httpd/error_log"
TransferLog "/usr/local/var/log/httpd/access_log"
SSLEngine on
SSLProtocol TLSv1.2
SSLCipherSuite -ALL:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256
SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
SSLCertificateFile "/usr/local/etc/httpd/server.crt"
SSLCACertificateFile "/usr/local/etc/httpd/server.crt"
SSLVerifyClient optional_no_ca
SSLOptions +StdEnvVars +ExportCertData
SSLCompression off
RequestHeader set X-Ssl-Cipher "%{SSL_CIPHER}e" env=SSL_CIPHER
RequestHeader set X-Ssl-Cert "%{SSL_CLIENT_CERT}e" env=SSL_CLIENT_CERT
ProxyPreserveHost on
ProxyPass "/" "http://localhost:8081/"
ProxyPassReverse "/" "http://localhost:8081/"
```
### Starting Apache
Stop and start the Apache using `apachectl` command.
```shell theme={null}
$ apachectl stop && apachectl start
```
After the restart, connect to [https://rs.example.com:8443](https://rs.example.com:8443) using your Web browser and confirm if the content is the same as [http://localhost:8081](http://localhost:8081), the top page of java-resource-server.
## Executing an API request (2)
Make an API request using `curl` command in the same way as [Executing an API request (1)](#executing-an-api-request-1).
The access token can be obtained by doing the flow described in [Executing FAPI authorization flow (2)](#executing-fapi-authorization-flow-2). In this tutorial, the
following value is used.
```text theme={null}
6Ivp...LiM4
```
Make a request with the access token specified in `Authorization` header.
The different parts from the previous example ([Executing an API request (1)](#executing-an-api-request-1)) are:
* Endpoint URL: using `https://rs.example.com:8443` instead of `http://localhost:8081`
* Client authentication method: using the private key (`client.key`) and the public key certificate (`client.crt`) in addition to the access token
Thus the `curl` command to be executed is like below.
```shell theme={null}
$ curl -v -X GET http://localhost:8081/api/country/JP \
-H 'Authorization: Bearer 6Ivp...LiM4' \
--key client.key --cert client.crt --insecure
...
```
You should receive a successful API response like below.
```json theme={null}
{
"name": "Japan",
"alpha2": "JP",
"alpha3": "JPN",
"numeric": 392,
"currency": "JPY"
}
```
It means that:
* java-resource-server extracted the client certificate from the mutual TLS connection and sent it to Authlete,
* Authlete validated the binding between the client certificate and the access token, and
* java-resource-server has processed the API request as expected.
# Conclusion
In this tutorial, we confirmed FAPI-compliant authorization flow and API request
using a FAPI-enabled Authlete service,
reference implementations (java-oauth-server and java-resource-server)
and a reverse proxy.
# Financial-grade API (FAPI)
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/financial-grade-api-fapi-overview
Technical information about Financial-grade API (FAPI) and Authlete's implementation.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/compliance-profiles/financial-grade-api-fapi-overview).
## What is FAPI?
[FAPI](https://openid.net/wg/fapi/), being standardized by a working group under the [OpenID Foundation](https://openid.net/), is a standard for secure and interoperable API communication. Beyond the financial industry, it plays an important role in sectors that demand a high level of security, such as healthcare and e-government.
FAPI builds on OAuth 2.0 and OpenID Connect. It started with FAPI 1.0 and has since evolved into FAPI 2.0, the current version. FAPI 2.0 adopts newer protocols to protect data more securely, while being simpler to implement than FAPI 1.0.
## FAPI Security Profiles
Among those specifications, [the FAPI Security Profiles](https://openid.net/wg/fapi/specifications/) can be applied to online services in any sector, not only financial services, that requires a higher level of security than standard OAuth or OpenID Connect provides. There are two of them, FAPI 1.0 and FAPI 2.0, and Authlete supports both.
### FAPI 1.0
FAPI 1.0 is split into two parts.
* [Financial-grade API Security Profile (FAPI) 1.0 – Part 1: Baseline](https://openid.net/specs/openid-financial-api-part-1-1_0.html)
A baseline security profile of OAuth that is suitable for protecting APIs with a moderate inherent risk
* [Financial-grade API Security Profile (FAPI) 1.0 – Part 2: Advanced](https://openid.net/specs/openid-financial-api-part-2-1_0.html)
An advanced security profile of OAuth that is suitable for protecting APIs with high inherent risk, such as those giving access to highly sensitive data, or triggering financial transactions (e.g., payment initiation)
The latter one, "FAPI Part 2," provides higher security measures by leveraging advanced features defined in OpenID Connect specifications in addition to OAuth standards.
Here are some notable enhancements:
* Prevention of sender impersonation and message tampering in terms of authorization request and response
* Using [request object](https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests)
* Using [hybrid flow](https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth) or [JARM](https://bitbucket.org/openid/fapi/src/master/Financial_API_JWT_Secured_Authorization_Response_Mode.md)
* Prevention of leakage and unauthorized use of authorization code
* Strict checking of Redirect URI (`redirect_uri`)
* Prevention of client impersonation
* Client authentication with [mutual TLS client authentication](https://www.rfc-editor.org/rfc/rfc8705.html#name-mutual-tls-for-oauth-client)
or [JWT](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
* Prevention of unauthorized use of tokens
* [Certificate-bound access tokens](https://www.rfc-editor.org/rfc/rfc8705.html#name-mutual-tls-client-certifica)
### FAPI 2.0
[FAPI 2.0](/protocols-and-flows/compliance-profiles/fapi-2-0) is a comprehensive framework for raising API security. It breaks down into the following four components.
* [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-security-profile-2_0-final.html)
The core profile of FAPI 2.0. It makes use of recent security specifications such as PAR and DPoP
* [FAPI 2.0 Message Signing Profile](https://openid.net/specs/fapi-message-signing-2_0-final.html)
A profile that adds a non-repudiation mechanism to requests and responses based on OAuth 2.0
* [FAPI 2.0 Attacker Model](https://openid.net/specs/fapi-attacker-model-2_0-final.html)
Defines the potential risks to API security and the measures that prevent them
* [Grant Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html)
Extends OAuth 2.0 with a mechanism for managing the grants given to a client application
The names of the FAPI 2.0 profiles changed while the specifications were being developed: "Baseline Profile" became "Security Profile", and "Advanced Profile" became "Message Signing Profile". Keep this in mind when you come across material or configuration values that use the former names.
The following documents and slides might help you understand FAPI.
1. A Comprehensive Commentary on Financial-grade API
This white paper describes technical details on Financial-grade API (FAPI) security profiles on a line-by-line basis, and how Authlete implements FAPI to enable flexibile deployment.
## Authlete and FAPI
Authlete has supported Financial-grade API since July 2018 and [has been certified since April 2019](https://openid.net/certification/#FAPI_OPs).
Here are useful resources that help you understand how you can build a FAPI-compliant authorization server with Authlete.
* [Authlete FAPI Enhancements](https://www.authlete.com/resources/videos/20180724/authlete-fapi-enhancements/)
The session explains comparison of Authlete's unique semi-hosted approach and traditional approaches for deploying OAuth infrastructure, and how Authlete has extended its client authentication functions and supported mutual TLS to implement Financial-grade API (FAPI).
* [How to use FAPI feature](/protocols-and-flows/compliance-profiles/how-to-use-fapi)
How Authlete decides which profile to apply, for both FAPI 1.0 and FAPI 2.0. It is also the entry point to the page that covers each profile in detail.
* [Validation in FAPI mode](/protocols-and-flows/compliance-profiles/validation-in-fapi-mode)
The validations Authlete performs in FAPI mode in addition to those for standard OAuth 2.0 and OpenID Connect.
* [FAPI Basics](/protocols-and-flows/compliance-profiles/fapi-basics)
A tutorial to configure Authlete to build a Financial-grade API (FAPI) compliant authorization server.
* [FAPI Basics Supplement: Integration with Reference Implementations](/protocols-and-flows/compliance-profiles/fapi-basics-supplement-integration-with-reference-implementations)
A tutorial to integrate Authlete's reference implementations with an Authlete service, that has been configured with settings described in another tutorial, Financial-grade API (FAPI) Basics.
* [FAPI 2.0](/protocols-and-flows/compliance-profiles/fapi-2-0)
An overview of the FAPI 2.0 Security Profile and the Message Signing Profile, and an index of the articles on supporting them with Authlete.
# HAIP-compliant Verifiable Credential Issuance
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/haip-compliant-verifiable-credential-issuance
This is an explanation of the issuance procedure for Verifiable Credentials (VCs) that comply with the OpenID4VC High Assurance Interoperability Profile 1.0 (HAIP).
## Introduction
This article explains the procedure for issuing Verifiable Credentials (VCs)
that comply with the **[OpenID4VC High Assurance Interoperability Profile 1.0][HAIP]**
(hereinafter "**[HAIP][HAIP]**").
## Specification Key Points
Regarding the VC issuance procedure, HAIP can be broadly seen as a combination
of **[OpenID for Verifiable Credential Issuance 1.0][OID4VCI]** (hereinafter
"**[OID4VCI][OID4VCI]**") and the **[FAPI 2.0 Security Profile][FAPI2SP]**
(hereinafter "**[FAPI2SP][FAPI2SP]**"). However, there are some important
differences to be aware of, which are outlined below.
### Sender-Constrained Access Token
In FAPI2SP, the following mechanisms can be used to achieve
**sender-constrained access tokens**.
* **MTLS** ([RFC 8705][RFC_8705])
* **DPoP** ([RFC 9449][RFC_9449])
In contrast, HAIP permits only DPoP.
### Client Authentication
In FAPI2SP, the following **client authentication** methods are available.
* `tls_client_auth` ([RFC 8705][RFC_8705])
* `self_signed_tls_client_auth` ([RFC 8705][RFC_8705])
* `private_key_jwt` ([RFC 7523][RFC_7523])
In HAIP, in addition to these,
**[OAuth 2.0 Attestation-Based Client Authentication][ABCA]** (hereinafter
"**[ABCA][ABCA]**") is also supported.
* `attest_jwt_client_auth` ([ABCA][ABCA])
Note that when using ABCA in the context of HAIP, the client attestation must
include the `x5c` header parameter. In addition, the X.509 certificate
containing the public key for signature verification (i.e., the leaf
certificate in the certificate chain) must not be self-signed.
A literal reading of the HAIP specification suggests that client authentication
methods other than ABCA are also permitted. However, since discussions around
the HAIP specification appear to assume the use of ABCA, and the specification
may be updated to reflect this view (i.e., to allow only ABCA), this article
adopts ABCA as the client authentication method.
### Key Attestation
OID4VCI defines several formats for **Key Proofs** included in a credential
request. Among them, the **jwt Proof Type** ([OID4VCI Appendix F.1][OID4VCI_F1])
allows a Key Attestation ([OID4VCI Appendix D][OID4VCI_D]) to be specified in
the `key_attestation` header parameter. In addition, the
**attestation Proof Type** ([OID4VCI Appendix F.3][OID4VCI_F3]) allows the Key
Attestation itself to be used directly as the Key Proof.
When using a Key Attestation in the context of HAIP, it must include the `x5c`
header parameter. Furthermore, the X.509 certificate containing the public key
for signature verification (i.e., the leaf certificate in the certificate chain)
must not be self-signed.
### Scope
In general, an access token is associated with one or more scopes. In the
context of HAIP, those scopes must include at least one that refers to a
specific credential configuration.
More specifically, the access token must be associated with the value of the
`scope` property of at least one credential configuration listed in
`credential_configurations_supported` in the Credential
Issuer Metadata.
## Procedure Overview
An overview of the VC issuance procedure using the authorization code flow is
as follows:
1. PAR Request
2. Authorization Request
3. Token Request
4. Credential Request
However, since each request requires various tokens, the actual procedure
becomes more complex. The following outlines the procedure, including the
generation of those tokens.
1. PAR Request
* PKCE Token (Code Verifier and Code Challenge) Generation ([PKCE][RFC_7636])
* Client Attestation Generation ([ABCA][ABCA])
* Attestation Challenge Retrieval ([ABCA][ABCA])
* Client Attestation PoP Generation ([ABCA][ABCA])
* DPoP Proof JWT Generation ([DPoP][RFC_9449])
* PAR Request Submission ([PAR][RFC_9126])
2. Authorization Request
* Authorization Request Submission
3. Token Request
* Client Attestation Generation ([ABCA][ABCA]) (reusable)
* Attestation Challenge Retrieval ([ABCA][ABCA]) (reusable)
* Client Attestation PoP Generation ([ABCA][ABCA]) (reusable)
* DPoP Proof JWT Generation ([DPoP][RFC_9449])
* Token Request Submission
4. Credential Request
* Nonce Retrieval ([OID4VCI][OID4VCI])
* Key Attestation Generation ([OID4VCI][OID4VCI])
* Key Proof Generation ([OID4VCI][OID4VCI])
* DPoP Proof JWT Generation ([DPoP][RFC_9449])
* Credential Request Submission ([OID4VCI][OID4VCI])
## Actual Procedure
In this section, we will walk through the actual procedure.
For the scripts used to generate the various tokens, as well as the private
keys, public keys, and certificates, we use those published in
[authlete/oid4vci-demo][OID4VCI_DEMO].
### PAR Request
HAIP is based on FAPI2SP, and since FAPI2SP mandates the use of [PAR][RFC_9126],
PAR is also required for HAIP-compliant authorization requests. Here, we
register the authorization request at the PAR endpoint and obtain a
**request URI**.
#### PKCE Token Generation
HAIP is based on FAPI2SP, and since FAPI2SP mandates the use of [PKCE][RFC_7636],
HAIP-compliant authorization requests must include a code challenge, and token
requests must include a code verifier. Therefore, we use the `pkce` script to
generate them.
```shell theme={null}
./pkce
```
Output:
```shell theme={null}
CODE_VERIFIER=3HlzvGOxhJz3jK2fwstAM8aV2GvqzWFpvfnyOGm53kk
CODE_CHALLENGE=elpP-j7DRK-dvxy4GBOzSr4EjnWzwRBquR-mY-ijtT8
```
By using the shell built-in command `eval` as shown below, you can directly
assign the output of the `pkce` script to shell variables.
```shell theme={null}
eval "$(./pkce)"
```
#### Client Attestation Generation
A client attestation can be generated using the
`generate-client-attestation` script. Note that in HAIP,
the `x5c` header parameter is required, so you must use the `--x5c` option to
specify the X.509 certificates to be included in the `x5c` header parameter.
The `--x5c` option can be specified multiple times, and the X.509 certificates
are added to the `x5c` header parameter in the order they are provided.
```shell theme={null}
CLIENT_ATTESTATION=`./generate-client-attestation \
--attester-key=keys/client-attester-private.jwk \
--client-id=${CLIENT_ID} \
--client-key=client.jwk \
--x5c=keys/client-attester-certificate.pem`
```
Below is an example of the header and payload of a generated Client Attestation.
```json theme={null}
{
"typ": "oauth-client-attestation+jwt",
"alg": "ES256",
"x5c": [
"MIIBnTCCAUOgAwIBAgIUZr+BYJKFUDY6CIbWubXMjo1cEH8wCgYIKoZIzj0EAwIw
HzEdMBsGA1UEAwwUQ2xpZW50IEF0dGVzdGVyIFJvb3QwIBcNMjYwNDE0MTc1OTQy
WhgPNDc2NDAzMTExNzU5NDJaMBoxGDAWBgNVBAMMD0NsaWVudCBBdHRlc3RlcjBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABI7riGciOegPsU1MJrSF5CnwImt7Cjzx
YIgSpcLa4woMIxPaeKzhgHf+mi3qSset92VCeQIo0YljKsn6GS057bWjYDBeMAwG
A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBRwEoaaN/q/s+jX
HSE2bybFF82G3zAfBgNVHSMEGDAWgBQ5hrFgDwFhQ8FuCJQETUlyd2pj0jAKBggq
hkjOPQQDAgNIADBFAiEA2YOKXOd7UgtSMbVs0mMlss3RNXnMMK2RF2JlkrHQemIC
IAY6Qx5VxmHwovlc0PJgrCDOQZZRUOKBoedy6AoXLSYh"
],
"kid": "I6NQ3o1a3D2LW4pExAS620B3Amw4pkbNoAcxzkygYhM"
}
{
"sub": "trial_client",
"iat": 1776702629,
"exp": 1776789029,
"cnf": {
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "1AmVr4GoHdPgk48LWdS3T9m6m1mP4VTcj9usoSBnCQk",
"y": "te-WIuUIq2w8tXmXydlEX4pe9lNe-PBcozA8n7y8XTE"
}
}
}
```
Please note the following points:
* The value of the `typ` header parameter is `oauth-client-attestation+jwt`.
* The `x5c` header parameter includes the X.509 certificate for signature verification (as specified with the `--x5c` option).
* The `sub` claim is set to the client identifier (as specified with the `--client-id` option).
* The `cnf.jwk` claim contains the client's key (as specified with the `--client-key` option).
#### Attestation Challenge Retrieval
According to the [ABCA][ABCA] specification, if the authorization server
provides a **challenge endpoint**, the attestation challenge issued from
that endpoint must be embedded in the Client Attestation PoP. Whether the
authorization server provides a challenge endpoint can be determined by
checking if the `challenge_endpoint` parameter is included
in the server metadata.
The challenge endpoint accepts an HTTP POST request and returns JSON containing
an `attestation_challenge` property. Below is an example of
a request and response excerpted from the [ABCA][ABCA] specification.
Attestation Challenge Request Example:
```http theme={null}
POST /as/challenge HTTP/1.1
Host: as.example.com
Accept: application/json
```
Attestation Challenge Response Example:
```http theme={null}
HTTP/1.1 200 OK
Host: as.example.com
Content-Type: application/json
Cache-Control: no-store
{
"attestation_challenge": "AYjcyMzY3ZDhiNmJkNTZ"
}
```
If the URL of the challenge endpoint is stored in a shell variable named
`CHALLENGE_ENDPOINT`, you can assign the value of the
attestation challenge to a shell variable named `CHALLENGE` by executing
the following command.
```shell theme={null}
CHALLENGE=`curl ${CHALLENGE_ENDPOINT} \
-X POST | \
jq -r .attestation_challenge`
```
#### Client Attestation PoP Generation
A client attestation PoP can be generated using the
`generate-client-attestation-pop` script. If you need to
include a `challenge` claim, specify the `--challenge` option.
```shell theme={null}
CLIENT_ATTESTATION_POP=`./generate-client-attestation-pop \
--as-id=${AUTHORIZATION_SERVER} \
--client-key=client.jwk \
--challenge=${CHALLENGE}`
```
Below is an example of the header and payload of a generated Client Attestation
PoP.
```json theme={null}
{
"typ": "oauth-client-attestation-pop+jwt",
"alg": "ES256",
"kid": "7yNhIHVeaPFIB_0k-YpiFATomCLpxIv-e2AAFmCRBLE"
}
{
"aud": "https://trial.authlete.net",
"jti": "tiZaOFhUK0au8RA0",
"iat": 1776704669,
"exp": 1776791069,
"challenge": "HECMLA_LpoE9hSDlVP4yptT2i1zOnOdkIPnVa39rInA"
}
```
Please note the following points:
* The value of the `typ` header parameter is `oauth-client-attestation-pop+jwt`.
* The `aud` claim is set to the authorization server identifier (as specified with the `--as-id` option).
* The `challenge` claim is set to the attestation challenge (as specified with the `--challenge` option).
#### DPoP Proof JWT Generation
Since FAPI2SP states the following, the authorization code must also be
DPoP-bound.
> if using DPoP, shall support "Authorization Code Binding to DPoP Key"
> (as required by [Section 10.1][RFC_9449_10_1] of [RFC9449][RFC_9449]);
This can be achieved either by adding the `dpop_jkt` request parameter to the
request registered at the PAR endpoint, or by including a DPoP Proof JWT.
As noted in the specification, using a DPoP Proof JWT simplifies the
implementation (since the client can consistently include a DPoP Proof JWT in
all requests sent to the authorization server, regardless of the request type).
Therefore, in this article, we will generate a DPoP Proof JWT.
Use the `generate-dpop-proof` script to generate a DPoP Proof JWT by passing
the `-m POST` option (to specify the HTTP method for the PAR request), the
`-u $PAR_ENDPOINT` option (to specify the PAR endpoint URL), and the
`-k client.jwk` option (to specify the client’s key).
```shell theme={null}
DPOP_PROOF=`./generate-dpop-proof \
-m POST \
-u ${PAR_ENDPOINT} \
-k client.jwk`
```
Below is an example of the header and payload of a generated DPoP Proof JWT.
```json theme={null}
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"x": "1AmVr4GoHdPgk48LWdS3T9m6m1mP4VTcj9usoSBnCQk",
"y": "te-WIuUIq2w8tXmXydlEX4pe9lNe-PBcozA8n7y8XTE"
}
}
{
"jti": "zrZOAIoZJvCbEQrM",
"htm": "POST",
"htu": "https://trial.authlete.net/api/par",
"iat": 1776708531
}
```
Please note the following points:
* The value of the `typ` header parameter is `dpop+jwt`.
* The `jwk` header parameter contains the client's key (as specified with the `-k` option).
* The `htm` claim is set to the HTTP method of the PAR request (as specified with the `-m` option).
* The `htu` claim is set to the URL of the PAR endpoint (as specified with the `-u` option).
Some authorization servers require the DPoP Proof JWT to include a
`nonce` claim. In such cases, generate the DPoP Proof JWT with the
`-n` option. For details on the DPoP nonce, see
"DPoP Nonce."
#### PAR Request Submission
Now that the required tokens are ready, send the PAR request.
```shell theme={null}
curl ${PAR_ENDPOINT} \
-H "OAuth-Client-Attestation: ${CLIENT_ATTESTATION}" \
-H "OAuth-Client-Attestation-PoP: ${CLIENT_ATTESTATION_POP}" \
-H "DPoP: ${DPOP_PROOF}" \
-d client_id=${CLIENT_ID} \
-d response_type=code \
-d scope=digital_credential+haip \
-d redirect_uri=${REDIRECT_URI} \
-d code_challenge=${CODE_CHALLENGE} \
-d code_challenge_method=S256
```
The key points of this request are as follows:
| Item | Description |
| :- | :- |
| Client Authentication | When using ABCA, set the Client Attestation and the Client Attestation PoP in the `OAuth-Client-Attestation` and `OAuth-Client-Attestation-PoP` HTTP headers, respectively. |
| Sender-Constrained | Set the DPoP Proof JWT in the `DPoP` HTTP header. |
| `client_id` | The `client_id` request parameter is required in the authorization request. |
| `response_type` | In FAPI2SP, the value of the `response_type` request parameter must be `code` (no other values are allowed). |
| `scope` | To comply with the HAIP specification, the `scope` request parameter must include a scope corresponding to at least one credential configuration. In this example, it is assumed that the string `digital_credential` is configured as the `scope` property of some credential configuration. Additionally, this example includes the `haip` scope. |
| `redirect_uri` | The `redirect_uri` request parameter is required in FAPI2SP. |
| `code_challenge` | Since PKCE is mandatory in FAPI2SP, the `code_challenge` request parameter must be included. |
| `code_challenge_method` | Since FAPI2SP requires the use of `S256` as the code challenge method, `code_challenge_method=S256` must be explicitly included. |
If the PAR request is successful, the PAR endpoint returns JSON containing a
`request_uri` property. Below is an example of a PAR response
excerpted from the PAR specification.
```http theme={null}
HTTP/1.1 201 Created
Cache-Control: no-cache, no-store
Content-Type: application/json
{
"request_uri": "urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2",
"expires_in": 90
}
```
The value of the `request_uri` property is the issued
**request URI**. This request URI will later be used as the value of the
`request_uri` request parameter in the authorization request.
Per FAPI2SP requirements, the lifetime of the request URI must be less than
600 seconds.
### Authorization Request
Send an authorization request to the authorization endpoint of the authorization
server via the browser. At that time, use the request URI issued by the PAR
endpoint as the value of the `request_uri` request parameter.
```
${AUTHORIZATION_ENDPOINT}?client_id=${CLIENT_ID}&request_uri=${REQUEST_URI}
```
When the user completes authentication and grants consent on the authorization
page returned by the authorization endpoint, an **authorization code** is issued.
This authorization code will later be used as the value of the `code` request
parameter in the token request.
Per FAPI2SP requirements, the authorization code has a maximum lifetime of 60
seconds. Since it expires quickly, take care when sending the token request
manually.
### Token Request
#### Tokens Generation
The Client Attestation and Client Attestation PoP are also required for the
token request; however, if they have not yet expired, those created for the
PAR request can be reused.
On the other hand, the DPoP Proof JWT cannot be reused, because the `htu` claim
must be set to the URL of the target endpoint. Therefore, you must regenerate
the DPoP Proof JWT by rerunning the `generate-dpop-proof`
script with the token endpoint URL specified via the `-u` option.
```shell theme={null}
DPOP_PROOF=`./generate-dpop-proof \
-m POST \
-u ${TOKEN_ENDPOINT} \
-k client.jwk`
```
#### Token Request Submission
After preparing the required tokens, send the token request.
```shell theme={null}
curl ${TOKEN_ENDPOINT} \
-H "OAuth-Client-Attestation: ${CLIENT_ATTESTATION}" \
-H "OAuth-Client-Attestation-PoP: ${CLIENT_ATTESTATION_POP}" \
-H "DPoP: ${DPOP_PROOF}" \
-d grant_type=authorization_code \
-d code=${AUTHORIZATION_CODE} \
-d redirect_uri=${REDIRECT_URI} \
-d code_verifier=${CODE_VERIFIER}
```
The key points of this request are as follows:
| Item | Description |
| :- | :- |
| Client Authentication | When using ABCA, set the Client Attestation and the Client Attestation PoP in the `OAuth-Client-Attestation` and `OAuth-Client-Attestation-PoP` HTTP headers, respectively. |
| Sender-Constrained | Set the DPoP Proof JWT in the `DPoP` HTTP header. |
| `grant_type` | Set the value to `authorization_code` to indicate the authorization code flow. |
| `code` | Specify the authorization code issued as a result of the authorization request. |
| `redirect_uri` | Specify the same redirect URI that was included in the PAR request. |
| `code_verifier` | Specify the code verifier corresponding to the code challenge included in the PAR request. |
If the token request is successful, the token endpoint returns JSON containing
an `access_token` property.
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "zdRKYNzJ0hR99ztiBgd8TTXzQhbattjhtSd-NDykt1A",
"token_type": "DPoP",
"expires_in": 86400,
"scope": "digital_credential haip",
"refresh_token": "EiHKJFol1UOm7sa9CaKvlnmDH0TP4_takv1I7iWnHI8"
}
```
The value of the `access_token` property is the issued
**access token**. This access token will later be set in the `Authorization`
HTTP header of the credential request.
### Credential Request
A literal interpretation of the HAIP specification suggests that it is not
strictly required to include a Key Proof in the credential request. However,
since it is difficult to imagine real-world use cases without key binding,
the credential request example shown here includes a Key Proof.
#### Nonce Retrieval
If the credential issuer provides a **nonce endpoint**, the nonce issued by
that endpoint must be included in the Key Proof and Key Attestation. Whether
the credential issuer provides a nonce endpoint can be determined by checking
whether the `nonce_endpoint` parameter is included in the
credential issuer metadata.
The nonce endpoint accepts an HTTP POST request and returns JSON containing a
`c_nonce` property. Below is an example of a request and response excerpted
from the [OID4VCI][OID4VCI] specification.
Nonce Request Example:
```http theme={null}
POST /nonce HTTP/1.1
Host: credential-issuer.example.com
Content-Length: 0
```
Nonce Response Example:
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
DPoP-Nonce: eyJ7S_zG.eyJH0-Z.HX4w-7v
{
"c_nonce": "wKI4LT17ac15ES9bw8ac4"
}
```
If the URL of the nonce endpoint is stored in a shell variable named
`NONCE_ENDPOINT`, you can assign the value of the nonce to a
shell variable named `NONCE` by executing the following command.
```shell theme={null}
NONCE=`curl ${NONCE_ENDPOINT} \
-X POST | \
jq -r .c_nonce`
```
#### Key Attestation Generation
A Key Attestation can be generated using the
`generate-key-attestation` script. Note that in HAIP, the
`x5c` header parameter is mandatory, so you must use the `--x5c` option to
specify the X.509 certificates to be included in the `x5c` header parameter.
The `--x5c` option can be specified multiple times, and the X.509 certificates
are added to the `x5c` header parameter in the order in which they are provided.
```shell theme={null}
KEY_ATTESTATION=`./generate-key-attestation \
--attester-key=keys/key-attester-private.jwk \
--attested-key=client.jwk \
--nonce=${NONCE} \
--x5c=keys/key-attester-certificate.pem`
```
Below is an example of the header and payload of a generated Key Attestation.
```json theme={null}
{
"typ": "key-attestation+jwt",
"alg": "ES256",
"x5c": [
"MIIBmDCCAT2gAwIBAgIUTD2qHZdvCkld8qneVGlwL4l+rWAwCgYIKoZIzj0EAwIw
HDEaMBgGA1UEAwwRS2V5IEF0dGVzdGVyIFJvb3QwIBcNMjYwNDE0MTkwODQ1WhgP
NDc2NDAzMTExOTA4NDVaMBcxFTATBgNVBAMMDEtleSBBdHRlc3RlcjBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABEj5wOUzDlQKX800+V7kanDu8wASHTw6ivrO2HOW
keWGUNXaToM14Z4EtyM/szOZYv0UOvsdXNLI1cnZAOgPp3+jYDBeMAwGA1UdEwEB
/wQCMAAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTCXpsU7JVvhynt3n5r5baJ
xPy21jAfBgNVHSMEGDAWgBRCjxuwwwG/DC9yI5yJ07oD04YvKzAKBggqhkjOPQQD
AgNJADBGAiEAv3aywa9hsZM5d9zV70GHVP9qmbFleq4SZbmQzIBDNHMCIQDqBvWI
G9avgr0k6TpwmzhomOTY1H0JigyGaZzuzBe9yQ=="
],
"kid": "qLgVYBf9lZ63QsEz04r9zb4NN3iZnf2-dnoc2k1GWbA"
}
{
"iat": 1776719761,
"exp": 1776806161,
"attested_keys": [
{
"crv": "P-256",
"kty": "EC",
"x": "1AmVr4GoHdPgk48LWdS3T9m6m1mP4VTcj9usoSBnCQk",
"y": "te-WIuUIq2w8tXmXydlEX4pe9lNe-PBcozA8n7y8XTE"
}
],
"nonce": "8aREnUPLVHJT0gswV8dD91YeTBWEKa4YCAd2HpXcOYw"
}
```
Please note the following points:
* The value of the `typ` header parameter is `key-attestation+jwt`.
* The `x5c` header parameter includes the X.509 certificate for signature verification (as specified with the `--x5c` option).
* The `attested_keys` claim contains the attested key(s) (as specified with the `--attested-key` option).
* The `nonce` claim is set to the nonce (as specified with the `--nonce` option).
#### Key Proof Generation
A Key Proof of the JWT Proof Type can be generated using the
`generate-key-proof` script.
```shell theme={null}
JWT_KEY_PROOF=`./generate-key-proof \
--client-id=${CLIENT_ID} \
--issuer=${CREDENTIAL_ISSUER} \
--key=client.jwk \
--nonce=${NONCE} \
--key-attestation=${KEY_ATTESTATION}`
```
Below is an example of the header and payload of a generated Key Proof.
```json theme={null}
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "1AmVr4GoHdPgk48LWdS3T9m6m1mP4VTcj9usoSBnCQk",
"y": "te-WIuUIq2w8tXmXydlEX4pe9lNe-PBcozA8n7y8XTE"
},
"key_attestation":
"eyJ0eXAiOiJrZXktYXR0ZXN0YXRpb24rand0IiwiYWxnIjoiRVMyNTYiLCJ4NWMi
OlsiTUlJQm1EQ0NBVDJnQXdJQkFnSVVURDJxSFpkdkNrbGQ4cW5lVkdsd0w0bCty
V0F3Q2dZSUtvWkl6ajBFQXdJd0hERWFNQmdHQTFVRUF3d1JTMlY1SUVGMGRHVnpk
R1Z5SUZKdmIzUXdJQmNOTWpZd05ERTBNVGt3T0RRMVdoZ1BORGMyTkRBek1URXhP
VEE0TkRWYU1CY3hGVEFUQmdOVkJBTU1ERXRsZVNCQmRIUmxjM1JsY2pCWk1CTUdC
eXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkVqNXdPVXpEbFFLWDgwMCtWN2th
bkR1OHdBU0hUdzZpdnJPMkhPV2tlV0dVTlhhVG9NMTRaNEV0eU0vc3pPWll2MFVP
dnNkWE5MSTFjblpBT2dQcDMrallEQmVNQXdHQTFVZEV3RUIvd1FDTUFBd0RnWURW
UjBQQVFIL0JBUURBZ2VBTUIwR0ExVWREZ1FXQkJUQ1hwc1U3SlZ2aHludDNuNXI1
YmFKeFB5MjFqQWZCZ05WSFNNRUdEQVdnQlJDanh1d3d3Ry9EQzl5STV5SjA3b0Qw
NFl2S3pBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQXYzYXl3YTloc1pNNWQ5elY3
MEdIVlA5cW1iRmxlcTRTWmJtUXpJQkROSE1DSVFEcUJ2V0lHOWF2Z3IwazZUcHdt
emhvbU9UWTFIMEppZ3lHYVp6dXpCZTl5UT09Il0sImtpZCI6InFMZ1ZZQmY5bFo2
M1FzRXowNHI5emI0Tk4zaVpuZjItZG5vYzJrMUdXYkEifQ.eyJpYXQiOjE3NzY3M
Tk3NjEsImV4cCI6MTc3NjgwNjE2MSwiYXR0ZXN0ZWRfa2V5cyI6W3siY3J2IjoiU
C0yNTYiLCJrdHkiOiJFQyIsIngiOiIxQW1WcjRHb0hkUGdrNDhMV2RTM1Q5bTZtM
W1QNFZUY2o5dXNvU0JuQ1FrIiwieSI6InRlLVdJdVVJcTJ3OHRYbVh5ZGxFWDRwZ
TlsTmUtUEJjb3pBOG43eThYVEUifV0sIm5vbmNlIjoiOGFSRW5VUExWSEpUMGdzd
1Y4ZEQ5MVllVEJXRUthNFlDQWQySHBYY09ZdyJ9.CeDXPV9gfr2x92IHSZ5BcyFf
RuTK2M5Y4JeHFgijfZeNBytz1QICaxnLOVTZjXYu-JlL21_xkPoODrWTSiDPLg"
}
{
"iss": "trial_client",
"aud": "https://trial.authlete.net",
"iat": 1776720642,
"nonce": "8aREnUPLVHJT0gswV8dD91YeTBWEKa4YCAd2HpXcOYw"
}
```
Please note the following points:
* The value of the `typ` header parameter is `openid4vci-proof+jwt`.
* The `jwk` header parameter contains the client's key (as specified with the `--key` option).
* The `key_attestation` header parameter contains the Key Attestation (as specified with the `--key-attestation` option).
* The `iss` claim is set to the client identifier (as specified with the `--client-id` option).
* The `aud` claim is set to the credential issuer identifier (as specified with the `--issuer` option).
* The `nonce` claim is set to the nonce (as specified with the `--nonce` option).
#### DPoP Proof JWT Generation
Re-run the `generate-dpop-proof` script with the
credential endpoint URL specified via the `-u` option to regenerate the DPoP
Proof JWT.
Note that in the credential request, the DPoP Proof JWT is sent together with
the access token, so the DPoP Proof JWT must include an `ath` claim. Therefore,
when running the `generate-dpop-proof` script, add the
`-a` option.
```shell theme={null}
DPOP_PROOF=`./generate-dpop-proof \
-m POST \
-u ${CREDENTIAL_ENDPOINT} \
-k client.jwk \
-a ${ACCESS_TOKEN}`
```
#### Credential Request Submission
Now that the Key Proof has been prepared, send the credential request. In this
example, it is assumed that the identifier of the credential configuration
associated with the `digital_credential` scope is
`DigitalCredential`.
```shell theme={null}
curl ${CREDENTIAL_ENDPOINT} \
-H "Authorization: DPoP ${ACCESS_TOKEN}" \
-H "DPoP: ${DPOP_PROOF}" \
--json '{
"credential_configuration_id": "DigitalCredential",
"proofs": {
"jwt": ["'${JWT_KEY_PROOF}'"]
}
}'
```
The key points of this request are as follows:
| Item | Description |
| :- | :- |
| Access Token | Set the access token issued by the token endpoint in the `Authorization` HTTP header. Since it is DPoP-bound, the scheme must be `DPoP`. |
| Sender-Constrained | Set the DPoP Proof JWT in the `DPoP` HTTP header. |
| `credential_configuration_id` | Either `credential_configuration_id` or `credential_identifier` is required in the credential request. |
| `proofs` | Specify the Key Proof |
In the request above, a key proof using the JWT Proof Type was used, and
therefore the Key Attestation was embedded in a separate JWT.
In contrast, with a Key Proof using the Attestation Proof Type, the Key
Attestation can be used directly as the Key Proof, as shown below.
```shell theme={null}
curl ${CREDENTIAL_ENDPOINT} \
-H "Authorization: DPoP ${ACCESS_TOKEN}" \
-H "DPoP: ${DPOP_PROOF}" \
--json '{
"credential_configuration_id": "DigitalCredential",
"proofs": {
"attestation": ["'${KEY_ATTESTATION}'"]
}
}'
```
The following is an example of a credential response.
```json theme={null}
{
"credentials": [
{
"credential":
"eyJ4NWMiOlsiTUlJQ1NEQ0NBZTZnQXdJQkFnSVVLSlM1R21Ram5mY0JrM1piL2VX
K0loblFrOHN3Q2dZSUtvWkl6ajBFQXdJd1pURUxNQWtHQTFVRUJoTUNTbEF4RGpB
TUJnTlZCQWdNQlZSdmEzbHZNUkF3RGdZRFZRUUhEQWREYUdsNWIyUmhNUmN3RlFZ
RFZRUUtEQTVCZFhSb2JHVjBaU3dnU1c1akxqRWJNQmtHQTFVRUF3d1NkSEpwWVd3
dVlYVjBhR3hsZEdVdWJtVjBNQ0FYRFRJMU1ESXlOVEExTXpJME9Wb1lEekl5T1Rn
eE1qRXhNRFV6TWpRNVdqQmxNUXN3Q1FZRFZRUUdFd0pLVURFT01Bd0dBMVVFQ0F3
RlZHOXJlVzh4RURBT0JnTlZCQWNNQjBOb2FYbHZaR0V4RnpBVkJnTlZCQW9NRGtG
MWRHaHNaWFJsTENCSmJtTXVNUnN3R1FZRFZRUUREQkowY21saGJDNWhkWFJvYkdW
MFpTNXVaWFF3V1RBVEJnY3Foa2pPUFFJQkJnZ3Foa2pPUFFNQkJ3TkNBQVFPZDZU
Q3ducG5tWHFwczhSUUhxK0s5Tm9vRmJyamIxeXlGeEpsSGs3V2ZUVk9yWkR1OU5x
K0lPYzl5cm83eStHOXJUZjB6dDhPV0o1aS9XaWpqSUxUbzNvd2VEQWRCZ05WSFE0
RUZnUVVQTVlhNmZRSlA2TTZ4NHRZTTFmYmYzL0UweE13SHdZRFZSMGpCQmd3Rm9B
VVBNWWE2ZlFKUDZNNng0dFlNMWZiZjMvRTB4TXdEd1lEVlIwVEFRSC9CQVV3QXdF
Qi96QWxCZ05WSFJFRUhqQWNoaHBvZEhSd2N6b3ZMM1J5YVdGc0xtRjFkR2hzWlhS
bExtNWxkREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBeW8zQS9vUVgvTWU4bXlN
V0wwMjVjVEJ3L2tlY2VIRUxmU1FIbWxlNVFBRUNJQzVnL3luT210L251a3NmSEFl
TUFCZjd0bzUyelRSa1JkdXFRQ1pITzNlWSJdLCJraWQiOiJaWUdJT0hZdUE5SXBV
aWpWd1FOdWwzbkU1MzZ4MUpTV0hpT2ZkUzdzYWRnIiwidHlwIjoiZGMrc2Qtand0
IiwiYWxnIjoiRVMyNTYifQ.eyJfc2QiOlsiM005WU43VXk0RHI0OFdCNEFhNmFEQ
2NudUhxdi1lRUw2RVFITjBMb3ExbyIsIks2UVZHUmVyOVFId2tDYkpoNW1PVUNLO
UwtX0luM0pPX2o2eUlQNzVJaGMiLCJQQ1M5dFBvMXlrb2NmV09iYzd0LUVObXU1M
HdzekY3UTNxZ0MyTFN3dmhRIiwiUS1tdllZWFpfVk9SZlc2ekhXSnhaeC1fNUxwM
EtNbG1lbGJPSUQ5Z3NyNCIsIlFfTjRKOHJnTnV5QS1Oa2RTVmdNR3pvN0RVM3lrW
Dk5dk1zVEJzcklLN28iLCJYcjF6RnFETU5kdXp2UDFBZFZqWFV4WlQ5T1RJRW9kd
TlSRmZ1c2FFYUJJIiwiWVNrSUNPRXI1dzZCY3lEQmVwck5sLXhGMmdheExtOGRaZ
lp1NVRUcFpiUSIsIll4LXFYdi10a3Babnd6MkJkN1pyOXhlaDBGOE05bnJzdUkxT
DdvaGlTNEkiLCJkS1pEUDhwYzdLbXlXNkVwSER2TVIxRnd0aDNKN0xoeldNRWgyW
kh3UXRRIiwiZUFPQ1l0VmN0bVllNnF6eEJvOUh4TE9panZaSlRKSjBBR2pHblJrW
WdsTSJdLCJ2Y3QiOiJodHRwczovL2NyZWRlbnRpYWxzLmV4YW1wbGUuY29tL2RpZ
2l0YWxfY3JlZGVudGlhbCIsIl9zZF9hbGciOiJzaGEtMjU2IiwiaXNzIjoiaHR0c
HM6Ly90cmlhbC5hdXRobGV0ZS5uZXQiLCJjbmYiOnsiandrIjp7Imt0eSI6IkVDI
iwiY3J2IjoiUC0yNTYiLCJraWQiOiJPUmduOXZ5S2ZKZnI2SmljN1dtaVRFV0pCb
FRZa1QwZS1JSU9NU3l2SXRvIiwieCI6IjFBbVZyNEdvSGRQZ2s0OExXZFMzVDltN
m0xbVA0VlRjajl1c29TQm5DUWsiLCJ5IjoidGUtV0l1VUlxMnc4dFhtWHlkbEVYN
HBlOWxOZS1QQmNvekE4bjd5OFhURSJ9fSwiZXhwIjoxNzc5MzEzOTg5LCJpYXQiO
jE3NzY3MjE5ODl9.k_BY_BMZVLgNRqutBakVGgpcfq5DbbQdgGsGnTvDWRqZX4KN
wbzEJd6bHZhIC7Di5_3QS_sUvByHnDpa95NJTw~WyIwbHlrWHpiLTNZR0kwZndVa
ktMZk5BIiwic3ViIiwiMTAwNCJd~WyJ3Vi04VWk0MUp3eEpmYzN3WnQyaFRRIiwi
Z2l2ZW5fbmFtZSIsIkluZ2EiXQ~WyJFWlZRUGI0ZFJzY01xa1dheEJmel9nIiwiZ
mFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd~WyJrT0ZrLVRFMzVlRlpRVzBrQnpR
V3FnIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd~"
}
]
}
```
Each element of the `credentials` array is a JSON object, and the value of its
`credential` property represents the issued VC. In this example, only one VC is
issued, and its format is [SD-JWT VC][SD_JWT_VC].
#### SD-JWT VC
Decoding the header and payload of the Issuer-signed JWT part of the issued
[SD-JWT VC][SD_JWT_VC] yields the following.
```json theme={null}
{
"x5c": [
"MIICSDCCAe6gAwIBAgIUKJS5GmQjnfcBk3Zb/eW+IhnQk8swCgYIKoZIzj0EAwIw
ZTELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRva3lvMRAwDgYDVQQHDAdDaGl5b2Rh
MRcwFQYDVQQKDA5BdXRobGV0ZSwgSW5jLjEbMBkGA1UEAwwSdHJpYWwuYXV0aGxl
dGUubmV0MCAXDTI1MDIyNTA1MzI0OVoYDzIyOTgxMjExMDUzMjQ5WjBlMQswCQYD
VQQGEwJKUDEOMAwGA1UECAwFVG9reW8xEDAOBgNVBAcMB0NoaXlvZGExFzAVBgNV
BAoMDkF1dGhsZXRlLCBJbmMuMRswGQYDVQQDDBJ0cmlhbC5hdXRobGV0ZS5uZXQw
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQOd6TCwnpnmXqps8RQHq+K9NooFbrj
b1yyFxJlHk7WfTVOrZDu9Nq+IOc9yro7y+G9rTf0zt8OWJ5i/WijjILTo3oweDAd
BgNVHQ4EFgQUPMYa6fQJP6M6x4tYM1fbf3/E0xMwHwYDVR0jBBgwFoAUPMYa6fQJ
P6M6x4tYM1fbf3/E0xMwDwYDVR0TAQH/BAUwAwEB/zAlBgNVHREEHjAchhpodHRw
czovL3RyaWFsLmF1dGhsZXRlLm5ldDAKBggqhkjOPQQDAgNIADBFAiEAyo3A/oQX
/Me8myMWL025cTBw/keceHELfSQHmle5QAECIC5g/ynOmt/nuksfHAeMABf7to52
zTRkRduqQCZHO3eY"
],
"kid": "ZYGIOHYuA9IpUijVwQNul3nE536x1JSWHiOfdS7sadg",
"typ": "dc+sd-jwt",
"alg": "ES256"
}
{
"_sd": [
"3M9YN7Uy4Dr48WB4Aa6aDCcnuHqv-eEL6EQHN0Loq1o",
"K6QVGRer9QHwkCbJh5mOUCK9L-_In3JO_j6yIP75Ihc",
"PCS9tPo1ykocfWObc7t-ENmu50wszF7Q3qgC2LSwvhQ",
"Q-mvYYXZ_VORfW6zHWJxZx-_5Lp0KMlmelbOID9gsr4",
"Q_N4J8rgNuyA-NkdSVgMGzo7DU3ykX99vMsTBsrIK7o",
"Xr1zFqDMNduzvP1AdVjXUxZT9OTIEodu9RFfusaEaBI",
"YSkICOEr5w6BcyDBeprNl-xF2gaxLm8dZfZu5TTpZbQ",
"Yx-qXv-tkpZnwz2Bd7Zr9xeh0F8M9nrsuI1L7ohiS4I",
"dKZDP8pc7KmyW6EpHDvMR1Fwth3J7LhzWMEh2ZHwQtQ",
"eAOCYtVctmYe6qzxBo9HxLOijvZJTJJ0AGjGnRkYglM"
],
"vct": "https://credentials.example.com/digital_credential",
"_sd_alg": "sha-256",
"iss": "https://trial.authlete.net",
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"kid": "ORgn9vyKfJfr6Jic7WmiTEWJBlTYkT0e-IIOMSyvIto",
"x": "1AmVr4GoHdPgk48LWdS3T9m6m1mP4VTcj9usoSBnCQk",
"y": "te-WIuUIq2w8tXmXydlEX4pe9lNe-PBcozA8n7y8XTE"
}
},
"exp": 1779313989,
"iat": 1776721989
}
```
Please note the following points:
* The value of the `typ` header parameter is `dc+sd-jwt`.
* The `_sd` claim contains a list of SHA-256 digest values of the disclosure set (note that it also includes fake digest values).
* The `_sd_alg` claim indicates the hash algorithm used to compute the disclosure digests.
* The required `vct` claim for SD-JWT VC is included.
* The `cnf.jwk` claim contains the client key specified in the Key Proof.
The following shows information about the disclosure set.
| Disclosure | `WyIwbHlrWHpiLTNZR0kwZndVaktMZk5BIiwic3ViIiwiMTAwNCJd` |
| :- | :- |
| Digest | `eAOCYtVctmYe6qzxBo9HxLOijvZJTJJ0AGjGnRkYglM` |
| Salt | `"0lykXzb-3YGI0fwUjKLfNA"` |
| Claim Name | `"sub"` |
| Claim Value | `"1004"` |
| Disclosure | `WyJ3Vi04VWk0MUp3eEpmYzN3WnQyaFRRIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ` |
| :- | :- |
| Digest | `3M9YN7Uy4Dr48WB4Aa6aDCcnuHqv-eEL6EQHN0Loq1o` |
| Salt | `"wV-8Ui41JwxJfc3wZt2hTQ"` |
| Claim Name | `"given_name"` |
| Claim Value | `"Inga"` |
| Disclosure | `WyJFWlZRUGI0ZFJzY01xa1dheEJmel9nIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd` |
| :- | :- |
| Digest | `Q-mvYYXZ_VORfW6zHWJxZx-_5Lp0KMlmelbOID9gsr4` |
| Salt | `"EZVQPb4dRscMqkWaxBfz_g"` |
| Claim Name | `"family_name"` |
| Claim Value | `"Silverstone"` |
| Disclosure | `WyJrT0ZrLVRFMzVlRlpRVzBrQnpRV3FnIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd` |
| :- | :- |
| Digest | `Yx-qXv-tkpZnwz2Bd7Zr9xeh0F8M9nrsuI1L7ohiS4I` |
| Salt | `"kOFk-TE35eFZQW0kBzQWqg"` |
| Claim Name | `"birthdate"` |
| Claim Value | `"1991-11-06"` |
## Certificate Chain Validation
In HAIP, both the Client Attestation and the Key Attestation must include the
`x5c` header parameter. When an authorization server or credential issuer
receives such an attestation, it verifies that the certificate chain specified
in the `x5c` header can be traced back to one of the trusted root certificates.
However, HAIP does not define which root certificate set must be used for this
validation.
That said, given that HAIP is being developed with the EUDI Wallet in mind, it
is reasonable to expect that HAIP deployments will be required to use root
certificates designated by EU regulatory authorities. In such a context, a
general-purpose HAIP implementation should provide a mechanism to configure the
set of root certificates used for validation.
Accordingly, Authlete has introduced the following set of service properties.
| Property Name | Description |
| :- | :- |
| `clientAttesterRoots` | OAuth 2.0 Attestation-Based Client Authentication defines Client Attestation. If a Client Attestation includes the `x5c` header parameter, Authlete validates the specified certificate chain. During validation, the X.509 certificates specified in this property (`clientAttesterRoots`) are used as trusted root certificates, but only when explicitly enabled. Specifically, this occurs only when the `clientAttesterRootsEnabled` property is set to `true`.
If validation fails with the configured root certificates, validation is performed using the system-installed root certificates. However, if configured not to use the system root certificates—specifically when the `clientAttesterRootsOnly` property is set to `true`—the system root certificates are not used.
The certificate format for this property must be PEM. PEM markers (`-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`) are optional. When Authlete returns the value of this property via its APIs, PEM markers are always included even if they were omitted when configured.
OAuth 2.0 Attestation-Based Client Authentication does not require the `x5c` header parameter in Client Attestation. However, OpenID4VC High Assurance Interoperability Profile 1.0 requires it, so care must be taken. If a value is set in the service's `haipVersion` property or the client's `haipVersion` property, or if a request includes a scope with the `haip` attribute, HAIP is enabled, and consequently the `x5c` header parameter in Client Attestation becomes mandatory. |
| `clientAttesterRootsEnabled` | Configure whether to use the trusted root certificates for validating the certificate chain specified in the `x5c` header parameter of Client Attestation. These certificates are defined in the `clientAttesterRoots` property. Unless this property (`clientAttesterRootsEnabled`) is explicitly enabled (set to `true`), the configured root certificates will not be used even if they are specified. |
| `clientAttesterRootsOnly` | Configure whether to validate the certificate chain specified in the `x5c` header parameter of Client Attestation using only the dedicated root certificates defined in the `clientAttesterRoots` property. When this property (`clientAttesterRootsOnly`) is enabled (set to `true`), system-installed root certificates are not used for validation. |
| `keyAttesterRoots` | OpenID for Verifiable Credential Issuance 1.0, Appendix D. Key Attestations defines Key Attestation. If a Key Attestation includes the `x5c` header parameter, Authlete validates the specified certificate chain. During validation, the X.509 certificates specified in this property (`keyAttesterRoots`) are used as trusted root certificates, but only when explicitly enabled. Specifically, this occurs only when the `keyAttesterRootsEnabled` property is set to `true`.
If validation fails with the configured root certificates, validation is performed using the system-installed root certificates. However, if configured not to use the system root certificates—specifically when the `keyAttesterRootsOnly` property is set to `true`—the system root certificates are not used.
The certificate format for this property must be PEM. PEM markers (`-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`) are optional. When Authlete returns the value of this property via its APIs, PEM markers are always included even if they were omitted when configured.
OpenID for Verifiable Credential Issuance 1.0 does not require the `x5c` header parameter in Key Attestation. However, OpenID4VC High Assurance Interoperability Profile 1.0 requires it, so care must be taken. If a value is set in the service's `haipVersion` property or the client's `haipVersion` property, or if a request includes a scope with the `haip` attribute, HAIP is enabled, and consequently the `x5c` header parameter in Key Attestation becomes mandatory. |
| `keyAttesterRootsEnabled` | Configure whether to use the trusted root certificates for validating the certificate chain specified in the `x5c` header parameter of Key Attestation. These certificates are defined in the `keyAttesterRoots` property. Unless this property (`keyAttesterRootsEnabled`) is explicitly enabled (set to `true`), the configured root certificates will not be used even if they are specified. |
| `keyAttesterRootsOnly` | Configure whether to validate the certificate chain specified in the `x5c` header parameter of Key Attestation using only the dedicated root certificates defined in the `keyAttesterRoots` property. When this property (`keyAttesterRootsOnly`) is enabled (set to `true`), system-installed root certificates are not used for validation. |
The following is an example configuration of the root certificate set used to
validate the certificate chains of Client Attestations and Key Attestations.
```json theme={null}
{
"clientAttesterRoots": [
"-----BEGIN CERTIFICATE-----\n
MIIBpTCCAUugAwIBAgIUUTw+Ep5ZOdplQAjzE/68Z9IUzzgwCgYIKoZIzj0EAwIw\n
HzEdMBsGA1UEAwwUQ2xpZW50IEF0dGVzdGVyIFJvb3QwIBcNMjYwNDE0MTU0MzQ1\n
WhgPNDc2NDAzMTExNTQzNDVaMB8xHTAbBgNVBAMMFENsaWVudCBBdHRlc3RlciBS\n
b290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyRlNlUPzTt6jF5s0ltIjoGHx\n
SFQu6GE+gZOMm5sOrNGmAaI6i48CSI6yrXpr/F0KV6t4IH9FcKsZWrpIA7evz6Nj\n
MGEwHQYDVR0OBBYEFDmGsWAPAWFDwW4IlARNSXJ3amPSMB8GA1UdIwQYMBaAFDmG\n
sWAPAWFDwW4IlARNSXJ3amPSMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD\n
AgEGMAoGCCqGSM49BAMCA0gAMEUCIQCXxFHg356YWT2gEIdU0Vdm2znUMRwm7Wgb\n
Jf9GbLDvggIgK98PHmvP8WRqPfQr1cNpSSBoogDhZhLcelvm/L0coLA=\n
-----END CERTIFICATE-----"
],
"clientAttesterRootsEnabled": true,
"clientAttesterRootsOnly": false,
"keyAttesterRoots": [
"-----BEGIN CERTIFICATE-----\n
MIIBoDCCAUWgAwIBAgIUWRZNOcKwPKKFYiMHguS6ZS81IPUwCgYIKoZIzj0EAwIw\n
HDEaMBgGA1UEAwwRS2V5IEF0dGVzdGVyIFJvb3QwIBcNMjYwNDE0MTkwNzUxWhgP\n
NDc2NDAzMTExOTA3NTFaMBwxGjAYBgNVBAMMEUtleSBBdHRlc3RlciBSb290MFkw\n
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7RNVRube8DDaZ1zYjBe3vPQFB8WVVqJ/\n
AAIxUd2HdgLd+7x6EU1T7aHQ5r/ARQpLeYsqiou02jwQoDW+rtNozaNjMGEwHQYD\n
VR0OBBYEFEKPG7DDAb8ML3IjnInTugPThi8rMB8GA1UdIwQYMBaAFEKPG7DDAb8M\n
L3IjnInTugPThi8rMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoG\n
CCqGSM49BAMCA0kAMEYCIQCtbIlRnFUs7QSuQZkv6NCxiK0Ui8V/P+gcq/70nWBk\n
yQIhAOowEiHzVvY1iTGYJ2at2Z1UigC3rq2q/E2A70AS8gc5\n
-----END CERTIFICATE-----"
],
"keyAttesterRootsEnabled": true,
"keyAttesterRootsOnly": false
}
```
## HAIP Activation
The method for determining whether to perform HAIP validation on requests to
the authorization server or credential issuer is implementation-dependent.
Authlete provides the following three methods.
| Activation Method | Description |
| :- | :- |
| Service's `haipVersion` property | If a value is set for the service's `haipVersion` property, HAIP validation is always performed for requests to that service. |
| Client's `haipVersion` property | If a value is set for the client's `haipVersion` property, HAIP validation is always performed for requests from that client. |
| Scope's `haip` attribute | If a request includes a scope whose `haip` attribute has a valid value, HAIP validation is performed for that request. |
As of April 2026, in the current Authlete implementation, the only value that
can be set for the `haipVersion` property and the `haip`
attribute is the string `1.0`.
## Conclusion
[HAIP][HAIP] is a profile designed to enhance interoperability and security
across VC-related specifications. The HAIP-related features introduced in
this document are available in Authlete version 3.0.31 and later. For further
details, please contact us via the [contact form][CONTACT].
[ABCA]: https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/
[CONTACT]: https://www.authlete.com/contact
[FAPI2SP]: https://openid.net/specs/fapi-security-profile-2_0.html
[HAIP]: https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html
[OID4VCI]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
[OID4VCI_D]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-D
[OID4VCI_DEMO]: https://github.com/authlete/oid4vci-demo/
[OID4VCI_F1]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-F.1
[OID4VCI_F3]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-F.3
[RFC_7523]: https://www.rfc-editor.org/rfc/rfc7523.html
[RFC_7636]: https://www.rfc-editor.org/rfc/rfc7636.html
[RFC_8705]: https://www.rfc-editor.org/rfc/rfc8705.html
[RFC_9126]: https://www.rfc-editor.org/rfc/rfc9126.html
[RFC_9449]: https://www.rfc-editor.org/rfc/rfc9449.html
[RFC_9449_10_1]: https://www.rfc-editor.org/rfc/rfc9449.html#section-10.1
[SD_JWT_VC]: https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/
# How to use FAPI feature
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/how-to-use-fapi
How to tell Authlete which FAPI profile to apply, and the service, client and request settings each FAPI 1.0 profile needs.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/compliance-profiles/how-to-use-fapi).
## Overview
This document explains how to use the **FAPI** feature in Authlete.
Authlete supports FAPI 1.0 and FAPI 2.0, and the way you tell Authlete which profile to apply is the same for both.
### Where each profile is documented
| Profile | See |
| - | - |
| FAPI 1.0 | A tutorial covering the whole setup: [Financial-grade API (FAPI) Basics](/protocols-and-flows/compliance-profiles/fapi-basics) The checks FAPI mode adds: [Validation in FAPI mode](/protocols-and-flows/compliance-profiles/validation-in-fapi-mode) |
| FAPI 2.0 | Overview: [FAPI 2.0](/protocols-and-flows/compliance-profiles/fapi-2-0) [FAPI 2.0 Security Profile](/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-security-profile-with-authlete) [FAPI 2.0 Message Signing: Authorization Requests](/protocols-and-flows/compliance-profiles/fapi-2-0-message-signing-profile-signing-authorization-requests) [FAPI 2.0 Message Signing: Authorization Responses](/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-message-signing-profile-signing-authorization-responses) |
## Introduction
### FAPI support in Authlete
Authlete supports both **FAPI 1.0** and **FAPI 2.0**.
Each of them defines several security profiles. The requirements an authorization server has to meet differ from profile to profile, so the server behaves differently depending on which one applies.
To use the FAPI feature, configure your service and client to meet the requirements of the profile you want, then tell Authlete which profile to apply.
### How to specify a profile
There are two ways.
| Method | Where it is set | What it covers |
| - | - | - |
| Static configuration | **FAPI Modes** on the service or the client | The whole service, or every request from that client |
| Scope attributes | The attributes of a scope | One request at a time, decided by the scopes it carries |
A static configuration applies the same profile to every request of that service or client. To host clients with different conformance levels on one authorization server, set **FAPI Modes** per client, or use scope attributes.
**FAPI Modes** is a service and client setting added in Authlete 3.0. Authlete 2.x has scope attributes only.
### The order in which Authlete resolves the profile
Authlete decides the profile per request in the following order. The first condition that matches wins, and nothing below it is evaluated.
1. If the FAPI feature is not enabled on the Authlete server, the request follows plain OAuth 2.0 / OpenID Connect.
2. If **FAPI Profile** is not enabled on the service, the request follows plain OAuth 2.0 / OpenID Connect.
3. If the service has **FAPI Modes** set, its value applies.
4. If the client has **FAPI Modes** set, its value applies.
5. Otherwise the attributes of the scopes carried by, or associated with, the request apply.
6. If none of the above matches, the request follows plain OAuth 2.0 / OpenID Connect.
Setting **FAPI Modes** on the service makes every request to that service use that mode, and neither the client's **FAPI Modes** nor any scope attribute is consulted. Leave the service-level mode unset if you need per-client conformance levels.
### FAPI modes and their scope attributes
| FAPI mode | Scope attribute key | Scope attribute value | Profile applied |
| - | - | - | - |
| FAPI 1.0 Baseline | `fapi` | `r` | FAPI 1.0 Part 1 (read-only API) |
| FAPI 1.0 Advanced | `fapi` | `rw` | FAPI 1.0 Part 2 (read-and-write API) |
| FAPI 2.0 Security Profile | `fapi2` | `sp` | FAPI 2.0 Security Profile |
| FAPI 2.0 Message Signing - Auth Request | `fapi2` | `ms-authreq` | FAPI 2.0 Message Signing Profile 5.3 |
| FAPI 2.0 Message Signing - Auth Response | `fapi2` | `ms-authres` | FAPI 2.0 Message Signing Profile 5.4 |
| FAPI 2.0 Message Signing - Introspection Response | `fapi2` | `ms-introspectionres` | FAPI 2.0 Message Signing Profile 5.5 |
| FAPI 2.0 Message Signing - Resource Request | `fapi2` | `ms-rscreq` | FAPI 2.0 Message Signing Profile 5.6.1 |
| FAPI 2.0 Message Signing - Resource Response | `fapi2` | `ms-rscres` | FAPI 2.0 Message Signing Profile 5.6.2 |
The `fapi2` values `baseline`, `advanced-jar` and `advanced-jarm` are legacy and now deprecated. Use the values in the table above for new configurations.
### When scope attributes point at more than one profile
Scope attributes are collected from **every** scope carried by, or associated with, the request, so one request can carry attributes belonging to different profiles.
Authlete then resolves them in this order. The first group that matches wins, and nothing below it is evaluated.
| Rank | Group | Matching scope attributes |
| - | - | - |
| 1 | FAPI 2.0 Message Signing | `fapi2` = `ms-authreq` / `ms-authres` / `ms-introspectionres` / `ms-rscreq` / `ms-rscres` (the deprecated `advanced-jar` counts as authorization request signing, `advanced-jarm` as authorization response signing) |
| 2 | FAPI 2.0 Security Profile | `fapi2` = `sp` (including the deprecated `baseline`) |
| 3 | FAPI 1.0 Advanced | `fapi` = `rw` |
| 4 | FAPI 1.0 Baseline | `fapi` = `r` |
**Only the members of the winning group apply together.** FAPI 2.0 message signing can apply several requirements at once if several of its attributes are present. Ranks 2 to 4 are mutually exclusive — exactly one of them is chosen.
For example, on a request where one scope carries `fapi` = `rw` and another carries `fapi2` = `sp`, only the FAPI 2.0 Security Profile applies; the FAPI 1.0 Advanced attribute is ignored.
Groups cannot be combined. If a single scope attribute pointing at FAPI 2.0 message signing is present, `fapi2` = `sp` and `fapi` = `rw` on the same request are not evaluated.
### Where the FAPI settings live in the console
The service settings are on the **Endpoints > Advanced > FAPI** tab of the service. **FAPI Profile** enables the FAPI profiles for the service, and **FAPI Modes** selects the mode that applies to it. Choosing **FAPI 2.0 Message Signing** there makes **FAPI 2.0 Message Signing Requirements** available, where you pick which requirements apply.
*The FAPI tab of the service settings*
The client settings are on the **Endpoints > Advanced > FAPI** tab of the client, which carries **FAPI 1.0 Modes** and **FAPI 2.0 Modes**.
*The FAPI tab of the client settings*
Scope attributes are set on the **Tokens and Claims > Advanced > Scope** tab of the service.
### Which FAPI modes can be combined
Only the FAPI 2.0 message signing modes can be selected together. Each of the following can only be selected on its own:
* FAPI 1.0 Baseline
* FAPI 1.0 Advanced
* FAPI 2.0 Security Profile
So **FAPI 2.0 Message Signing - Auth Request** and **FAPI 2.0 Message Signing - Auth Response** can be selected together and both take effect, while **FAPI 2.0 Security Profile** and **FAPI 2.0 Message Signing - Auth Request** cannot.
For the settings each FAPI 1.0 profile needs, see [Configuring FAPI 1.0](/protocols-and-flows/compliance-profiles/fapi-1-0-configuration). For FAPI 2.0, see the pages listed under "Where each profile is documented" above.
# Supporting FAPI 2.0 Message Signing Profile: Signing Authorization Responses with Authlete
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-message-signing-profile-signing-authorization-responses
Supporting FAPI 2.0 Message Signing Profile: Signing Authorization Responses with Authlete.
This page is for **Authlete 3.x**. For 2.x documentation, see [this page](/v2/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-message-signing-profile-signing-authorization-responses).
## Overview
This article explains how to support [FAPI 2.0 Message Signing Profile: Signing Authorization Responses](https://openid.net/specs/fapi-message-signing-2_0-final.html#section-5.4) (hereinafter referred to as FAPI2 authorization response signing) and in Authlete.
"FAPI2 authorization response signing" is available since Authlete 2.3.
"FAPI2 authorization response signing” requires satisfying all requirements of FAPI 2.0 Security Profile.
## Preconditions
* This document uses Authorization Code Flow compliant with FAPI2 authorization response signing as an example.
* `private_key_jwt` is used for client authentication.
* DPoP is used for sender-constrained access tokens.
## Service Settings
Configure your service as follows.
### ☑️ 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 > Token > Assertion Audience Mode
Enable this setting.
> "FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements"
>
>
8. shall only accept its issuer identifier value (as defined in \[RFC8414]) as a string in the aud claim received in client authentication assertions;
### ☑️ 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;
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 > ID Token
Set an allowable clock skew value that meets the following condition:
```
10 seconds ≤ clock skew ≤ 60 seconds
```
> "FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements"
>
>
13. to accommodate clock offsets, shall accept JWTs with an iat or nbf timestamp between 0 and 10 seconds in the future but shall reject JWTs with an iat or nbf timestamp greater than 60 seconds in the future. See Note 3 for further details and rationale;
>
> ...
>
> NOTE 3: Clock skew is a cause of many interoperability issues. Even a few hundred milliseconds of clock skew can cause JWTs to be rejected for being “issued in the future”. The DPoP specification \[RFC9449] suggests that JWTs are accepted in the reasonably near future (on the order of seconds or minutes). This document goes further by requiring authorization servers to accept JWTs that have timestamps up to 10 seconds in the future. 10 seconds was chosen as a value that does not affect security while greatly increasing interoperability. Implementers are free to accept JWTs with a timestamp of up to 60 seconds in the future. Some ecosystems have found that the value of 30 seconds is needed to fully eliminate clock skew issues. To prevent implementations switching off iat and nbf checks completely this document imposes a maximum timestamp in the future of 60 seconds.
### ☑️ Tokens and Claims > Advanced > Supported Scopes
Create a scope named `myscope` and add a scope attribute with the key `fapi2` and the value `ms-authres`.
## Client Configurations
Configure your client as follows.
### ☑️ 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];
### ☑️ Endpoints > Token > 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
>
> * adhere to \[RFC8725];
> * use `PS256`, `ES256`, or `EdDSA` (using the `Ed25519` variant) algorithms; and
> * 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"
>
>
2. RSA keys shall have a minimum length of 2048 bits.
>
>
3. Elliptic curve keys shall have a minimum length of 224 bits.
## Demonstration
This section demonstrates Authorization Code Flow in FAPI2 authorization response signing by using Authlete APIs.
### 1. Pushed Authorization Request & Response
Suppose that a client that implements FAPI2 authorization response signing 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=ms-authres` 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 to an appropriate value 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 demonstration, 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];
### ☑️ Response Mode
The `response_mode` parameter must be set to `jwt`.
> "FAPI 2.0 Message Signing Profile, 5.4. Signing authorization responses, 5.4.1. Requirements for Authorization Servers"
>
> Authorization servers implementing FAPI2 authorization response signing
>
>
1. shall support, require use of, and issue signed authorization responses via JWT Secured Authorization Response Mode for OAuth 2.0 \[JARM].
1. shall set the response\_mode to jwt in the authorization request as defined in \[JARM];
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
&response_mode=jwt
```
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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/pushed_auth_req \
-H 'Authorization: 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&response_mode=jwt"}'
```
A successful response from the API contains a request URI. The response would look like below.
```json theme={null}
{
"resultCode": "A245001",
"resultMessage": "[A245001] Successfully registered a request object for client ({Client ID}), URI is urn:ietf:params:oauth:request_uri:6uOtgZjBYPPspJLf630c8iJfi4xql_G3R7MKMjgTxRk.",
"action": "CREATED",
"requestUri": "urn:ietf:params:oauth:request_uri:6uO...",
"responseContent": "{\"expires_in\":300,\"request_uri\":\"urn:ietf:params:oauth:request_uri:6uO...\"}"
}
```
### 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.
```bash theme={null}
GET /oauth/authorize?client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:6uO... 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: Bearer {Authlete API Access Token}' \
-H 'Content-type: application/json' \
-d '{"parameters":"client_id={Client ID}&request_uri=urn:ietf:params:oauth:request_uri:6uO..."}'
```
A successful response from the API would be like below.
```json theme={null}
{
"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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization/issue \
-H 'Authorization: Bearer {Authlete API Access Token}' \
-H 'Content-type: application/json' \
-d '{"ticket":"CBKnPeMO...","subject":"john","result":"AUTHORIZED"}'
```
A successful response from the API will include an authorization code, as shown below.
```json theme={null}
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"authorizationCode": "smseP17u...",
"responseContent": "https://client.example.org/cb?response=eyJraWQi...",
...
}
```
You can find the `response` parameter in `responseContent`, and it contains a JWT (`eyJraWQi...`).
The payload of the JWT contains an authorization code as follows.
```json theme={null}
{
"aud": "{Client ID}",
"code": "smseP17u...",
"iss": "https://server.example.com",
"exp": 1702725889
}
```
### 3. 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
>
> * adhere to \[RFC8725];
> * use `PS256`, `ES256`, or `EdDSA` (using the `Ed25519` variant) algorithms; and
> * 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/token \
-H 'Authorization: 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 will include an access token, as shown below.
```json theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "7i9xPkbk...",
...
}
```
### 4. 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/introspection \
-H 'Authorization: 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.
```json theme={null}
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
...
}
```
# Supporting FAPI 2.0 Security Profile with Authlete
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-security-profile-with-authlete
Supporting FAPI 2.0 Security Profile with Authlete.
This page is for **Authlete 3.x**. For 2.x documentation, see [this page](/v2/protocols-and-flows/compliance-profiles/supporting-fapi-2-0-security-profile-with-authlete).
## Overview
This document explains how to support [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-security-profile-2_0-final.html) in Authlete.
"FAPI 2.0 Security Profile" is available since Authlete 2.3.
## Preconditions
* This document uses Authorization Code Flow compliant with FAPI 2.0 Security Profile as an example.
* `private_key_jwt` is used for client authentication.
* DPoP is used for sender-constrained access tokens.
## Service Settings
Configure your service as follows.
### ☑️ 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 > Token > Assertion Audience Mode
Enable this setting.
> "FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements"
>
>
8. shall only accept its issuer identifier value (as defined in \[RFC8414]) as a string in the aud claim received in client authentication assertions;
### ☑️ 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;
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 > ID Token
Set an allowable clock skew value that meets the following condition:
```
10 seconds ≤ clock skew ≤ 60 seconds
```
> "FAPI 2.0 Security Profile, 5.3.2. Requirements for authorization servers, 5.3.2.1. General requirements"
>
>
13. to accommodate clock offsets, shall accept JWTs with an iat or nbf timestamp between 0 and 10 seconds in the future but shall reject JWTs with an iat or nbf timestamp greater than 60 seconds in the future. See Note 3 for further details and rationale;
>
> ...
>
> NOTE 3: Clock skew is a cause of many interoperability issues. Even a few hundred milliseconds of clock skew can cause JWTs to be rejected for being “issued in the future”. The DPoP specification \[RFC9449] suggests that JWTs are accepted in the reasonably near future (on the order of seconds or minutes). This document goes further by requiring authorization servers to accept JWTs that have timestamps up to 10 seconds in the future. 10 seconds was chosen as a value that does not affect security while greatly increasing interoperability. Implementers are free to accept JWTs with a timestamp of up to 60 seconds in the future. Some ecosystems have found that the value of 30 seconds is needed to fully eliminate clock skew issues. To prevent implementations switching off iat and nbf checks completely this document imposes a maximum timestamp in the future of 60 seconds.
### ☑️ 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 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];
### ☑️ Endpoints > Token > 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:
>
> * adhere to \[RFC8725];
> * use `PS256`, `ES256`, or `EdDSA` (using the `Ed25519` variant) algorithms; and
> * 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"
>
>
2. RSA keys shall have a minimum length of 2048 bits.
>
>
3. Elliptic curve keys shall have a minimum length of 224 bits.
## Demonstration
This section demonstrates the Authorization Code Flow defined in the FAPI 2.0 Security Profile by using Authlete APIs.
### 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 to an appropriate value 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 demonstration, 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, a 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/pushed_auth_req \
-H 'Authorization: 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.
```json theme={null}
{
"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\":300,\"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.
```bash theme={null}
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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization \
-H 'Authorization: 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.
```json theme={null}
{
"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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/authorization/issue \
-H 'Authorization: Bearer {Authlete API Access Token}' \
-H 'Content-type: application/json' \
-d '{"ticket":"CBKnPeMO...","subject":"john","result":"AUTHORIZED"}'
```
A successful response from the API will include an authorization code, as shown below.
```json theme={null}
{
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"authorizationCode": "smseP17u...",
...
}
```
### 3. 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:
>
> * adhere to \[RFC8725];
> * use `PS256`, `ES256`, or `EdDSA` (using the `Ed25519` variant) algorithms; and
> * 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/token \
-H 'Authorization: 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 will include an access token, as shown below.
```json theme={null}
{
"resultCode": "A050001",
"resultMessage": "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"accessToken": "7i9xPkbk...",
...
}
```
### 4. 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.
```shell theme={null}
curl -s -X POST https://us.authlete.com/api/{Service ID}/auth/introspection \
-H 'Authorization: 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.
```json theme={null}
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
...
}
```
# Validation in FAPI mode
Source: https://developers.authlete.com/protocols-and-flows/compliance-profiles/validation-in-fapi-mode
This article discusses how Authlete validates requests in FAPI 1.0 Baseline/Advanced mode for Financial-grade API Security Profiles.
## Preface
Authlete 2.0+ provides Financial-grade API (FAPI) mode, which enables to issue and validate tokens based on FAPI 1.0 Baseline ([Financial-grade API Security Profile 1.0 - Part 1: Baseline](https://openid.net/specs/openid-financial-api-part-1-1_0-final.html)) and FAPI 1.0 Advanced ([Financial-grade API Security Profile 1.0 - Part 2: Advanced](https://openid.net/specs/openid-financial-api-part-2-1_0.html)). This document describes how Authlete validates requests in FAPI 1.0 Baseline/Advacned mode.
### Authorization Endpoint
\*\*1. response\_type parameter \*\*
| | |
| - | - |
| Baseline | - |
| Advanced | The value must be either "code id\_token" or "code id\_token token" unless the response\_mode value is "query.jwt", "fragment.jwt", "form\_post.jwt" or "jwt". |
**2. redirect\_uri parameter**
| | |
| - | - |
| Baseline | The redirect\_uri value must be specified. |
| Advanced | same as Baseline |
> The redirect\_uri value is required when the scope parameters include "openid", according to OpenID Connect Core, not Financial-grade API.
**3. Redirect URI scheme**
| | |
| - | - |
| Baseline | The redirect\_uri should use the "https" scheme. |
| Advanced | same as Baseline |
> You cannot use custom schemes in FAPI 1.0 mode. Thus, you may use the method described in [7.2 Claimed "https" Scheme URI Redirection](https://tools.ietf.org/html/bcp212#section-7.2)
> at [BCP 212](https://tools.ietf.org/html/bcp212)
> (OAuth 2.0 for Native Apps) when you want to handle the redirect sequences only in native apps.
**4. Redirect URI validation**
| | |
| - | - |
| Baseline | This URI MUST exactly match one of the Redirection URI values for the Client pre-registered at the OpenID Provider. |
| Advanced | same as Baseline |
> The exact match is required when the scope parameters include "openid" according to OpenID Connect Core, not Financial-grade API.
**5. state parameter**
| | |
| - | - |
| Baseline | The state parameter is required when the scope parameter does not include "openid" scope value. |
| Advanced | same as Baseline |
**6. nonce parameter**
| | |
| - | - |
| Baseline | The nonce request parameter is required if it is desired to obtain a persistent identifier of the authenticated user. |
| Advanced | same as Baseline |
> Authlete considers "if it is desired to obtain a persistent identifier of the authenticated user" as "if an id\_token is requested". Therefore, the nonce parameter is required "when the response\_type request parameter includes id\_token" or "when the response\_type includes code and the scope parameter includes openid."
**7. Request Object**
| | |
| - | - |
| Baseline | - |
| Advanced | The signed request object is required, which means that request or request\_uri parameter shall be in authorization requests. |
**8. Request Object Signature**
| | |
| - | - |
| Baseline | - |
| Advanced | The request object must be signed. |
**9. Request Parameters outside Request Object**
| | |
| - | - |
| Baseline | - |
| Advanced | All parameters outside the requet object must be included inside the request object. |
**10. exp claim in Request Object**
| | |
| - | - |
| Baseline | - |
| Advanced | The request object must contain an exp claim. |
**11. aud claim in Request Object**
| | |
| - | - |
| Baseline | - |
| Advanced | The request object must contain an aud claim that is, or that is an array containing, the OP's Issuer Identifier URL. |
**12. Signing Algorithm of Request Object**
| | |
| - | - |
| Baseline | - |
| Advanced | PS256 or ES256 |
**13. code\_challenge parameter**
| | |
| - | - |
| Baseline | The code\_challenge parameter is required. |
| Advanced | The code\_challenge parameter is required when the client type is public. |
**14. code\_challenge\_method parameter**
| | |
| - | - |
| Baseline | The code\_challenge\_method value must be S256. |
| Advanced | The code\_challenge\_method value must be S256 when the client type is public. |
> The code\_challenge\_method value must be S256 because authorization servers treat the code with plain when the code\_challenge\_method is not specified.
\*\*15. ACR \*\*
| | |
| - | - |
| Baseline | - |
| Advanced | acr claim is required as an essential claim. A client must use the claims request parameter, pass JSON as its value, and include \{“essential":true} inside the JSON. |
> This validation won't be executed in OPEN\_BANKING mode.
\*\*16. Signing Algorithm of Response JWT \*\*
| | |
| - | - |
| Baseline | - |
| Advanced | The metadata of a client, authorization\_signed\_response\_alg, must be PS256 or ES256. |
> Request response will be formated in JSON when JARM is used.
**17. Signing Algorithm of ID Token**
| | |
| - | - |
| Baseline | - |
| Advanced | The metadata of a client, id\_token\_signed\_response\_alg, must be PS256 or ES256. |
### Token Endpoint
**18. Client Authentication Options**
| | |
| - | - |
| Baseline | - tls\_client\_auth - self\_signed\_tls\_client\_auth - client\_secret\_jwt - private\_key\_jwt |
| | |
| Advanced | - tls\_client\_auth - self\_signed\_tls\_client\_auth - private\_key\_jwt |
| | |
**19. Signing Algorithm of Client Assertion**
| | |
| - | - |
| Baseline | - |
| Advanced | PS256 or ES256 |
**20. Key Size of Client Assertion**
| | |
| - | - |
| Baseline | A key of size 2048 bits or larger is required if RSA algorithms are used for the client authentication. A key of size 160 bits or larger is required if elliptic curve algorithms are used for the client authentication. |
| Advanced | same as Baseline |
**21. Signing Algorithm of ID Token**
| | |
| - | - |
| Baseline | - |
| Advanced | The metadata of a client, id\_token\_signed\_response\_alg, must be PS256 or ES256. |
**22. Mechanism for sender-constrained access tokens**
| | |
| - | - |
| Baseline | - |
| Advanced | Support MTLS (Certificate Binding) as mechanism for sender-constrained access tokens. |
### User Info Endpoint
**23. Signing Algorithm of User Info Response**
| | |
| - | - |
| Baseline | - |
| Advanced | The algorithm must be either PS256 or ES256 when a user info response is signed. |
### Reference
* [Financial-grade API (FAPI), explained by an implementer](https://medium.com/@darutk/financial-grade-api-fapi-explained-by-an-implementer-d09fcf2ff932)
# OAuth Client ID Metadata Document (CIMD)
Source: https://developers.authlete.com/protocols-and-flows/protocol-extensions/oauth-client-id-metadata-document-cimd
Explanation about "OAuth Client ID Metadata Document (CIMD)" and Authlete's implementation.
## Introduction
In authorization requests defined in
[RFC 6749: The OAuth 2.0 Authorization Framework][RFC_6749], the client
application ("client" hereafter) must specify its identifier ("client ID"
hereafter) using the `client_id` request parameter. In addition, the
authorization server must verify that the `redirect_uri` specified in the
request is one that has been registered for the client (i.e., that the
redirect URI is included in the client's `redirect_uris` metadata).
Given these requirements, it follows that before the client sends a request
to the authorization server, the client's identifier and metadata must already
be registered with the authorization server.
There are several standard specifications for pre-registering clients, such as
[OpenID Connect Dynamic Client Registration 1.0][OIDC_DYNREG] (DCR),
[RFC 7591 OAuth 2.0 Dynamic Client Registration Protocol][RFC_7591] (DCR), and
[Explicit Registration][OIDFED_EXPLICIT_REG] of [OpenID Federation 1.0][OIDFED]
([explanatory document][OIDFED_ARTICLE]). Support for these specifications by
an authorization server is optional.
Even when an authorization server supports the standards mentioned above,
it is common for its implementation to provide proprietary client registration
methods as well, such as static configuration files or a web console. Authlete
also supports client registration through its web console and the
`/client/create` API, in addition to these standard
specifications.
Because an authorization request requires it, an application must retain the
client ID assigned by the authorization server. If the application functions
as a client of multiple authorization servers, it must manage a different
client ID for each server it communicates with. This situation is by no means
uncommon. As described in "Current Architecture of Ecosystems" in the
[GAIN PoC Interim Report (Spring 2023)][GAIN_POC_REPORT], ecosystems modeled
after the UK's Open Banking generally operate in this manner.
Although this DCR-based approach works, it requires applications to use
different client IDs for each authorization server. As the number of
authorization servers increases, managing these identifiers becomes
cumbersome. From an application's perspective, it would be ideal to reuse
a single client ID across all authorization servers whenever possible.
In fact, a mechanism that allows a client ID to be shared across all
authorization servers has already been realized in the
[OpenID Federation 1.0][OIDFED] specification. In this specification,
a client ID is a globally unique URL starting with `https://`. The client's
metadata is then provided at the location obtained by appending
`/.well-known/openid-federation` to that URL
(the figure below is an excerpt from the [explanatory document][OIDFED_ARTICLE]).
Authorization servers that support [OpenID Federation 1.0][OIDFED] will, when
they receive an authorization request whose `client_id` parameter refers to an
unregistered client and that identifier is a URL starting with `https://`,
automatically fetch the client metadata from the location obtained by appending
`/.well-known/openid-federation` to that URL and
register the client (this is called [Automatic Registration][OIDFED_AUTOMATIC_REG]).
After that, the authorization server simply continues processing the
authorization request as if nothing unusual had happened.
Because trust chain verification and metadata policy enforcement are involved,
the process of deriving client metadata is actually quite complex. Please refer
to the [explanatory document](/guides/flows-and-protocols/identity-and-federation/openid-federation-1-0) for details.
A new specification called the **[OAuth Client ID Metadata Document][CIMD]**
(**CIMD**) has been proposed in a landscape where a prior specification already
exists. In this specification as well, the client ID is a URL starting with
`https://`, and the client metadata is provided from the location indicated by
that URL.
Compared to [OpenID Federation 1.0][OIDFED], [CIMD][CIMD] is a much simpler
specification. This simplicity makes implementation easier for both clients and
servers. However, in exchange for that simplicity, the [CIMD][CIMD]
specification does not define any method for verifying whether a client is a
trustworthy communication partner. As a result, unless an authorization server's
implementation imposes some kind of proprietary restriction, anyone can freely
register a client with an authorization server that supports [CIMD][CIMD]. This
stands in stark contrast to [OpenID Federation 1.0][OIDFED], which clearly
defines how to build trust relationships between entities using trust chains.
In this article, we describe the [CIMD][CIMD] specification and
Authlete-specific features that complement it.
## Specification
### Overview
Put simply, the [CIMD][CIMD] specification says: *"Use a URL beginning with
`https://` as the client identifier. Retrieve the client metadata from the
location pointed to by that URL."*
Let's walk through the flow. First, the client sends an authorization request
to the authorization server. The authorization request includes the `client_id`
request parameter, and the value of that parameter is a URL beginning with
`https://`.
The authorization server parses the received authorization request. It then
recognizes that the client ID begins with `https://`.
If the client corresponding to that client ID is not yet registered, the
authorization server accesses the location pointed to by that URL.
The location being accessed returns the client metadata in JSON format.
The authorization server registers the client with itself based on the client
metadata and continues processing the authorization request.
### Client ID Requirements
* The scheme must be `https`
* A path component is required
* The path must not contain path segments `.` or `..`
* A fragment component must not be included
* A username or password must not be included
* A query component should not be included
### Client Metadata Restriction
* The URL of the metadata and the `client_id` value in the metadata must match.
* Symmetric key client authentication methods such as
`client_secret_basic`,
`client_secret_post`, and
`client_secret_jwt` must not be specified in
`token_endpoint_auth_method`.
* `client_secret` and `client_secret_expires_at`
must not be used.
## Service Settings
Authlete has supported [CIMD][CIMD] since version 3.0.22. In this section, we
introduce the properties added to the Service to support [CIMD][CIMD], as well
as the related API request parameters.
### clientIdMetadataDocumentSupported Property
A new boolean property, `clientIdMetadataDocumentSupported`,
has been added to the Service to indicate whether [CIMD][CIMD] is supported.
The default value of this property is `false`, so if you want to use
[CIMD][CIMD], you need to explicitly set it to `true`.
```json theme={null}
{
"clientIdMetadataDocumentSupported": true
}
```
The `clientIdMetadataDocumentSupported` property
corresponds to the `client_id_metadata_document_supported`
server metadata defined in the [CIMD][CIMD] specification. When
`clientIdMetadataDocumentSupported` is set to
`true`, the discovery document ([OpenID Connect Discovery 1.0][OIDC_DISCOVERY])
generated by Authlete's `/service/configuration` API will
include the following entry:
```json theme={null}
{
"client_id_metadata_document_supported": true
}
```
### Metadata Policy
A configuration that controls how client metadata retrieved via CIMD (Client ID Metadata Document) is processed and validated before being used. When enabled, the policy defined here is applied to metadata fetched from the client ID URI, and the resulting metadata is stored as the client registration.
This feature is enabled by setting the `cimdMetadataPolicyEnabled` property to `true` and providing a JSON policy definition in the `cimdMetadataPolicy` property.
The metadata policy follows the [OpenID Federation 1.0 specification (Section 6.1 – Metadata Policy)](https://openid.net/specs/openid-federation-1_0.html#name-metadata-policy) and supports operators such as `add`, `subset_of`, `superset_of`, `one_of`, `default`, `value`, and `essential`.
#### Example configuration
```json theme={null}
{
"cimdMetadataPolicyEnabled": true,
"cimdMetadataPolicy": {
"id_token_signed_response_alg": {
"default": "ES256",
"one_of": ["ES256", "ES384", "ES512"]
},
"redirect_uris": {
"add": [
"http://localhost:12345/redirect"
]
}
}
}
```
#### Service API payload example
When the Metadata Policy is enabled, the Service API payload contains the policy as a JSON-encoded string.
```json theme={null}
{
"cimdMetadataPolicyEnabled": true,
"cimdMetadataPolicy": "{\"redirect_uris\":{\"add\":[\"http://localhost:12345/redirect\"]},\"id_token_signed_response_alg\":{\"default\":\"ES256\",\"one_of\":[\"ES256\",\"ES384\",\"ES512\"]}}"
}
```
#### Behavior
When `cimdMetadataPolicyEnabled` is set to `true`, Authlete applies the defined policy to metadata obtained through CIMD before storing it as the client registration. The policy can add values, restrict options, set defaults, or validate metadata fields according to the OpenID Federation 1.0 metadata policy grammar.
If the policy is disabled, retrieved client metadata is stored without modification.
### Allowlist
You can restrict the URLs allowed as client IDs using an allowlist. The
allowlist is specified in the Service's `cimdAllowlist`
property. To enable restriction based on the allowlist, set the
`cimdAllowlistEnabled` property to `true`.
```json theme={null}
{
"cimdAllowlistEnabled": true,
"cimdAllowlist": [
"https://example.com/a/b"
]
}
```
If the `cimdAllowlistEnabled` property remains
`false`, anyone can freely register clients without restriction.
The value of the `cimdAllowlist` property is an array of
strings, and each element must be a valid URL.
A client ID is considered valid if it matches any one of the URLs in the
allowlist. The match is not determined by a simple string comparison, but
is performed according to the following rules:
| | Component | Comparison |
| :-: | :- | :- |
| 1 | Scheme | The schemes must match. |
| 2 | Authority | The authorities must match (simple string comparison; the default port is not considered). |
| 3 | Path | The path of the client ID must fully include the path of the URL in the allowlist. |
| 4 | Query | If the URL in the allowlist includes a query component, the query components must match. |
The path comparison is performed on a segment-by-segment basis. For example,
if the allowlist contains `https://example.com/a/b`,
then `https://example.com/a/b/c` is considered a
valid client ID, whereas `https://example.com/a` or
`https://example.com/a/bb` are considered invalid.
### Always Retrieved
The authorization server caches the retrieved client metadata for a certain
period. Until that period expires, it does not re-retrieve the metadata and
continues to reference the cached version.
This behavior is generally desirable, but there are times when you may want
the authorization server to re-retrieve the client metadata before the cache
expires — for example, when the client metadata is being changed
frequently during client application development. If you have experience in
web content creation, you may have found the browser's Super Reload feature
useful. The forced re-retrieval functionality for client metadata is, in a
sense, like a Super Reload.
In [OpenID Federation 1.0][OIDFED], the `trust_chain` request parameter allows
you to prompt the authorization server to update the client metadata before the
cache expires. [CIMD][CIMD], on the other hand, does not provide such a
mechanism. Therefore, I proposed standardizing a way to prompt the re-retrieval
of client metadata ([CIMD ISSUE 59][CIMD_ISSUE_59]). However, since there was
little chance of gaining consensus, I decided to implement an Authlete-specific
mechanism.
If the Service's `cimdAlwaysRetrieved` property is set to
`true`, Authlete will always re-retrieve the client metadata. More precisely,
the re-retrieval occurs in the initiating request of an authorization flow.
"Initiating request of an authorization flow" refers to the following cases:
* In the authorization code flow (and other flows initiated at the
authorization endpoint), re-retrieval is performed during the authorization
request, but not during the subsequent token request.
* In client credentials flow (and other flows initiated at the token endpoint),
the token request itself is the initiating request, so re-retrieval occurs at
the time of the token request.
* In the CIBA flow ([CIBA Core][CIBA_CORE]), re-retrieval is performed during
the backchannel authentication request, but not during the subsequent token
request.
* In the device flow ([RFC 8628][RFC_8628]), re-retrieval is performed during
the device authorization request, but not during the subsequent token request.
The “always retrieved” feature is useful during development. However, once the
system is running stably and client metadata rarely changes, always
re-retrieving is excessive. Therefore, once the system stabilizes, you would
typically set the `cimdAlwaysRetrieved` property to `false`.
That said, there may still be circumstances where you want to force a
re-retrieval even after the system has stabilized. To accommodate this, several
Authlete APIs —specifically the ones listed below— accept the
request parameter `cimdOptions.alwaysRetrieved`. If
this parameter is set to `true`, the client metadata will be re-retrieved
regardless of the cache status, even if the Service's
`cimdAlwaysRetrieved` setting is `false`.
| API | Description |
| :- | :- |
| `/api/{service-id}/auth/authorization` | Processes authorization requests |
| `/api/{service-id}/auth/token` | Processes token requests |
| `/api/{service-id}/backchannel/authentication` | Processes backchannel authentication requests |
| `/api/{service-id}/device/authorization` | Processes device authorization requests |
Using this runtime feature, an authorization server can implement its own
forced client metadata re-retrieval functionality.
### HTTP Permitted
In the [CIMD][CIMD] specification, the scheme of the URL representing the
client ID must be `https`.
However, if the Service's `cimdHttpPermitted` flag is set
to `true`, Authlete will also allow the `http` scheme. Enabling this feature
allows you to use a web server without TLS protection as a server hosting
client metadata. This feature is intended for use during development.
Just as the `cimdOptions.alwaysRetrieved` request
parameter corresponds to the `cimdAlwaysRetrieved` property,
there is also a `cimdOptions.httpPermitted` request
parameter corresponding to the `cimdHttpPermitted` property.
### Query Permitted
In the [CIMD][CIMD] specification, the URL representing the client ID SHOULD
NOT have a query component. By default, Authlete does not accept URLs with a
query component as [CIMD][CIMD] client IDs.
To allow Authlete to accept URLs with a query component as [CIMD][CIMD] client
IDs, you need to set the Service's `cimdQueryPermitted`
property to `true`.
This property also has a corresponding request parameter:
`cimdOptions.queryPermitted`.
### HTTP Alias Prohibited
Authlete internally assigns a numeric identifier to each client. In addition,
there is a feature called "Client ID Alias", which allows developers to assign
an arbitrary name to a client. Until Authlete 2.3, this feature could be turned
on or off, but due to historical reasons, in Authlete 3.0 the feature is always
enabled and cannot be disabled.
Since arbitrary names can be assigned, for example, a URL with an `https`
scheme can be set as a client identifier. However, such a client identifier,
while appearing similar to a client ID in [OpenID Federation 1.0][OIDFED] or
[CIMD][CIMD], is actually just an alias and does not trigger the automatic
client metadata update process.
To prevent potential confusion, Authlete has introduced a new Service property,
`httpAliasProhibited`, which prevents strings starting
with `https://` or `http://` from being set as aliases.
For backward compatibility, the default value of this
`httpAliasProhibited` property is `false`, but it is
recommended to set it to `true` whenever possible.
When Authlete searches for a client corresponding to a client ID included in
an authorization request (or other standard protocol messages), it does so in
the following order of priority: numeric internal identifier → alias
→ OpenID Federation 1.0 entity ID → CIMD client metadata document URL.
Therefore, if the client ID matches an alias, the CIMD functionality will not
be triggered for that client ID.
### Metadata Policy
Authlete provides a feature that applies a metadata policy to retrieved client
metadata and adjusts it before storing the metadata in the database. This
feature applies the metadata policy defined in
[Section 6.1, “Metadata Policy,”][OIDFED_METADATA_POLICY] of
[OpenID Federation 1.0][OIDFED] to [CIMD][CIMD].
For example, suppose that the client metadata obtained from the URL indicated
by the client ID is as follows:
```json theme={null}
{
"client_name": "Example Client",
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"token_endpoint_auth_method": "none",
"client_id": "https://example.com/client.json",
"redirect_uris": [
"https://example.com/redirect"
]
}
```
When the following metadata policy is applied to this client metadata,
```json theme={null}
{
"id_token_signed_response_alg": {
"default": "ES256",
"one_of": ["ES256", "ES384", "ES512"]
},
"redirect_uris": {
"add": [
"http://localhost:12345/redirect"
]
}
}
```
the resulting client metadata will look like the following. Note that
`http://localhost:12345/redirect` has been added to
`redirect_uris`, and that a new
`id_token_signed_response_alg` property with
the value `ES256` has been added.
```json theme={null}
{
"client_name": "Example Client",
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"token_endpoint_auth_method": "none",
"client_id": "https://example.com/client.json",
"redirect_uris": [
"https://example.com/redirect",
"http://localhost:12345/redirect"
],
"id_token_signed_response_alg": "ES256"
}
```
The [OpenID
Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html) specification defines the default value
of the `id_token_signed_response_alg` property as
`RS256`. Therefore, if this property is not explicitly included, its
value defaults to `RS256`, and as a result, the ID token is signed using
`RS256`.
However, if a metadata policy like the one shown above is prepared and applied,
the value of this property becomes `ES256`, and consequently, the ID
token is signed using `ES256`.
To use this feature, set the Service's `cimdMetadataPolicyEnabled`
property to `true` and configure the metadata policy in the
`cimdMetadataPolicy` property.
```json theme={null}
{
"cimdMetadataPolicyEnabled": true,
"cimdMetadataPolicy": "{\"redirect_uris\":{\"add\":\"http://localhost:12345/redirect\"},\"id_token_signed_response_alg\":{\"default\":\"ES256\",\"one_of\":[\"ES256\",\"ES384\",\"ES512\"]}}"
}
```
## Client Properties
When Authlete communicates client information outside of Authlete via the
Authlete APIs, it uses a data structure called [Client][CLIENT]. To support
[CIMD][CIMD], the following properties have been added to this data structure:
| Property | Description |
| :- | :- |
| `metadataDocumentLocation` | The location of client metadata |
| `metadataDocumentExpiresAt` | The expiration time of client metadata (in milliseconds elapsed since the Unix epoch) |
| `metadataDocumentUpdatedAt` | The last update time of client metadata (in milliseconds elapsed since the Unix epoch) |
| `discoveredByMetadataDocument` | The boolean flag indicating whether the client has been registered using CIMD |
As mentioned in the [CIMD][CIMD] specification, Authlete calculates the
validity period of client metadata using existing HTTP mechanisms (see:
[RFC 9111 HTTP Caching][RFC_9111]). The
`metadataDocumentExpiresAt` property indicates
the time when this validity period expires.
If the web server hosting the client metadata forgets to set cache-related
parameters such as `max-age`, or sets an excessively long lifetime, the
calculated validity period of the client metadata could far exceed the
expected client metadata update interval. If such an unexpectedly long
validity period is applied, updates to the client metadata may not be
detected at the appropriate intervals. To prevent this, the current Authlete
implementation caps the validity period at 86,400 seconds (1 day), regardless
of the calculated value.
For special cases where customers using the on-premises version of Authlete
or Authlete's internal developers run both the Authlete server and the web
server hosting client metadata on the same local machine during development,
the maximum validity period of client metadata is set to 600 seconds (10
minutes) if the host part of the client ID points to the local machine —
specifically, in any of the following cases. Note that this behavior may
change in the future.
`localhost`
`127.0.0.1`
`::1`
`host.docker.internal`
`host.minikube.internal`
The following are the properties related to the client identifier.
| Property | Type | Specification | Description |
| :- | :-: | :-: | :- |
| `clientId` | Number | | The numeric client identifier |
| `clientIdAlias` | String | | The client ID alias |
| `clientIdAliasEnabled` | Boolean | | Whether the Client ID Alias feature is enabled (Always `true` in Authlete 3.0) |
| `dynamicallyRegistered` | Boolean | DCR | Whether the client has been registered using DCR |
| `entityId` | String | [OIDFED][OIDFED] | The entity ID |
| `automaticallyRegistered` | Boolean | [OIDFED][OIDFED] | Whether the client has been registered using [Automatic Registration][OIDFED_AUTOMATIC_REG] |
| `explicitlyRegistered` | Boolean | [OIDFED][OIDFED] | Whether the client has been registered using [Explicit Registration][OIDFED_EXPLICIT_REG] |
| `metadataDocumentLocation` | String | [CIMD][CIMD] | The location of client metadata |
| `metadataDocumentExpiresAt` | Number | [CIMD][CIMD] | The expiration time of client metadata |
| `metadataDocumentUpdatedAt` | Number | [CIMD][CIMD] | The last update time of client metadata |
| `discoveredByMetadataDocument` | Boolean | [CIMD][CIMD] | Whether the client has been registered using [CIMD][CIMD] |
Due to historical reasons, there are multiple boolean flags indicating the
origin of a client —namely, `dynamicallyRegistered`,
`automaticallyRegistered`, `explicitlyRegistered`,
and `discoveredByMetadataDocument`. These flags are
mutually exclusive, meaning that more than one flag cannot be `true` at the
same time.
Having separate mutually exclusive flags is cumbersome, so starting from
Authlete version 3.0.22, the Authlete API responses also include a string
property called `clientSource` in addition to these flags.
This property can take the following values:
| Value | Description |
| :- | :- |
| `DYNAMIC_REGISTRATION` | Registration using DCR |
| `AUTOMATIC_REGISTRATION` | Registration using [OpenID Federation 1.0][OIDFED]'s [Automatic Registration][OIDFED_AUTOMATIC_REG] |
| `EXPLICIT_REGISTRATION` | Registration using [OpenID Federation 1.0][OIDFED]'s [Explicit Registration][OIDFED_EXPLICIT_REG] |
| `METADATA_DOCUMENT` | Registration using [CIMD][CIMD] |
| `STATIC_REGISTRATION` | Others (static registration) |
## Response Parameters
The responses of several Authlete APIs, such as
`/api/{service-id}/auth/authorization`, include
information about the origin of the client ID used in the request.
| Parameter | Type | Specification | Description |
| :- | :-: | :-: | :- |
| `clientIdAliasUsed` | Boolean | | The alias was used as the client ID |
| `clientIdAlias` | String | | The alias |
| `clientEntityIdUsed` | Boolean | [OIDFED][OIDFED] | The entity ID was used as the client ID |
| `clientEntityId` | String | [OIDFED][OIDFED] | The entity ID |
| `metadataDocumentUsed` | Boolean | [CIMD][CIMD] | The location of client metadata was used as the client ID |
| `metadataDocumentLocation` | String | [CIMD][CIMD] | The location of client metadata |
## Conclusion
In the 2025-11-25 edition of the MCP (Model Context Protocol) specification,
[CIMD][CIMD] was incorporated as part of the standard
([MCP 2025-11-25 Authorization][MCP_20251125_AUTHORIZATION]).
Authlete completed the implementation of the [CIMD][CIMD] specification in
November 2025, and as introduced in this article, also implemented the
following Authlete-specific features:
* Allowlist (`cimdAllowlist`, `cimdAllowlistEnabled`)
* Always Retrieved (`cimdAlwaysRetrieved` | `cimdOptions.alwaysRetrieved`)
* HTTP Permitted (`cimdHttpPermitted` | `cimdOptions.httpPermitted`)
* Query Permitted (`cimdQueryPermitted` | `cimdOptions.queryPermitted`)
* Metadata Policy (`cimdMetadataPolicy`, `cimdMetadataPolicyEnabled`)
[CIMD][CIMD] is a specification that is still under development and is expected
to continue evolving. Additionally, industry experience with implementing and
operating [CIMD][CIMD] is just beginning to accumulate. Authlete plans to add
features as needed in response to updates to the specification and industry
feedback. If you have any feature requests, please contact us via the
[contact form][CONTACT].
[RFC_6749]: https://www.rfc-editor.org/rfc/rfc6749.html
[RFC_7591]: https://www.rfc-editor.org/rfc/rfc7591.html
[RFC_8628]: https://www.rfc-editor.org/rfc/rfc8628.html
[RFC_9111]: https://www.rfc-editor.org/rfc/rfc9111.html
[OIDC_DISCOVERY]: https://openid.net/specs/openid-connect-discovery-1_0.html
[OIDC_DYNREG]: https://openid.net/specs/openid-connect-registration-1_0.html
[OIDFED]: https://openid.net/specs/openid-federation-1_0.html
[OIDFED_ARTICLE]: /developers/oidcfed/
[OIDFED_AUTOMATIC_REG]: https://openid.net/specs/openid-federation-1_0.html#name-automatic-registration
[OIDFED_EXPLICIT_REG]: https://openid.net/specs/openid-federation-1_0.html#name-explicit-registration
[OIDFED_METADATA_POLICY]: https://openid.net/specs/openid-federation-1_0.html#name-metadata-policy
[CIBA_CORE]: https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html
[CIMD]: https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/
[CIMD_ISSUE_59]: https://github.com/oauth-wg/draft-ietf-oauth-client-id-metadata-document/issues/59
[MCP_20251125_AUTHORIZATION]: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
[GAIN_POC_REPORT]: https://medium.com/@darutk/gain-poc-interim-report-spring-2023-e02a1cd90b1c
[CLIENT]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Client.html
[CONTACT]: /contact/
# OpenID Connect Back-Channel Logout 1.0
Source: https://developers.authlete.com/protocols-and-flows/protocol-extensions/openid-connect-back-channel-logout-1-0
This document describes Authlete's support for the OpenID Connect Back-Channel Logout 1.0 specification.
## Introduction
This article describes Authlete's support for the
**[OpenID Connect Back-Channel Logout 1.0][BC_LOGOUT]** specification.
## Specification Overview
The server generates a **[Logout Token][LOGOUT_TOKEN]** and sends an HTTP POST
request containing it to the client's **Back-Channel Logout URI**.
Upon receiving the Logout Token, the client performs logout processing.
A Logout Token is a JWT. The value of its `typ` header parameter is defined as
`logout+jwt`. Its payload contains either the `sub` claim, the `sid` claim, or
both, and these claims identify the logout target.
## Logout Token API
Authlete provides an API for generating Logout Tokens.
The API path is
`/api/{service-id}/backchannel/logout/token`.
The request body format is either `application/json` or
`application/x-www-form-urlencoded`.
The request parameters are as follows.
| Request Parameter | Necessity | Description |
| :- | :-: | :- |
| `clientIdentifier` | REQUIRED | The client identifier. |
| `subject` | OPTIONAL | The subject (identifier) of the end-user. The specified value is used as the value of the `sub` claim in the Logout Token. Either this `subject` parameter or the `sessionId` parameter must be specified. |
| `sessionId` | OPTIONAL | The session ID. The specified value is used as the value of the `sid` claim in the Logout Token. Either this `sessionId` parameter or the `subject` parameter must be specified. |
The response parameters are as follows.
| Response Parameter | Description |
| :- | :- |
| `logoutToken` | The generated Logout Token. |
| `backchannelLogoutUri` | The value of the client's `backchannel_logout_uri` metadata parameter. |
| `action` | Indicates the next action that the server should take. Possible values are `OK`, `SERVER_ERROR`, and `CALLER_ERROR`. `OK` indicates that the Logout Token was generated successfully. |
| `resultCode` | A code representing the result of the API call (a parameter included in almost all Authlete API responses). |
| `resultMessage` | A message describing the result of the API call (a parameter included in almost all Authlete API responses). |
The following is an example API call.
```shell theme={null}
curl --oauth2-bearer ${ACCESS_TOKEN} \
https://${AUTHLETE_SERVER}/api/${SERVICE_ID}/backchannel/logout/token \
--json '{
"clientIdentifier": "client001",
"subject": "user001",
"sessionId": "sid001"
}'
```
The following is an example API response.
```json theme={null}
{
"action": "OK",
"logoutToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImxvZ291dCtqd3QiLCJraWQiOiIzVFNzM0U4djc3cXhQckhCNUtDendYY3RRajhJY0FBbHRuMThVYWZ1T1RzIn0.eyJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsInN1YiI6InVzZXIwMDEiLCJhdWQiOiJjbGllbnQwMDEiLCJpYXQiOjE3Nzg0NjE1NjIsImV4cCI6MTc3ODQ2MTY4MiwianRpIjoiMzBhNjljZTctMTQ0YS00MTc5LWIzOGItMTMyNDc1ZDk3Y2E4Iiwic2lkIjoic2lkMDAxIiwiZXZlbnRzIjp7Imh0dHA6Ly9zY2hlbWFzLm9wZW5pZC5uZXQvZXZlbnQvYmFja2NoYW5uZWwtbG9nb3V0Ijp7fX19.rmckdJrYEZg5i085KFMXLpHkPLHe63sjk8Qr9UwRJKBEAIueJomJ0DdzbOybm4475_DtP8wxkhAG0hE_YZgYyQ",
"backchannelLogoutUri": "https://client.example.com/logout",
"resultCode": "A518001",
"resultMessage": "[A518001] A Logout Token for the client 'client001' was issued successfully."
}
```
Decoding the header and payload of the Logout Token included in this example
yields the following.
```json theme={null}
{
"alg": "ES256",
"typ": "logout+jwt",
"kid": "3TSs3E8v77qxPrHB5KCzwXctQj8IcAAltn18UafuOTs"
}
```
```json theme={null}
{
"iss": "https://trial.authlete.net",
"sub": "user001",
"aud": "client001",
"iat": 1778461562,
"exp": 1778461682,
"jti": "30a69ce7-144a-4179-b38b-132475d97ca8",
"sid": "sid001",
"events": {
"http://schemas.openid.net/event/backchannel-logout": {}
}
}
```
Note that the current implementation of the
`/api/{service-id}/backchannel/logout/token`
API does not automatically revoke tokens. If token revocation is necessary,
trigger it explicitly, for example by calling the revocation endpoint.
## Metadata
The [OpenID Connect Back-Channel Logout 1.0][BC_LOGOUT] specification defines
two server metadata parameters and two client metadata parameters.
Corresponding properties have been added to Authlete's `Service` and `Client`
models as well.
| Service Property | Description |
| :- | :- |
| `backchannelLogoutSupported` | This property indicates whether this service supports the [OpenID Connect Back-Channel Logout 1.0][BC_LOGOUT] specification. This property corresponds to the `backchannel_logout_supported` server metadata parameter defined in the specification. |
| `backchannelLogoutSessionSupported` | This boolean property indicates whether this service supports the `sid` claim in [Logout Token][LOGOUT_TOKEN]s. This property corresponds to the `backchannel_logout_session_supported` server metadata parameter defined in the [OpenID Connect Back-Channel Logout 1.0][BC_LOGOUT] specification.
If this property is set to `true`, API calls to the `/api/{service-id}/backchannel/logout/token` API should include the `sessionId` request parameter, whose value is embedded in the Logout Token as the value of the `sid` claim. Likewise, API calls to the `/api/{service-id}/auth/authorization/issue` API should include the `sessionId` request parameter, whose value is embedded in ID Tokens as the value of the `sid` claim. |
| Client Property | Description |
| :- | :- |
| `backchannelLogoutUri` | The Back-Channel Logout URI defined in the [OpenID Connect Back-Channel Logout 1.0][BC_LOGOUT] specification. It must be an HTTP-accessible URL because HTTP POST requests containing a [Logout Token][LOGOUT_TOKEN] are sent to the endpoint. This property corresponds to the `backchannel_logout_uri` client metadata parameter defined in the specification.
The URL must consist only of ASCII characters and must not exceed 200 characters in length. The scheme must be either `https` or `http`, and the URL must not contain a fragment component.
The configured value will be included in the response from the `/api/{service-id}/backchannel/logout/token` API as the value of the `backchannelLogoutUri` parameter. |
| `backchannelLogoutSessionRequired` | This boolean property indicates whether this client requires [Logout Token][LOGOUT_TOKEN]s to include the `sid` claim. This property corresponds to the `backchannel_logout_session_required` client metadata parameter defined in the [OpenID Connect Back-Channel Logout 1.0][BC_LOGOUT] specification. |
## Conclusion
Support for the [OpenID Connect Back-Channel Logout 1.0][BC_LOGOUT]
specification is available starting with Authlete version 3.0.32.
For more information, please contact us via the [contact form][CONTACT].
[BC_LOGOUT]: https://openid.net/specs/openid-connect-backchannel-1_0.html
[CONTACT]: https://www.authlete.com/contact
[LOGOUT_TOKEN]: https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken
# Proof Key for Code Exchange (RFC 7636)
Source: https://developers.authlete.com/protocols-and-flows/protocol-extensions/proof-key-for-code-exchange-pkce
This document describes PKCE, a countermeasure agains the authorization code interception attack, defined in RFC 7636.
This page is for Authlete 3.0. For 2.x, see [this page](/v2/protocols-and-flows/protocol-extensions/pkce).
## 1. Introduction
[RFC 7636](http://tools.ietf.org/html/rfc7636) : **Proof Key for Code Exchange** (PKCE, pronounced "pixy") is a specification about a countermeasure against the **authorization code interception attack**.
The specification was released on September, 2015. It has added:
1. `code_challenge` parameter and `code_challenge_method` parameter to authorization requests using the authorization code flow, and
2. `code_verifier` parameter to token requests that correspond to the authorization requests.
This mechanism enables an authorization server to reject a token request from a malicious application that does not have a code verifier.
## 2. PKCE Authorization Request
### 2.1 Request Parameters
An authorization request that uses PKCE goes out with `code_challenge` parameter and optionally with `code_challenge_method` parameter.
### 2.2 Code Challenge Value
The value of code\_challenge parameter is computed by applying a code challenge method (= computation logic) to a code verifier.
### 2.3 Code Verifier Value
A code verifier itself is a random string using characters of `[A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"`, with a minimum length of 43 characters and a maximum length of 128 characters.
### 2.4 Code Challenge Method
The defined code challenge methods are `plain` and `S256`. Respective computation logics to convert a code verifier into a code challenge are as follows.
| Method | Logic |
| - | - |
| `plain` | code\_challenge = code\_verifier |
| `S256` | code\_challenge = BASE64URL-ENCODE(SHA256(ASCII(code\_verifier))) |
The `plain` method does not change the input, so the value of `code_verifier` and the resultant value of `code_challenge` are equal.
The `S256` method computes the SHA-256 hash of the input and then encodes the hash value using Base64-URL. For example, when the value of `code_verifier` is dBjftJeZ4CVP-mB92K27uhbUJU1p1r\_wW1gFWFOEjXk, the value of `code_challenge` becomes E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM.
When the used code challenge method is `S256`, a client application must tell it by including `code_challenge_method=S256` parameter in an authorization request. When `code_challenge_method` parameter is omitted, an authorization server assumes `plain` as the default value.
## 3. PKCE Authorization Response
After generating an authorization code, an authorization server saves it into its DB with the code challenge and the code challenge method contained in the authorization request.
The authorization server will use the saved code challenge and the code challenge method later to verify a token request from the client application.
A response from the authorization endpoint has nothing special for PKCE. It's a normal response as usual.
## 4. PCKE Token Request
After receiving an authorization code from an authorization server, a client application makes a token request. In addition to the authorization code, the token request must include the code verifier used to compute the code challenge.
The name of the request parameter to specify a code verifier is `code_verifier`.
## 5. PKCE Token Response
### 5.1 Require Code Verifier
A token endpoint of an authorization server that supports PKCE checks whether a token request contains a valid code verifier.
Of course, this check is performed only when grant\_type is authorization\_code and the authorization code contained in the token request is associated with a code challenge.
If a token request does not contain a valid code verifier although the conditions above meet, the request is regarded as from a malicious application and the authorization server returns an error response.
### 5.2 Verify Code Verifier
Verification is performed by comparing two code challenges.
One is what was contained in the *authorization* request and is stored in the DB. The other is what an authorization server computes using the code verifier contained in the *token* request and the code challenge method stored in the DB.
If the two code challenges are equal, the token request can be regarded as from the legitimate client application that has made the original authorization request. Otherwise, the token request must be regarded from a malicious application.
### 5.3 Issue Access Token
If a token request is verified, an authorization server issues an access token as usual.
## 6. Try PKCE With Authlete
### 6.1 Preparation
#### 6.1.1 Sign up
If you don't have an Authlete account yet, you can [sign up here](https://login.authlete.com/signup) for free. It only takes 5 minutes.

#### 6.1.2 Service ID and Client ID
To make an authorization request, you will need a **Service ID** and a **Client ID**.
If you need a **Service ID** and a **Client ID**, follow the [Quick Setup Guide](/get-started/quickstarts/using-demo-authorization-server) to setup your service and client.
Also, you can find the **Service** and **Client** ID the [Authlete Management Console](https://console.authlete.com/).
#### 6.1.3 Service and Client Settings
This section explains how to configure your service and client settings.
Table. Service Settings
| Screen | Parameter | Value |
| - | - | - |
| Service Settings > Endpoints > Global Settings > General | Supported Grant Types | Include `AUTHORIZATION_CODE` |
| Service Settings > Endpoints > Global Settings > General | Supported Response Types | Include `CODE` |
Configure the service:
1. Navigate to **Service Settings > Endpoints > Global Settings > General**.
2. Under **Select Supported Grant Types**, select `AUTHORIZATION_CODE`.
3. Under **Select Response Types**, select `CODE`.
4. Click **Save Changes** to apply the updates.
Table. Client Settings
| Screen | Parameter | Value |
| - | - | - |
| Client Settings > Basic Settings > General | Client Type | Select `PUBLIC` |
| Client Settings > Basic Settings > General | Redirect URIs | `https://client.example.org/cb/example.com` |
| Client Settings > Endpoints > Global Settings > General | Supported Grant Types | Include `AUTHORIZATION_CODE` |
| Client Settings > Endpoints > Global Settings > General | Supported Response Types | Include `CODE` |
Configure Client Basic Settings:
1. Navigate to **Client Settings > Basic Settings > General**.
2. Under **Client Type**, choose the `PUBLIC` radio button.
3. Under **Redirect URIs**, click **Add** and register the URI your client receives the response on. This page uses `https://client.example.org/cb/example.com`.
4. Click **Save Changes** to apply the updates.
Configure Client Endpoints:
1. Navigate to **Client Settings > Endpoints > Global Settings > General**.
2. Under **Select Grant Types**, select `AUTHORIZATION_CODE`.
3. Under **Select Response Types**, select `CODE`.
4. Click **Save Changes** to apply the updates.
### 6.2. Authorization Request
#### 6.2.1. Authorization Endpoint Request
As explained in \[OAuth2 Basic], Authlete's authorization API can validate authorization requests received by the authorization server on its behalf. Use the following `curl` command to make an authorization request from Authorization Server to the Authlete API. Make sure to replace ``, ``, `` and `` with your values.
```bash theme={null}
# Linux/Mac
curl -v -X POST "https://us.authlete.com/api//auth/authorization" \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{ "parameters": "response_type=code&client_id=&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb%2Fexample.com&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256" }'
# Windows (PowerShell)
curl.exe -v -X POST "https://us.authlete.com/api//auth/authorization" `
-H "Authorization: Bearer " `
-H "Content-Type: application/json" `
-d '{ \"parameters\": \"response_type=code&client_id=&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb%2Fexample.com&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256\" }'
```
Note that the request contains `code_challenge` parameter and `code_challenge_method` parameter. To implement PKCE, you need include and test these parameters.
#### 6.2.2 Authorization Response
If the request is valid, Authlete returns a response like this:
```json theme={null}
{
"action": "INTERACTION",
"resultCode": "A004001",
"resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 933860280) for the authorization request from the client (ID = 2800496004). [response_type=code, openid=false]",
"ticket": "cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE",
"client": {...},
"service": {...},
}
```
Assuming that the resource owner has already been authenticated and has obtained consent, the authorization server calls the issue endpoint to obtain an authorization code. To make a token request, execute the following `curl` command. Make sure to replace ``, ``, `` and `` with your values.
```bash theme={null}
# Linux/Mac
curl -X POST "https://us.authlete.com/api//auth/authorization/issue" \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{ "ticket": "","subject": "testuser01"}'
# Windows (PowerShell)
curl.exe -X POST "https://us.authlete.com/api//auth/authorization/issue" `
-H "Authorization: Bearer " `
-H "Content-Type: application/json" `
-d '{ \"ticket\": \"Ticket e.g. cElOaH9j4mS6AiIGR9oLqHlDn9jpvcNjqSgyRqfcmAE\",\"subject\": \"testuser01\"}'
```
If the request is valid, Authlete generates the following response.
```json theme={null}
{
"action": "LOCATION",
"authorizationCode": "ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI",
"idToken": null,
"jwtAccessToken": null,
"responseContent": "https://client.example.org/cb/example.com?code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI&iss=https%3A%2F%2Fauthlete.com",
"resultCode": "A040001",
"resultMessage": "[A040001] The authorization request was processed successfully.",
"ticketInfo": {}
}
```
### 6.3 Token Request
#### 6.3.1 Token Request with CURL
We assume the authorization server makes a redirection response to the user agent, and then the user agent makes the following HTTP GET request to the client.
```bash theme={null}
GET /cb/example.com?code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI HTTP/1.1
Host: client.example.org
```
To make a token request, execute the following `curl` command. Make sure to replace ``, ``, `` and `` with your values.
```bash theme={null}
# Linux/Mac
curl -v -X POST https://us.authlete.com/api//auth/token \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{ "parameters": "grant_type=authorization_code&code=&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb%2Fexample.com&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", "clientId": "" }'
# Windows (PowerShell)
curl.exe -v -X POST https://us.authlete.com/api//auth/token `
-H "Content-Type: application/json" `
-H "Authorization: Bearer " `
-d '{\"parameters\": \"grant_type=authorization_code&code=&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb%2Fexample.com&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\", \"clientId\": \"\" }'
```
#### 6.3.2. Token Response
If the authorization code and the code verifier you entered are valid, an access token is returned in the form of JSON like the following.
```json theme={null}
{
"resultMessage" : "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
"action" : "OK",
"clientIdAliasUsed" : false,
"subject" : "testuser01",
"resultCode" : "A050001",
"grantType" : "AUTHORIZATION_CODE",
"accessToken" : "7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY",
"refreshToken" : "T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE",
"responseContent" : "{\"access_token\":\"7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY\",\"token_type\":\"Bearer\",\"expires_in\":86400,\"scope\":null,\"refresh_token\":\"T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE\"}",
"accessTokenDuration" : 86400,
"accessTokenExpiresAt" : 1730552811449,
"refreshTokenDuration" : 864000,
"refreshTokenExpiresAt" : 1731330411449,
"clientAuthMethod" : "NONE",
"clientId" : 26478243745571
}
```
The refresh token is issued because the refresh token grant is enabled on both the service and the client. With the grant left out of either one, the response carries no `refreshToken`.
Congratulations! You have succeeded in getting an access token using the authorization code flow secured by PKCE.
### 6.4. PKCE Configuration
Authlete provides configuration items for PKCE, on the service and on the client. In the [Authlete Management Console](https://console.authlete.com/) they sit under **Proof Key for Code Exchange (PKCE)** on the Authorization screen.
#### Service PKCE Settings
To enable PKCE in Service Settings:
1. Navigate to **Service Settings > Endpoints > Authorization > General**.
2. Under **Proof Key for Code Exchange (PKCE)**, enable **Require PKCE** and **Require S256 for Code Challenge Method**.
3. Click **Save Changes** to apply the updates.
#### Client PKCE Settings
To enable PKCE in Client Settings:
1. Navigate to **Client Settings > Endpoints > Authorization > General**.
2. Under **Proof Key for Code Exchange (PKCE)**, enable **Require PKCE** and **Require S256 for Code Challenge Method**.
3. Click **Save Changes** to apply the updates.
If Proof Key for Code Exchange (PKCE) is `enabled`, the `code_challenge` request parameter is always required for authorization requests using authorization code flow. The default value is `disabled`.
Enable `Proof Key for Code Exchange (PKCE)` for better security. It makes the authorization server reject any authorization request using the authorization code flow that is not accompanied with `code_challenge` request parameter.
# Rich Authorization Requests (RAR)
Source: https://developers.authlete.com/protocols-and-flows/protocol-extensions/rich-authorization-requests-rar-spec
Overview of OAuth 2.0 Rich Authorization Requests (RAR) for fine-grained permission representation
## Preface
[RFC 9396: OAuth 2.0 Rich Authorization Requests](https://www.rfc-editor.org/rfc/rfc9396)
(RAR for short) is a proposed standard extension of OAuth 2.0. It allows clients to use JSON data structures to specify more fine-grained authorization requirements than an existing capability of "scope" parameter.
This article describes the support for RAR on Authlete and how you can use it to represent fine-grained permissions.
## RAR overview
"scope" is a mechanism introduced by [OAuth 2 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)
specification for representing the permissions granted to a third party by a resource owner. You can assign a specific semantic to a scope to represent a permission or an option.
For instance: OpenID Connect specification defines the "openid" scope as well as "profile" among others. While "profile" grants to 3rd party access to [specific attributes](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)
of the user profile, the "openid" scope has a different semantic: it implies an option to generate an ID token and an access permission to UserInfo endpoint.
With the broad usage of OAuth 2, new use cases rise and as such new technical solutions are required. The RAR specification addresses scenarios where thrid parties are required to express an intent with more context for user approval, e.g., online payments, file sharing, health exams etc.
### RAR structure
RAR defines a common JSON structure for the finer grained permissions, where each permission has a "type" attribute, and optionally "locations", "actions", "datatypes", "identifier", "privileges", and others defined by the parties. You can check the structure description under [Request parameter "authorization\_details"](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar#section-2)
section.
The idea is that an authorization server (AS), or an ecosystem that the AS is in, will define different "types" identifiers and their semantics: what do they represent, their structure, their meaning to end users, and association to the other attributes. It will precisely define the permission to be requested to end user.
The "locations" attribute has the same concept of the [Resource Indicators mechanism](/configuration-reference/tokens-and-claims/resource-indicators), where the location specifies a resource server URI that the permission is about. With the attribute "identifier", you can point to the specific resource on requesting permissions. While the "locations" indicate a branch of resources, the "identifier" points to a single resource, for short.
The "actions" is a big step forward as it can represent for instance: the action on a filesystem, the action on the bank account, an action on medical device, an act on an appliance, a permission on a database, etc.
The "datatypes" and "privileges" can be used to represent specifics of the entitlements been requested.
## Configuration
See [Configuring RAR](/configuration-reference/tokens-and-claims/rich-authorization-requests-rar).
# OpenID for Verifiable Credential Issuance
Source: https://developers.authlete.com/protocols-and-flows/verifiable-credentials/openid-for-verifiable-credential-issuance
Explanation about OpenID for Verifiable Credential Issuance and how Authlete supports the specification.
For **Authlete 2.x** documentation, see [2.x version](/v2/protocols-and-flows/verifiable-credentials/oid4vci).
## 1. Introduction
This document explains the "**[OpenID for Verifiable Credential Issuance][OID4VCI]**" (**OID4VCI**) specification and how Authlete supports the specification.
This document elaborately explains overviews and details of various concepts, using well over 100 diagrams. All the explanations carefully avoid assuming prior knowledge of concepts not yet explained, allowing readers to understand the content without the need to navigate back and forth within the document. Additionally, the document refrains from delving into excessive details while presenting the overall picture to prevent readers from getting disoriented. These considerations make this document significantly more readable than the specification itself. Therefore, reading this document beforehand will be a great help when you read the specification.
### 1.1. Specification Differences
Authlete completed its implementation of the first Implementer's Draft of
OID4VCI 1.0 (hereafter [OID4VCI 1.0 ID1][OID4VCI_10_ID1]) in 2023. After that,
the OID4VCI 1.0 specification went through the second Implementer's Draft and
was finalized in September 2025 (hereafter [OID4VCI 1.0 Final][OID4VCI_10_FINAL]).
Starting from version 3.0.28, Authlete supports not only OID4VCI 1.0 ID1 but
also OID4VCI 1.0 Final. However, due to the large number of breaking changes
introduced between OID4VCI 1.0 ID1 and OID4VCI 1.0 Final, Authlete has adopted
a different approach from the conventional method of providing configuration
options for each individual specification difference. Instead, it allows
switching support in bulk between OID4VCI 1.0 ID1 and OID4VCI 1.0 Final.
Specifically, a property named `oid4vciVersion` has been
introduced in `Service`, and behavior is determined based on its value. If this
property is not set or is set to `1.0-ID1`, Authlete operates in compliance
with OID4VCI 1.0 ID1. On the other hand, if this property is set and its value
is `1.0` or `1.0-Final`, Authlete operates in compliance with OID4VCI 1.0 Final.
| Value of oid4vciVersion | Specification Applied |
| :- | :- |
| Not set | 1.0-ID1 |
| `1.0-ID1` | 1.0-ID1 |
| `1.0` | 1.0-Final |
| `1.0-Final` | 1.0-Final |
In this document, notes regarding breaking changes between 1.0-ID1 and 1.0-Final
are indicated using the notation **\[BREAKING CHANGE]**.
### 1.2. Change History
Revision History
Date
Changes
2023-10-22
The initial version was published.
2023-10-27
Some subsections were added to the "OID4VCI Implementation" section.
2023-11-15
Some explanations and diagrams were updated to align with the specification changes below.
The type of the `credentials_supported` credential issuer
metadata has been changed from a JSON array to a JSON object.
The type of elements in the `credentials` array in a credential
offer has been restricted to "string" only. JSON objects are no longer accepted.
The elements in the `credentials` array in a credential offer
reference the "keys" in the `credentials_supported` object,
not the values of the `scope` property of elements in the
`credentials_supported` object.
2023-11-24
The "OID4VCI Demo" section was added.
Some explanations and diagrams were updated to align with the specification change below.
The draft 01 of the SD-JWT VC
specification has renamed the `type` claim to `vct`,
which means "verifiable credential type".
2023-12-30
A section for mdoc demo was added.
Some explanations and diagrams were updated to align with the specification changes below.
The `credentials` property in a credential offer has been renamed to
`credential_configurations`.
The `user_pin_required` boolean property in a credential offer
has been replaced with the `tx_code` JSON object.
The `user_pin` parameter in a token request has been replaced with
`tx_code`.
The `credentials_supported` credential issuer metadata has been
renamed to `credential_configurations_supported`.
A RAR object with the type `openid_credential` requires the
`credential_configuration_id` property as mandatory.
The `format` property is no longer required.
The `proof_types_supported` parameter in a credential
issuer metadata has been renamed to `proof_types`.
The three properties related to credential response encryption
(`credential_encryption_jwk`,
`credential_response_encryption_alg`, and
`credential_response_encryption_enc`)
in a credential request have been packed into one JSON object,
`credential_response_encryption`.
Data Integrity Proof has been added as a method of key proof.
The specification of the SD-JWT VC format has been incorporated into the OID4VCI specification.
The `credential_definition` property, present in the RAR object,
credential request, and metadata for the `vc+sd-jwt`, has been removed.
Consequently, the `vct` property and the `claims` property,
formerly nested within the `credential_definition` property,
have been moved up one level.
`/.well-known/jwt-issuer` has been renamed to
`/.well-known/jwt-vc-issuer`.
The `_sd_hash` claim in the key binding JWT of an SD-JWT has been
renamed to `sd_hash`.
2024-01-31
Updated to align with the specification changes below.
The metadata related to credential response encryption has been packed into
one JSON object, `credential_response_encryption`.
The `format` property has been removed from credential responses.
The `format` property in a RAR object has resurrected. A RAR object
with the `openid_credential` type must include either the
`credential_configuration_id` property of the
`format` property.
The `credential_configurations` property in a credential offer
has been renamed to `credential_configuration_ids`.
2024-02-03
Updated to align with the specification changes below.
The `cryptographic_suites_supported` property in a
credential configuration has been renamed to
`credential_signing_alg_values_supported`.
The `proof_types` property in a credential configuration
has been renamed to `proof_types_supported`, and its
type has been changed from a string array to a JSON object containing
nested JSON objects.
2024-05-11
Added the "POTENTIAL Interop Event / Track 1 / Light Profile" section under
the "OID4VCI Demo" section.
2024-06-05
Updated the "POTENTIAL Interop Event / Track 1 / Light Profile" section
to reflect a bug fix for the mdoc payload.
(cf. authlete/cbor PR 10)
2024-06-06
Added the "POTENTIAL Interop Event / Track 2 / Light Profile" section under
the "OID4VCI Demo" section.
2024-06-11
Updated the "4.3.2.5. Step 5 : CWT Key Proof" section to reflect a bug fix
in the format of `COSE_Key`.
2024-06-28
Added the "POTENTIAL Interop Event / Track 2 / Full Profile" section under
the "OID4VCI Demo" section.
2026-03-19
Updated significantly to align with the completion of the OID4VCI 1.0
Final implementation. Among the many breaking changes introduced between
OID4VCI 1.0 ID1 and OID4VCI 1.0 Final, the following are particularly
impactful:
Issuance of `c_nonce` from the token endpoint and
credential endpoint was discontinued, and a dedicated nonce endpoint
was introduced specifically for issuing `c_nonce`.
In token request RAR objects, the
`credential_configuration_id` property became
mandatory, and the `format` property was removed.
In token response RAR objects, the
`credential_identifiers` property became mandatory.
The batch credential endpoint was removed, and the credential
endpoint was updated to allow issuance of multiple VCs.
Required parameters for credential requests were changed. Instead of
requiring `format` or `credential_identifier`,
the requirement was changed to `credential_configuration_id`
or `credential_identifier`.
The method for including key proofs in credential requests was
changed. The `proof` parameter was removed and replaced
with the `proofs` parameter.
Required parameters in credential responses were changed. The
`credential` parameter was removed and replaced with
the `credentials` parameter.
The specification for delayed credential responses was changed. The
`credential` parameter was removed and replaced with
`credentials`, and responses for cases where the
requested VCs are not yet available were changed from an
`issuance_pending` error to a 202 Accepted response.
The structure of credential issuer metadata was partially changed.
A `credential_metadata` object was introduced, with
`display` and `claims` moved into it, and the
structure of `claims` was changed.
## 2. OID4VCI Specification
The OID4VCI specification defines rules for **issuance** of verifiable credentials.
### 2.1. Core Technical Terms
#### 2.1.1. Verifiable Credential
"**Verifiable credential**" is a key technical term in the OID4VCI specification.
"Credential" in the term represents a collection of data about a user or users (or any identifiable entities). Given name, family name, and birthdate are examples of data about a user.
"Verifiable" in the term indicates that it is possible to verify that the data collection has not been tampered with. Technically speaking, it means that the data collection is digitally signed.
Digital driving licenses or health insurance cards stored on a mobile device are examples of verifiable credentials.
#### 2.1.2. Credential Issuer
Verifiable credentials are issued by a "**credential issuer**". Credential issuer is also a technical term. The specification describes behaviors of a credential issuer.
#### 2.1.3. Access Token
To obtain a verifiable credential from a credential issuer, the requester of the issuance must present an "**access token**" to the credential issuer. The access token here is the one defined in [RFC 6749][RFC_6749], which is the core specification of **OAuth 2.0**.
#### 2.1.4. Authorization Server
Access tokens are issued by an "**authorization server**". The fundamental behaviors of an authorization server are defined in [RFC 6749][RFC_6749], and there are many other standard specifications around RFC 6749 that add extra functionalities to an authorization server. The OID4VCI specification also defines additional requirements for an authorization server so that an authorization server can issue access tokens that can be used for the issuance of verifiable credentials.
#### 2.1.5. Wallet
In the OID4VCI specification, a software application that communicates with an authorization server and a credential issuer to obtain a verifiable credential is referred to as a "**wallet**". Technically speaking, within the context of issuing verifiable credentials, a wallet acts as a "**client application**" of OAuth 2.0. Thus, from a technical perspective, the terms wallet and client application are interchangeable in the context of the OID4VCI specification.
However, please note that in the context of "OpenID for Verifiable Presentations", which this document does not explain, a wallet acts as a server, not as a client application.
#### 2.1.6. Relationship
The following diagram illustrates the relationship among the core technical terms.
### 2.2. Access Token Issuance Overview
#### 2.2.1. Pre-Authorized Code Flow
The specification defines multiple methods for issuing access tokens that are usable for the issuance of verifiable credentials.
One of these methods is entirely new. The new one is referred to as the "**pre-authorized code flow**". In the flow, as the first step, a wallet obtains a "**pre-authorized code**" from a credential issuer.
Then, the wallet presents the pre-authorized code at the "**token endpoint**" ([RFC 6749][RFC_6749], [3.2. Token Endpoint][RFC_6749_3_2]) of an authorization server.
In return, the wallet receives an access token.
As explained in the previous section, the wallet presents the access token to the credential issuer. To be specific, the wallet presents the access token at the "**credential endpoint**" of the credential issuer.
In return, the wallet receives a verifiable credential.
The diagram below is an overview of the pre-authorized code flow.
#### 2.2.2. Authorization Code Flow
The other methods than the pre-authorized code flow are extensions of the traditional "**authorization code flow**" ([RFC 6749][RFC_6749], [4.1. Authorization Code Grant][RFC_6749_4_1]).
Let's review the flow.
In the authorization code flow, as the first step, a client application (which is a wallet in the OID4VCI context) sends an "**authorization request**" to the "**authorization endpoint**" ([RFC 6749][RFC_6749], [3.1. Authorization Endpoint][RFC_6749_3_1]) of an authorization server via a web browser.
On receiving the authorization request, the authorization server begins communicating with a user via the web browser. After obtaining consent from the user, the authorization server issues an "**authorization code**" to the client application.
Then, the client application sends a "**token request**" including the authorization code to the token endpoint of the authorization server.
In return, the client application receives an access token.
The process after getting an access token is the same as the one of the pre-authorized code flow. The client application presents the access token at the credential endpoint of the credential issuer.
In return, the client application receives a verifiable credential.
The diagram below illustrates the authorization code flow followed by the credential issuance.
The OID4VCI specification extends the authorization request in the authorization code flow. To be specific, the specification utilizes the following request parameters of an authorization request.
1. The `issuer_state` request parameter.
2. The `authorization_details` request parameter.
3. The `scope` request parameter.
The `issuer_state` request parameter is a new one defined by the OID4VCI specification.
The `authorization_details` request parameter is defined in "[RFC 9396 OAuth 2.0 Rich Authorization Requests][RFC_9396]", a.k.a. "**RAR**".
The `scope` request parameter is a traditional one defined in "[RFC 6749 The OAuth 2.0 Authorization Framework][RFC_6749]".
As of this writing, the OID4VCI specification does not address the cases in which the above request parameters are used in combination. It remains uncertain whether the mechanisms using these request parameters are mutually exclusive or can coexist.
Authlete's current implementation considers all these mechanisms and generates an access token that can be used for the issuance of all the verifiable credentials requested via the request parameters.
#### 2.2.3. Authorization Code Flow + issuer\_state
The `issuer_state` request parameter is defined in the OID4VCI specification. To use the request parameter, a wallet needs to obtain an "**issuer state**" from a credential issuer before making an authorization request.
Then, the wallet makes an authorization request with the issuer state included as the value of the `issuer_state` request parameter.
The remaining part after the authorization request is the same as that of the normal authorization code flow.
The diagram below illustrates the authorization code flow with an issuer state.
#### 2.2.4. Authorization Code Flow + authorization\_details
The RAR specification ([RFC 9396][RFC_9396]) defines the `authorization_details` parameter as a general-purpose parameter that conveys detailed information about authorization. It is up to deployments how to use the parameter.
The value of the parameter is a JSON array, and each element of the array is a JSON object. We call the object "**RAR object**".
The RAR object is flexible. Any properties can be put in the object. However, the RAR specification predefines several properties that are expected to be commonly used across the foreseeable use cases.
Among such predefined properties, the `"type"` property is the only mandatory property. The property indicates what the RAR object represents.
And, the OID4VCI specification defines a special value, `"openid_credential"`, for the `"type"` property in order to indicate that the RAR object conveys information about the verifiable credential that the wallet wants.
#### 2.2.5. Authorization Code Flow + scope
The `scope` request parameter is one of the traditional ones defined in the core specification of OAuth 2.0 ([RFC 6749][RFC_6749]). Its original usage is to list permissions that the client application wants. If the user approves the request, the authorization server issues an access token that has the requested permissions.
Historically, the `scope` request parameter has been used for purposes beyond its original intent, and the OID4VCI specification has similarly extended the use of the `scope` request parameter.
A credential issuer manages the types of verifiable credentials it can issue as "**credential configurations**", and publishes the list of the credential configurations at a certain place. Each credential configuration may have a `"scope"` property.
A wallet may include values of the `"scope"` property in the `scope` request parameter to indicate which type of verifiable credentials it wants.
Multiple credential configurations may have the same value for the `"scope"` property.
Because the core specification of OAuth 2.0 (RFC 6749) requires that authorization servers ignore unknown `scope` values, it is likely that you need to explicitly pre-register the values of the `"scope"` property of the supported credentials as supported scope values with your authorization server.
In addition, if OpenID Federation is employed, cares need to be taken to prevent metadata policies from dropping `scope` values for verifiable credential issuance unintentionally.
### 2.3. Credential Issuance Overview
Once a wallet obtains an access token from an authorization server, the wallet can request a credential issuer to issue a verifiable credential by presenting the access token.
In the foundational procedure, the wallet sends a credential request with an access token to the credential endpoint of the credential issuer.
The credential issuer issues the requested verifiable credential as a response.
#### 2.3.1. Deferred Credential Issuance
However, it is possible that the verifiable credential is not yet available when requested. For example, there might be time-consuming offline processes happening in the background.
In such a case, the credential issuer issues a "**transaction ID**" instead.
In this case, the wallet waits until the verifiable credential issuance is ready. Then, it presents the previously received transaction ID and access token to the "**deferred credential endpoint**".
The credential issuer issues the requested verifiable credential as a response.
If the verifiable credential is still not ready, the deferred credential endpoint will return an error indicating it (e.g., `"error":"issuance_pending"`). In this case, the wallet will make a "**deferred credential request**" again later.
#### 2.3.2. Batch Credential Issuance
**\[BREAKING CHANGE]**
The batch credential endpoint has been removed in OID4VCI 1.0 Final.
The wallet may want to obtain multiple verifiable credentials at a time. For such use cases, there is a "**batch credential endpoint**".
The wallet sends a "**batch credential request**" with an access token to the batch credential endpoint.
The endpoint returns multiple verifiable credentials and/or transaction IDs.
Each transaction ID can be used to obtain a verifiable credential from the deferred credential endpoint.
### 2.4. Access Token Issuance Details
In the previous sections, we've provided an overview of access token issuance and credential issuance. In this section, we will delve into the technical details of access token issuance.
#### 2.4.1. Credential Offer
When a credential issuer issues a pre-authorized code, it provides a "**credential offer**" that includes the pre-authorized code instead of issuing it directly.
Likewise, an issuer state is also included as part of a credential offer. A credential offer may contain either a pre-authorized code, an issuer state, or both.
A credential offer contains other information, too. It always contains the identifier of the credential issuer.
Also, a credential offer contains information about the verifiable credentials that the credential issuer offers.
##### 2.4.1.1. Credential Offer Issuance by Value
To transmit the credential offer to the wallet, a URL is employed. This URL is a "**credential offer endpoint**" with a query parameter `credential_offer`. The value of the query parameter is the content of the credential offer.
If the URL is accessed in some way and the access can be processed by the wallet, the wallet can receive the credential offer.
However, a key issue here is how to trigger the access. The OID4VCI specification anticipates an HTTP GET request or HTTP redirection initiated by the credential issuer, but it does not define how the credential issuer and the wallet should agree upon the method of triggering the access.
Another issue is that the credential issuer will not be able to know the value of the credential offer endpoint when providing a credential offer. The specification defines a client metadata called `credential_offer_endpoint`, which represents the wallet's credential offer endpoint. However, especially in cases where a QR code representing the URL is used as suggested by the specification, the credential issuer do not have access to the wallet's metadata because the credential issuer cannot know for which wallet it is going to provide a credential offer. For such cases, `openid-credential-offer://` is defined as the fallback credential offer endpoint.
For the reasons mentioned above, credential issuers will implement the generation and transmission of the credential offer in their respective custom ways. Therefore, interoperability cannot be expected to a great extent.
##### 2.4.1.2. Credential Offer Issuance by Reference
A credential offer may be passed to the wallet by reference. To be specific, the URL may contain the location of the issued credential offer instead of its content. In that case, a `credential_offer_uri` query parameter is used to point to the location.
The value of the `credential_offer_uri` query parameter points to an endpoint that returns the content of the issued credential offer.
By accessing the URI,
the wallet can obtain the content of the issued credential offer.
##### 2.4.1.3. Credential Offer Content
The actual content of a credential offer is a JSON object.
The identifier of the credential issuer is put as the value of the `"credential_issuer"` property.
The information about the verifiable credentials that the credential issuer
offers is put in the `"credential_configuration_ids"` array.
The specific details of the array elements are discussed later.
When issued, an issuer state is placed at a somewhat nested location.
There is a `"grants"` property as a top-level property in a credential offer. The value of the `"grants"` property is a JSON object. The keys within the `"grants"` JSON object are identifiers of grant types, such as `authorization_code`.
The value of each entry in the `"grants"` JSON object is another JSON object containing properties related to the grant type represented by the corresponding key.
In the case of the issuer state, the value of the issued issuer state is placed as the value of the `"issuer_state"` property within the `"authorization_code"` JSON object, which is within the `"grants"` JSON object.
Similarly, in the case of the pre-authorized code, the value of the issued pre-authorized code is placed as the value of the `"pre-authorized_code"` property within the `"urn:ietf:params:oauth:grant-type:pre-authorized_code"` JSON object, which is within the `"grants"` JSON object. The string `"urn:ietf:params:oauth:grant-type:pre-authorized_code"` here is the new identifier assigned to the pre-authorized code flow.
The `"urn:ietf:params:oauth:grant-type:pre-authorized_code"` JSON object may contain a `"tx_code"` JSON object, which contains information about a "**transaction code**".
When the property is provided, the token request using the pre-authorized code will have to include a transaction code. Further details about this are described later.
The diagram below illustrates an overview of the structure of the content of a credential offer.
##### 2.4.1.4. "credential\_configuration\_ids" in Credential Offer
The `"credential_configuration_ids"` property in a credential offer holds information
about the verifiable credentials that the credential issuer offers.
The value of the property is a JSON array. The elements in the array
are strings.
The values of the elements are the identifiers of the credential configurations.
#### 2.4.2. Pre-Authorized Code Flow Details
Once a wallet obtains a pre-authorized code, it can make a token request with the pre-authorized code.
The table below lists the request parameters required for a token request to comply with the pre-authorized code flow.
| Parameter | Description |
| :- | :- |
| `grant_type` | The value must be `"urn:ietf:params:oauth:grant-type:pre-authorized_code"`. |
| `pre-authorized_code` | A pre-authorized code. |
| `tx_code` | A transaction code. Required if a credential offer contains a `tx_code` object. |
The `tx_code` parameter is required if the pre-authorized code has been issued
with a `tx_code` object like below. In this case, it is expected that the user
will receive a transaction code corresponding to the pre-authorized code
through some out-of-band mechanism.
```json theme={null}
{
"credential_issuer": "...",
"credential_configuration_ids": [
"..."
],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "...",
"tx_code": {
"length": 6,
"input_mode": "numeric",
"description": "Input the one-time code sent via email"
}
}
}
}
```
The `tx_code` object may contain the following parameters to help the wallet prepare a UI component for the user to input the transaction code.
| Parameter | Description |
| :- | :- |
| `length` | The length of the transaction code. |
| `input_mode` | The input mode for the transaction code. The pre-defined values are `"numeric"` and `"text"`. |
| `description` | The information regarding the transaction code, such as the delivery channel. |
Also, additional request parameters related to client authentication may be required. For example, when the `private_key_jwt` client authentication is employed, the `client_assertion` and `client_assertion_type` request parameters are required.
One ambitious attempt of the OID4VCI specification is that, depending on the configuration of the authorization server
(to be specific, when the `pre-authorized_grant_anonymous_access_supported` server
metadata is `true`), in the pre-authorized code flow, it may be permissible to make token requests that do
not contain any information to identify the client application. If such a token request were actually made, an access
token not associated with a specific client application would be issued.
The following is an example of a token request without client authentication
using the pre-authorized code flow, excerpted from the OID4VCI 1.0 ID1
specification.
```http theme={null}
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code
&pre-authorized_code=SplxlOBeZQQYbYS6WxSbIA
&tx_code=493536
```
The token endpoint will return a response including an access token as usual.
The following is an example of token response excerpted from the OID4VCI 1.0
ID1 specification.
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ",
"token_type": "bearer",
"expires_in": 86400,
"c_nonce": "tZignsnFbp",
"c_nonce_expires_in": 86400
}
```
As the example above shows, when the issued access token can be used for
the issuance of verifiable credentials, the token response may contain
the `c_nonce` and `c_nonce_expires_in` response parameters,
in addition to the traditional response parameters (`access_token`,
`token_type` and `expires_in`) defined in the core specification of
OAuth 2.0 ([RFC 6749][RFC_6749]). Further details regarding these response
parameters will be explained later.
**\[BREAKING CHANGE]**
In OID4VCI 1.0 Final, the token response does not include `c_nonce`
or `c_nonce_expires_in`, because `c_nonce` is
now issued from the newly introduced nonce endpoint.
#### 2.4.3. Issuable Credentials
While there are multiple methods available to issue access tokens for verifiable credentials, from the perspective of access token implementation, they all converge on one common goal. That is to associate information about the types of verifiable credentials that can be issued with the access token.
In this document, we call such information "**issuable credential**". But, please keep in mind that "issuable credential" is not an official term.
Elements in the `"credential_configuration_ids"` array in a credential offer are JSON strings.
They indirectly specify issuable credentials by referencing credential configurations.
The number of elements in the `"credential_configuration_ids"` array
in this example is one, but the array may contain multiple elements.
Such elements compose the set of issuable credentials represented by the credential offer.
Therefore, it can be said that a token request using the pre-authorized code flow requests an access token associated with the issuable credentials specified in the credential offer that contains the pre-authorized code.
Similarly, it can be said that an authorization request with the `issuer_state` request parameter requests an access token associated with the issuable credentials specified in the credential offer that contains the issuer state.
A RAR object with `"type":"openid_credential"` may specify a credential configuration
using the `"credential_configuration_id"` property as the base
of an issuable credential.
A RAR object with the `openid_credential` type may use the `format` property instead.
In a RAR object with the `openid_credential` type,
the `credential_configuration_id` property and
the `format` property are mutually exclusive.
**\[BREAKING CHANGE]**
OID4VCI 1.0 Final has removed the `format` parameter. As a result,
in OID4VCI 1.0 Final, an RAR object whose `type` is
`openid_credential` must always include
`credential_configuration_id`.
In OID4VCI 1.0 ID1, it was possible to request a credential that was not
associated with any credential configuration. However, due to this change in
the RAR object specification, as well as the change requiring that a credential
request must include either the `credential_configuration_id`
parameter or the `credential_identifier` parameter, it is no
longer possible to request such credentials.
As a result, one inconsistency inherent in the OID4VCI specification has been
resolved. In OID4VCI 1.0 ID1, there were cases where a credential configuration
could not be identified, despite the presence of requirements that assumed it
could be identified (for example, the requirement that the `proof`
request parameter is mandatory when `proof_types_supported`
is defined in the credential configuration).
Values in the `scope` request parameter may indirectly specify one or more issuable credentials
via the `"scope"` property of credential configurations.
Although the current draft of the OID4VCI specification does not explicitly address the cases where these mechanisms to specify issuable credentials are used simultaneously, in our interpretation and implementation, all the issuable credentials specified by these mechanisms are combined into a single set.
The diagram below illustrates the overall picture of the mechanisms to specify issuable credentials.
You might be curious about the structures of the credential information and the RAR object in the diagram.
However, before delving into those specifics, we need to walk through the formats of verifiable credentials.
### 2.5. Credential Verification
In order to discuss the formats of verifiable credentials, we need to understand their intended purpose. Let's examine the verification steps of a verifiable credential, one by one.
First, the credential issuer prepares data to include in a verifiable credential. In this example, we use name, birthdate, and address.
To sign the data, the credential issuer prepares a pair of a private key and a public key.
Then, the credential issuer signs the data with the private key. As a result, a signature is generated.
The set of the data and the signature is a verifiable credential.
The credential issuer passes the verifiable credential to the wallet.
If the wallet wants to verify the signature of the verifiable credential, it obtains the public key from the credential issuer through some means or other, and uses it to verify the signature. If the verification succeeds, it can be ensured that the verifiable credential has been issued by the legitimate credential issuer and the content has not been tampered with.
The key distinction between the use of a verifiable credential and an ID token is that the "**holder**" of a verifiable credential may present it to others. Before presented, a verifiable credential is transformed into a "**verifiable presentation**".
However, from a technical perspective, it is possible for a verifiable credential and its corresponding verifiable presentation to be exactly identical.
The wallet presents the verifiable presentation to others. The external entities receiving the verifiable presentation are referred to as "**verifiers**" because they are supposed to verify the verifiable presentation before providing services to the holder.
If the verifier wants to verify the signature of the verifiable presentation, it obtains the public key from the credential issuer through some means or other, and uses it to verify the signature.
#### 2.5.1. Key Binding
By the way, how can a verifier confirm the following?
1. The verifiable presentation has been presented by the holder.
2. The verifiable credential underlying the verifiable presentation has been issued by the credential issuer to the holder.
As for the first point, it can be achieved by having the holder provide a pair of a private key and a public key,
and including a signature made with the holder's private key in the verifiable presentation. The target data for the signature can be arbitrary as long as the data is presented together with the signature.
As for the second point, it can be achieved by including the holder's public key in the verifiable credential's data and having the credential issuer sign the entire data with its private key.
The cryptographic association between the verifiable credential and the holder in this manner is referred to as "**key binding**".
Strictly speaking, the public key used for the key binding mechanism is not always bound to the holder. Instead, it may be bound to one of the devices managed by the holder or even a device that may not be directly managed by the holder. Considering these cases, this mechanism is no longer referred to as "holder binding".
When the wallet requests a verifiable credential with cryptographic key binding, it includes the public key in the credential request. However, the credential issuer should not unconditionally accept the presented public key, as a malicious wallet could present an irrelevant public key.
Therefore, the wallet must demonstrate the legitimate ownership of the public key. To achieve this, the wallet generates a signature using the private key and presents it alongside the public key. This combination of the signature and the public key is commonly known as a "**key proof**".
If the credential issuer can validate the presented public key, it can create a verifiable credential with key binding.
The verifiable credential is then passed to the wallet,
which uses it to create a verifiable presentation. The wallet includes a signature created using the private key in the verifiable presentation.
The verifiable presentation is then passed to the verifier.
The verifier can verify the signature added by the wallet by using the public key embedded in the verifiable presentation.
The following diagram illustrates the overall picture of the credential verification explained so far.
### 2.6. Selective Disclosure
When presenting a verifiable presentation, the holder may choose to disclose only certain parts of the verifiable credential's content. For example, if the verifiable credential contains name, birthdate, and address, the holder may opt to disclose only the name and birthdate while omitting the address information.
We call the act of selectively disclosing chosen information like this "**selective disclosure**".
However, omitting information without special consideration will result in the failure of the signature verification. This is because the dataset targeted for signing differs from the dataset received by the verifier.
There are several methods to achieve selective disclosure without invalidating the signature. BBS+ (Boneh-Lynn-Shacham signature plus) and CL Signatures (Camenisch-Lysyanskaya Signatures) are examples and may seem promising. However, in the real world, the adoption of these methods depends on various factors, as outlined below, and it is not always the case that solutions based on academically elegant theories become widespread:
1. The complexity of the theory.
2. The ease of implementation.
3. Monetary costs for licensing.
4. Legal restrictions on usage.
5. Support from Hardware Security Module (HSM) products.
6. How much the industry believes the algorithm is robust/secure.
After thorough [comparison of credential profiles][CREDENTIAL_PROFILE_COMPARISON] and hackathons, the industry has decided to create a new format called "**[Selective Disclosure for JWTs (SD-JWT)][SD_JWT]**".
Selective Disclosure for JWTs (SD-JWT) was published in November 2025 as
RFC 9901: Selective
Disclosure for JSON Web Tokens. Authlete is listed as one of the
authors of this specification.
#### 2.6.1. SD-JWT
SD-JWT ([RFC 9901][RFC_9901]) is a format that utilizes JWT
([RFC 7519 JSON Web Token (JWT)][RFC_7519]) to achieve selective disclosure.
The payload part of a normal JWT contains pairs of a claim name and its value.
To make such a claim "selectively-disclosable" using the SD-JWT format, you first extract the claim.
Then, you add an arbitrary salt to it,
and create a JSON array including the salt, the claim name and the claim value.
The next step is to encode that JSON array in base64url. In the SD-JWT specification, the resulting string is referred to as "**disclosure**".
The original claim is replaced with the digest value of the disclosure. The digest value is base64url-encoded and placed in the `"_sd"` array, which is inserted where the original claim was located.
The same process is applied to other claims that need to be made selectively-disclosable.
By concatenating the "**issuer-signed JWT**" with the disclosures using tildes (`~`), a single string is formed.
This resulting string is an SD-JWT.
The next step is optional, but if you want to perform key binding, please prepare a key pair.
Then, embed the public key into the issuer-signed JWT,
Instead of embedding the public key, a reference to it may be included.
sign a specific dataset defined in the specification, and place the resulting JWT at the end of the previously created SD-JWT. The JWT is called "**key binding JWT**".
Note that the responsibility for generating a key binding JWT lies with the holder/wallet, not the credential issuer. Therefore, even if the verifiable credential format is based on SD-JWT, the verifiable credential does not include a key binding JWT when it is issued by the credential issuer.
The following diagram illustrates the overall process of generating SD-JWT that we have explained so far.
The key point is that if the recipient of an SD-JWT doesn't receive all the disclosures, they can only reconstruct claims corresponding to the received disclosures. Importantly, even in the case, the signature of the issuer-signed JWT remains valid.
For more detailed information, please refer to the SD-JWT specification
([RFC 9901][RFC_9901]) itself. Additionally, you can find useful information
in the README of the open-source SD-JWT library for the Java programming
language, [authlete/sd-jwt][AUTHLETE_SD_JWT].
### 2.7. Verifiable Credential Formats
#### 2.7.1. Confusion Surrounding Verifiable Credential Formats
The content of this section describes the situation as it was in 2023.
Although circumstances have since changed, it is retained as-is for
historical reference.
The confusion surrounding verifiable credential formats stems from the existence of multiple competing specifications, each with its own set of challenges, and many of which are still in development. Furthermore, the fact that organizations from various countries, regions, and industries are promoting different formats is also complicating the situation.
When it comes to verifiable credentials, many people associate them with the "**[W3C Verifiable Credentials Data Model][W3C_VCDM]**" (**W3C VCDM**). This is primarily because the document is often seen as the primary source defining the three-party Issuer-Holder-Verifier model. However, W3C VCDM itself is not flawless, and discussions are indeed ongoing. While [version 1.1][W3C_VCDM_1_1] was released on March 3, 2022, [version 2.0][W3C_VCDM_2_0] is currently under discussion.
When observed from an external perspective, what further complicates the situation is the specification titled "**[Securing Verifiable Credentials using JOSE and COSE][W3C_JOSE_COSE]**" (**[w3c/vc-jose-cose][W3C_JOSE_COSE_REPO]**). The specification states in the "Abstract" section that it *"defines how to secure credentials and presentations conforming to the VC-DATA-MODEL"*, but it conflicts with certain requirements of W3C VCDM. For instance, W3C VCDM mandates that the value of the `"typ"` header parameter must be `"JWT"`, but this requirement is not followed by w3c/vc-jose-cose. Additionally, W3C VCDM introduces the `"vc"` and `"vp"` claims as the designated places to embed verifiable credentials and verifiable presentations. However, w3c/vc-jose-cose does not utilize these `"vc"` and `"vp"` claims.
Furthermore, what adds to the confusion for newcomers is that the OID4VCI specification defines `jwt_vc_json`, `jwt_vc_json-ld`, and `ldp_vc` as credential format profiles based on W3C VCDM, but most people in the OpenID industry contributing to the specification do not seem inclined to support these credential format profiles. They are currently dedicating their efforts to the specification development and implementation of verifiable credential formats based on SD-JWT and "**[ISO/IEC 18013-5][ISO_IEC_18013_5]**" (Personal identification - ISO-compliant driving licence - Part 5: Mobile driving licence (**mDL**) application).
For those discussing OAuth and OpenID Connect, ISO/IEC 18013-5 is challenging to approach because its format is based on the less familiar binary format, "**Concise Binary Object Representation**" (**CBOR**) ([RFC 8949][RFC_8949]) and "**CBOR Object Signing and Encryption**" (**COSE**) ([RFC 9052][RFC_9052], [RFC 9053][RFC_9053]). Additionally, detailed technical articles about it are not widely available online because ISO standards must be purchased.
The public key distribution method for verifying verifiable credentials
is also a challenging issue. When a verifier receives a verifiable
credential, they cannot determine whether it was issued according to the
OID4VCI specification. Therefore, it is not ideal to force verifiers to
search for public keys starting from the metadata of credential issuers
(`/.well-known/openid-credential-issuer`).
As an alternative starting point, the well-known path
`/.well-known/jwt-issuer` was proposed in the specification
called "**[SD-JWT-based Verifiable Credentials][SD_JWT_VC]** (**SD-JWT VC**)".
The path name has been renamed to `/.well-known/jwt-vc-issuer`
later because the previous path name could easily clash with other JWT
issuer-related specifications. However, the issue remains that the path name
unnecessarily assumes that VC formats are based on JWT. Therefore, some people
are not favorable to the solution. In fact, the Italian ecosystem that
leverages [OpenID Federation][OIDFED] has opted not to use
`/.well-known/jwt-vc-issuer`.
Instead, they have chosen to define a new entity type identifier called
`openid_credential_issuer` and embed public keys for
verifiable credential verification in the
`"metadata"."openid_credential_issuer"`
object of the entity configuration.
As a related topic, a new [client authentication][CLIENT_AUTHENTICATION]
method called "**[OAuth 2.0 Attestation-Based Client Authentication][ATTESTATION_BASED_CLIENT_AUTH]**"
is currently under development. For the method, a wallet must obtain a
"**wallet attestation**" from an "**attester**" in advance because the
wallet needs to include this attestation when performing the client
authentication method. The recipient (e.g., an authorization server)
of the attestation must obtain the public key for verifying the
attestation's signature from the attester. Here, the distribution of
public keys for attestations is an issue similar to that described for
verifiable credentials above. And, here again,
`/.well-known/jwt-vc-issuer` is proposed as a
possible option. This is the very predicted concern, which makes it technically
impossible to run an attester and a credential issuer on the same server
(but whether running both on the same server is conceptually suitable or
not is a different matter). Additionally, whether the attestation format
is JWT or not is not essential.
However, a more serious issue regarding the attestation-based client
authentication is that agreement on the basic concept has not been fully
reached yet (cf. [ISSUE 61](https://github.com/vcstuff/draft-ietf-oauth-attestation-based-client-auth/issues/61)).
#### 2.7.2. Essential Functions of Verifiable Credential Formats
As mentioned in the previous section, there are many challenges related to verifiable credential formats. However, we believe that the essential functions expected from verifiable credential formats can be summarized as follows:
1. **Verifiability**
2. **Key Binding**
3. **Selective Disclosure**
In the following section, we will explain a verifiable credential format based on SD-JWT that can meet these requirements.
#### 2.7.3. SD-JWT VC
SD-JWT is a general-purpose data format and not a verifiable credential format in itself. However, by adding certain requirements, it is possible to define a verifiable credential format based on SD-JWT. "**[SD-JWT-based Verifiable Credentials][SD_JWT_VC]** (**SD-JWT VC**)" is a specification designed for this purpose.
Since an overview of SD-JWT has already been provided, we will only briefly introduce the key points of SD-JWT VC in the following table. Please refer to the SD-JWT VC specification for more details.
`kb+jwt` (as required by the SD-JWT specification)
Payload
`iat`
REQUIRED
The issuance time.
`aud`
REQUIRED
The intended recipient of the key binding JWT, which is typically the verifier.
`nonce`
REQUIRED
A string ensuring the freshness of the signature.
`sd_hash`
REQUIRED
The base64url-encoded hash digest over the issuer-signed JWT and the selected disclosures.
**\[BREAKING CHANGE]**
The media type for SD-JWT VC was initially `application/vc+sd-jwt`.
However, despite still being under coordination, the W3C formally registered
`application/vc` for a different purpose (whether by mistake
or intentionally), which made it impossible to use
`application/vc+sd-jwt` for SD-JWT VC. As a result,
`application/dc+sd-jwt` came to be used instead.
This issue is mentioned on
page 51 of the OAuth WG's
SD-JWT materials presented at IETF 121 Dublin (November 2024).
The SD-JWT VC specification itself also contains the following note:
Note that this draft used `vc+sd-jwt` as the value of the
`typ` header from its inception in July 2023 until November
2024 when it was changed to `dc+sd-jwt` to avoid conflict
with the vc media type name registered by the W3C's Verifiable
Credentials Data Model draft. In order to facilitate a minimally
disruptive transition, it is RECOMMENDED that Verifiers and Holders
accept both `vc+sd-jwt` and `dc+sd-jwt` as the
value of the `typ` header for a reasonable transitional period.
The actual value of the `vct` claim and additional claims specific to the credential type
in the issuer-signed JWT are determined by respective deployments, and they fall outside
the scope of the SD-JWT VC specification.
#### 2.7.4. Other Verifiable Credential Formats
This document does not describe other verifiable credential formats such as `jwt_vc_json`.
### 2.8. Credential Information for Access Token
Since we have covered verifiable credential formats, we can revisit the topic of credential information for access tokens.
#### 2.8.1. Credential Information in RAR Object
When the type of a RAR object is `openid_credential`, the RAR object contains
information about an issuable credential.
Such a RAR object must include the `credential_configuration_id`
property when conforming to OID4VCI 1.0 Final.
When conforming to OID4VCI 1.0 ID1, it must include either the
`credential_configuration_id` property or the `format`
property. These two properties are mutually exclusive.
**\[BREAKING CHANGE]**
OID4VCI 1.0 Final has removed the `format` parameter.
##### 2.8.1.1. RAR Object with The `credential_configuration_id` Property
The value of the `credential_configuration_id` property
points to an entry in the credential configurations in the credential
issuer metadata.
The following is an excerpt from OID4VCI 1.0 Final.
```json theme={null}
[
{
"type": "openid_credential",
"credential_configuration_id": "UniversityDegreeCredential"
}
]
```
On the other hand, the `mso_mdoc` format uses a `"claims"` property.
```json theme={null}
{
"type": "openid_credential",
"credential_configuration_id": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"given_name": {},
"family_name": {},
"birth_date": {}
},
"org.iso.18013.5.1.aamva": {
"organ_donor": {}
}
}
}
```
##### 2.8.1.2. RAR Object with The `format` Property
When the `format` property is used instead of the `credential_configuration_id`
property, the RAR object needs to contain complete information about an issuable credential.
The following is an excerpt from OID4VCI 1.0 ID1. As this is an older example,
please note that the value of `format` is `vc+sd-jwt` rather than `dc+sd-jwt`.
```json theme={null}
[
{
"type": "openid_credential",
"format": "vc+sd-jwt",
"vct": "SD_JWT_VC_example_in_OpenID4VCI"
}
]
```
#### 2.8.2. Credential Information in Credential Configuration
Information about credential configurationss is described as a part of "**credential issuer metadata**".
The metadata is a JSON object. It contains a `credential_configurations_supported` JSON object.
Each of the entries in the object represents credential configuration about a verifiable
credential supported by the credential issuer.
The properties within a credential configuration object are divided into
(1) those that may appear common to all credential configuration objects and
(2) those specific to the respective format.
For example, the `format` property is included in all corresponding credential
objects, whereas the `vct` property is present only in the SD-JWT VC format.
**\[BREAKING CHANGE]**
Breaking changes have also been made to the properties within credential
configurations. Major changes include the following:
* The introduction of the `credential_metadata` object.
* The `display` property has been moved into `credential_metadata`.
* The `claims` property has been moved into `credential_metadata`.
* The type of the `claims` property has been changed from a JSON object to a JSON array, and the representation of claim names has been changed from JSON object keys to `path` arrays.
* Elements in the `credential_signing_alg_values_supported` array were previously strings regardless of the VC format, but are now defined as numeric values when `format` is `mso_mdoc`.
### 2.9. Credential Issuance Details
The previous sections have covered the details about access token issuance. Next, we will delve into the details about credential issuance.
#### 2.9.1. Key Proof
As explained in the "Credential Verification" section, the wallet is expected to provide a key proof if it wishes to obtain a verifiable credential capable of key binding.
OID4VCI 1.0 ID1 defined the following three formats for key proof:
| Proof Type | Section |
| :- | :- |
| `jwt` | OID4VCI 1.0 ID1, [7.2.1.1. jwt Proof Type][OID4VCI_10_ID1_7_2_1_1] |
| `ldp_vp` | OID4VCI 1.0 ID1, [7.2.1.2. ldp\_vp Proof Type][OID4VCI_10_ID1_7_2_1_2] |
| `cwt` | OID4VCI 1.0 ID1, [7.2.1.2. cwt Proof Type][OID4VCI_10_ID1_7_2_1_3] |
On the other hand, OID4VCI 1.0 Final removed formats other than `jwt` and
introduced two new formats. As a result, the following three formats are
supported in OID4VCI 1.0 Final:
| Proof Type | Section |
| :- | :- |
| `jwt` | OID4VCI 1.0 Final, [F.1. jwt Proof Type][OID4VCI_10_FINAL_F1] |
| `di_vp` | OID4VCI 1.0 Final, [F.2. di\_vp Proof Type][OID4VCI_10_FINAL_F2] |
| `attestation` | OID4VCI 1.0 Final, [F.3. attestation Proof Type][OID4VCI_10_FINAL_F3] |
**\[BREAKING CHANGE]**
In OID4VCI 1.0 Final, the key proof formats `ldp_vp` and
`cwt` have been removed. Authlete implemented CWT-based key
proof and participated in the
POTENTIAL
interoperability event. However, the results of the interoperability
event revealed that improving interoperability for CWT-based key proof
would require a much more detailed specification. The
DCP WG
therefore decided that the required effort was not justified and
discontinued CWT-based key proof.
##### 2.9.1.1. Key Proof JWT
By definition, a key proof includes a public key or a reference to the key. In the case of the key proof based on JWT, several methods are employed to include this key information as listed below. A key proof JWT must use one and only one of the methods.
1. The `jwk` header parameter ([RFC 7515, 4.1.3][RFC_7515_4_1_3])
2. The `x5c` header parameter ([RFC 7515, 4.1.6][RFC_7515_4_1_6])
3. The `kid` header parameter ([RFC 7515, 4.1.4][RFC_7515_4_1_4])
You may have the impression that the combination of the `kid` and
`trust_chain` header parameters can specify a public key for key
binding. However, the combination won't work unless supplementary rules and
mechanisms are introduced.
All of these methods embed the key information in the header of a key proof JWT.
In the case of using the `jwk` header parameter, the public key is embedded in the format of "**JWK**" ([RFC 7517 JSON Web Key (JWK)][RFC_7517]).
The value of the `jwk` header parameter is a JSON object representing the public key.
The key proof JWT itself must be signed with the private key that corresponds to the public key.
In the case of using the `x5c` header parameter, an X.509 certificate for the public key needs to be prepared. The base64 representation of the DER representation of the certificate must be included in the `x5c` JSON array as the first element. If the certificate chain of the certificate is available, the chain can be included along with the certificate. See [RFC 7515, 4.1.6. "x5c" (X.509 Certificate Chain) Header Parameter][RFC_7515_4_1_6] for the details of the format that the `x5c` parameter expects.
In the case of using the `kid` header parameter, its value should be a [DID URL][W3C_DID_DID_URL] that can be resolved to the public key.
Next, let's take a look at the payload part of a key proof JWT.
The following table lists the claims that must or may appear in the payload part of a key proof JWT.
Name
Presence
Description
`iss`
CONDITIONALLY REQUIRED
The identifier of the client application (wallet).
`aud`
REQUIRED
The identifier of the credential issuer.
`iat`
REQUIRED
The issuance time.
`nonce`
CONDITIONALLY REQUIERD
The server-provided `c_nonce`.
* The `iss` claim represents the identifier of the client application (wallet) and is required in most cases. The only exception is when the access token is issued using the pre-authorized code flow, and the token request for the access token doesn't include any information to identify the client application. Such token requests are allowed only if the authorization server permits anonymous access in the pre-authorized code flow. The authorization server's support for this is indicated by the boolean server metadata, `pre-authorized_grant_anonymous_access_supported`.
* The `aud` claim represents the identifier of the credential issuer and is always required.
* The `iat` claim represents the issuance time of the key proof JWT, as defined in [RFC 7519, 4.1.6. "iat" (Issued At) Claim][RFC_7519_4_1_6]. This claim is always required.
* The `nonce` claim corresponds to the `c_nonce` included in the token response and/or the credential response. It is required when the token response contains the `c_nonce` parameter. Additionally, the credential issuer may mandate the `nonce` claim, even when the token response doesn't contain the `c_nonce` parameter. More information about `c_nonce` will be provided later.
The following table summarizes the requirements for a key proof JWT.
Exactly one of these header parameters must be included,
representing a public key or the reference to a public key.
`x5c`
`kid`
Payload
`iss`
CONDITIONALLY REQUIRED
The identifier of the client application (wallet).
`aud`
REQUIRED
The identifier of the credential issuer.
`iat`
REQUIRED
The issuance time.
`nonce`
CONDITIONALLY REQUIRED
The server-provided `c_nonce`.
The following is an example of key proof JWT.
```
eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJraWQiOiJHVURvZFB1SURJYllocmdmMHZsT3RNd1otczNiaVpFT3hWMFRTRjBKN3R3IiwieCI6InJjdU1FT1BYbVBJRlotc0Jvbkxyb1VvaTVYdGZ4NktWeFlFR09YMi1UbGsiLCJ5IjoiNUw1SUZrUFpNT0doTVpsNHRaSk9ISjdtckZQbnJSeV9RSURUOXRWZF9obyIsImFsZyI6IkVTMjU2In19.eyJpc3MiOiJodHRwczovL3dhbGxldC5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNjk3MjM0NzcwLCJub25jZSI6IjZhMzA3YjU1LWM4ZTEtNDg4YS05NjFlLTI1MzQ4ZmYzZTlkYSJ9.Vvo_X_fZyanUZ-y5X0yYtY7d70bbjMKUKqAoDiCBmP3NT4xNfTEpuYl9eu7vxc2fLf67ZdbSfw4rwEp8qvvWpA
```
The header and payload of the key proof JWT example are decoded into the following JSONs, respectively.
```json theme={null}
{
"alg": "ES256",
"typ": "openid4vci-proof+jwt",
"jwk": {
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"x": "rcuMEOPXmPIFZ-sBonLroUoi5Xtfx6KVxYEGOX2-Tlk",
"y": "5L5IFkPZMOGhMZl4tZJOHJ7mrFPnrRy_QIDT9tVd_ho",
"kid": "GUDodPuIDIbYhrgf0vlOtMwZ-s3biZEOxV0TSF0J7tw"
}
}
```
```json theme={null}
{
"iss": "https://wallet.example.com",
"aud": "https://issuer.example.com",
"iat": 1697234770,
"nonce": "6a307b55-c8e1-488a-961e-25348ff3e9da"
}
```
##### 2.9.1.2. Other Key Proofs
This document does not explain other key proofs.
Please refer to the OID4VCI specification for them.
#### 2.9.2. c\_nonce
**\[BREAKING CHANGE]**
In OID4VCI 1.0 ID1, `c_nonce` was issued by the token endpoint or
the credential endpoint. However, in OID4VCI 1.0 Final, those endpoints no
longer issue `c_nonce`. Instead, in OID4VCI 1.0 Final, the newly
introduced nonce endpoint issues `c_nonce`.
##### 2.9.2.1. c\_nonce in OID4VCI 1.0 ID1
As the primary countermeasure against key proof replay, the credential issuer may require the inclusion of the `nonce` claim in the key proof. The value of this claim is provided as a `c_nonce` response parameter from the authorization server or the credential issuer.
A token response from the authorization server may include the `c_nonce` response parameter along with the `c_nonce_expires_in` response parameter, which indicates the lifetime of the `c_nonce` in seconds.
The wallet uses the value of the `c_nonce` response parameter as the value of the `nonce` claim in a key proof JWT.
The wallet includes the key proof JWT in a credential request.
If the `nonce` claim is missing, although the credential issuer requires it, or if the specified `nonce` value has expired, the credential endpoint will return an error response. This error response includes either the expected `c_nonce` value or a fresh `c_nonce` value. Additionally, even when a valid `nonce` value is provided, the credential response may still include `c_nonce` for future use. In either case, `c_nonce` is included in a credential response if the credential issuer requires key proofs include the `nonce` claim.
If necessary, the wallet can regenerate a new key proof using the `c_nonce` value provided by the credential endpoint and make a credential request again with the fresh key proof.
The diagram below provides an overview of `c_nonce`.
##### 2.9.2.2. c\_nonce in OID4VCI 1.0 Final
In OID4VCI 1.0 Final, the nonce for key proof is issued from the credential
issuer's "Nonce Endpoint."
Requests to the nonce endpoint do not have a message body, but the HTTP method
is POST. Since the nonce endpoint is not a protected resource, there is no need
to include an access token in the request.
```http theme={null}
POST /nonce HTTP/1.1
Host: credential-issuer.example.com
Content-Length: 0
```
The response is a JSON object containing a `c_nonce` property. The wallet uses
the value of this `c_nonce` property as the nonce for key proof.
```json theme={null}
{
"c_nonce": "wKI4LT17ac15ES9bw8ac4"
}
```
##### 2.9.2.3. Implementation of c\_nonce
A closer look at the OID4VCI 1.0 ID1 specification shows that an access token
is always present in contexts where `c_nonce` appears. A token response is
returned when an access token is issued, and requests to the credential
endpoint include an access token.
Therefore, in OID4VCI 1.0 ID1, `c_nonce` can be managed in association with an
access token. If implemented this way, a separate `c_nonce` can be generated
for each access token, and each can have its own expiration time. As a side
effect, it also prevents a situation where different clients share and use the
same `c_nonce`.
On the other hand, in OID4VCI 1.0 Final (more precisely, from ID2 onward),
`c_nonce` cannot be managed in association with an access token. This is
because, by specification, the nonce endpoint is accessible without an access
token, meaning that no access token exists at the time the nonce endpoint
generates a `c_nonce`.
In the context of implementing the nonce endpoint, not only is there no access
token, but it is also impossible to identify which client application (wallet)
is making the request. Therefore, it is not possible to generate a `c_nonce`
per client. As a result, **in OID4VCI 1.0 Final, all clients (including
attackers) share the same `c_nonce`**.
The nonce endpoint returns the same `c_nonce` to any requester within the same
time window.
#### 2.9.3. Credential Request
**\[BREAKING CHANGE]**
The credential request specification has been changed in a breaking manner.
In OID4VCI 1.0 ID1, either the `format` parameter or the
`credential_identifier` parameter was required, and the
parameter used to specify key proof was `proof`.
In contrast, in OID4VCI 1.0 Final, the `format` parameter has been
removed, and either `credential_identifier` or the newly
introduced `credential_configuration_id` is required.
In addition, the parameter used to specify key proof has been changed to
`proofs`.
Below, the credential requests in OID4VCI 1.0 ID1 and OID4VCI 1.0 Final are explained separately.
A `credential_response_encryption` JSON object may be present
for credential response encryption, but it is not discussed here.
##### 2.9.3.1. Credential Request in OID4VCI 1.0 ID1
###### 2.9.3.1.1. Credential Information in Credential Request
A credential request is an HTTP POST request with an access token and a JSON-formatted payload. This payload contains credential information and may include an optional key proof.
Credential information in a credential request includes a mandatory `"format"` property and additional format-specific properties. For example, when the value of the `"format"` property is `"jwt_vc_json"`, an accompanying `"credential_definition"` property is expected.
The credential information is a description about the verifiable credential that the wallet wants to obtain. As implied by the example in the diagram above, taken from the OID4VCI specification, this description goes beyond merely identifying an issuable credential from among the issuable credentials associated with the access token. For instance, the intention of the example credential request is to request a verifiable credential in the format of `jwt_vc_json` that includes the `given_name`, `family_name` and `degree` claims only.
However, there are the following issues here:
1. It's not easy to determine which of the issuable credentials meet the specified conditions.
2. There's a possibility that multiple issuable credentials may satisfy the conditions.
3. Minor differences in conditions can lead to the selection of a different issuable credential.
4. It's not easy to confirm whether the presented access token has the permission to request verifiable credentials that meet the specified conditions.
Simply put, this specification lacks considerations for implementations.
The problem reported by [Issue 175](https://github.com/openid/OpenID4VCI/issues/175)
is an example that can be caused by the flaw in this specification.
Therefore, unless the specification is improved, it is likely that
**credential issuer implementations will issue verifiable credentials with fixed structures,
ignoring finer conditions specified at runtime (in access token requests or credential requests)**.
The OID4VCI has recently introduced the `credential_identifier` parameter
that is mutually exclusive with the `format` parameter. However, such
credential instance identifiers become available (if the credential issuer supports
the mechanism) only when RAR objects of type `openid_credential` are used,
and the specification says that the credential instance identifiers cannot be used
when the `scope` parameter is used. The `credential_identifier`
parameter is a solution designed solely for a specific use case and does not serve
as a resolution for the issue mentioned above.
See Issue 197
for further discussions.
###### 2.9.3.1.2. Key Proof Information in Credential Request
Key proof information in a credential request is represented by a `"proof"` property. The value of the property is a JSON object.
The `"proof"` object contains a mandatory `"proof_type"` property that indicates the format of the key proof.
When the value of the `"proof_type"` property is `"jwt"`, a JWT is used as a key proof. In this case, the `"proof"` object contains a `"jwt"` property. The value of the `"jwt"` property is a JWT that conforms to the specification of the key proof JWT.
The diagram below is an overview of a credential request.
##### 2.9.3.2. Credential Request in OID4VCI 1.0 Final
###### 2.9.3.2.1. Credential Information in Credential Request
A credential request is an HTTP POST request that includes an access token and
a JSON-formatted payload. The payload contains credential information and
optional key proof.
The credential information in a credential request is specified using either
the `credential_configuration_id` property or the
`credential_identifier` property. These two properties are
mutually exclusive.
The `credential_configuration_id` property specifies a key
that exists in the `credential_configurations_supported`
object in the credential issuer's metadata.
The `credential_identifier` property specifies a value listed
in the `credential_identifiers` array included in the RAR
object in the token response. For example, if the token response is the same as
the example shown in [6.2. Successful Token Response][OID4VCI_10_FINAL_6_2] of
OID4VCI 1.0 Final (excerpted below),
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ",
"token_type": "Bearer",
"expires_in": 86400,
"authorization_details": [
{
"type": "openid_credential",
"credential_configuration_id": "UniversityDegreeCredential",
"credential_identifiers": [
"CivilEngineeringDegree-2023",
"ElectricalEngineeringDegree-2023"
]
}
]
}
```
Either `CivilEngineeringDegree-2023` or
`ElectricalEngineeringDegree-2023`, as listed in the
`credential_identifiers` array, can be specified as the value
of the `credential_identifier` parameter.
In the OID4VCI 1.0 Final specification, if a token response includes an RAR
object (whose `type` is `openid_credential`), that
RAR object must include a `credential_identifiers` array.
However, since it is possible to send a credential request using a credential
configuration ID even without credential identifiers, it would have been
preferable for the `credential_identifiers` array to be
optional.
Automatic Derivation of Credential Identifiers
By the way, where do the values (credential identifiers) listed in the
`credential_identifiers` array included in the RAR object of
a token response come from? In fact, the OID4VCI specification does not mention
this. Even when reviewing the definition of credential configurations, there is
no property corresponding to a set of supported credential identifiers.
Therefore, it is reasonable to assume that the definition of credential
identifiers is left unconstrained and can be freely determined by the
credential issuer.
That said, defining credential identifiers in an unstructured manner is
undesirable. Therefore, Authlete has decided to automatically derive credential
identifiers based on the following rules:
If a credential configuration includes a `credential_definition.type`
array, each element prefixed with `"{credential configuration ID}/"` is
treated as a credential identifier.
If a credential configuration does not include a `credential_definition.type`
array, the credential configuration ID itself is used as the sole credential identifier.
For example, suppose a credential configuration named ConfigurationA includes
a `credential_definition` object, and that object contains a
JSON array with the key `type`, whose value is
`["Type1", "Type2"]`.
```json theme={null}
{
"credential_configurations_supported": {
"ConfigurationA": {
"credential_definition" : {
"type": [ "Type1", "Type2" ]
}
}
}
}
```
In this case, two credential identifiers—`ConfigurationA/Type1`
and `ConfigurationA/Type2`—are automatically derived.
```json theme={null}
{
"credential_identifiers": [
"ConfigurationA/Type1",
"ConfigurationA/Type2"
]
}
```
On the other hand, if the credential configuration does not include a
`credential_definition.type` array, the derived credential
identifier is simply the credential configuration ID itself.
```json theme={null}
{
"credential_identifiers": [
"ConfigurationA",
]
}
```
The `credential_definition.type` array is formally defined
for the `mso_mdoc` format. On the other hand, it is not formally
defined for the `dc+sd-jwt` format. However, regardless of whether
it is formally defined, if the `credential_definition.type`
array is present, Authlete uses it to derive credential identifiers.
###### 2.9.3.2.2. Key Proof Information in Credential Request
The key proof information in a credential request is represented by the
`"proofs"` property. The value of this property is a JSON object.
Each key in the `"proofs"` object represents a key proof type (see
[Appendix F. Proof Types][OID4VCI_10_FINAL_F]). The corresponding value is
an array, and each element in the array is a key proof.
The following example of a credential request is excerpted from
[8.2. Credential Request][OID4VCI_10_FINAL_8_2] of OID4VCI 1.0 Final.
This example includes one key proof of type `jwt`.
```http theme={null}
POST /credential HTTP/1.1
Host: server.example.com
Content-Type: application/json
Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW
{
"credential_configuration_id": "org.iso.18013.5.1.mDL",
"proofs": {
"jwt": [
"eyJraWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEva2V5cy8x
IiwiYWxnIjoiRVMyNTYiLCJ0eXAiOiJKV1QifQ"
]
}
}
```
The overall structure of a credential request in OID4VCI 1.0 Final is
as shown in the following diagram.
#### 2.9.4. Credential Response
**\[BREAKING CHANGE]**
The credential response specification has been changed in a breaking manner.
In OID4VCI 1.0 ID1, the value of the `credential` parameter in the
credential response represents the issued VC. In contrast, in OID4VCI 1.0
Final, the `credential` parameter has been removed and replaced with
the `credentials` parameter. The value of this parameter is an array
containing the issued VCs. If the credential request includes key proofs, the
same number of VCs are included in the `credentials` array.
If the credential issuer cannot immediately provide the requested VC, the
credential response includes a `transaction_id` parameter.
This is the same in both OID4VCI 1.0 ID1 and OID4VCI 1.0 Final; however,
OID4VCI 1.0 Final additionally introduces the `interval` parameter.
In OID4VCI 1.0 ID1, the credential response includes the `c_nonce`
parameter, but in OID4VCI 1.0 Final it does not, as `c_nonce` is
issued by the nonce endpoint.
Below, the credential responses in OID4VCI 1.0 ID1 and OID4VCI 1.0 Final are
explained separately.
##### 2.9.4.1. Credential Response in OID4VCI 1.0 ID1
###### 2.9.4.1.1. Credential Response with Verifiable Credential
A credential response is an HTTP response containing JSON.
When a verifiable credential is successfully issued, it is placed in the JSON as the value of the `"credential"` property.
For instance, in the case of SD-JWT-based verifiable credentials conforming to [SD-JWT VC][SD_JWT_VC], the `"credential"` property is a JSON string in the format of SD-JWT.
In addition, the credential response may contain the `c_nonce` and `c_nonce_expires_in` response parameters, as explained previously.
Let's dive into some details of an SD-JWT-based verifiable credential.
An SD-JWT consists of an issuer-signed JWT, zero or more disclosures, and an optional key binding JWT. Tildes (`~`) are used as delimiters between the components. Note that because a key binding JWT is generated by a wallet, verifiable credentials do not have a key binding JWT when they are issued by a credential issuer.
```
~...~
```
The first component in an SD-JWT is an issuer-signed JWT. As a standard JWT, the header and payload of the issuer-signed JWT can be base64url-decoded.
The following are important points to note.
1. The value of the `typ` header parameter is `"vc+sd-jwt"`.
2. The payload contains `"cnf"."jwk"` for key binding.
3. The payload contains the `"_sd_alg"` property, which indicates the hash algorithm used for disclosures.
4. The payload does not contain user claims like `"given_name"`. Instead, it contains the `"_sd"` array, which holds digest values of disclosures for user claims.
The example of SD-JWT-based verifiable credential contains four disclosures.
By base64url-decoding the disclosures, the original JSON arrays will appear.
The digest values of the disclosures are computed using the hash algorithm indicated by the `"_sd_alg"` property and are listed in the `"_sd"` array. The order of the digest values in the array must be independent of the order of the disclosures in the SD-JWT. In this example, the digest values are listed in ASCII-code order.
The diagram below is an overview of a credential response with an SD-JWT-based verifiable credential.
###### 2.9.4.1.2. Credential Response with Transaction ID
When the requested verifiable credential is not ready, the credential endpoint returns a transaction ID instead of a verifiable credential. The transaction ID is included in the credential response as the value of the `"transaction_id"` response parameter. The following is an example from the OID4VCI specification.
```http theme={null}
HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: no-store
{
"transaction_id": "8xLOxBtZp8",
"c_nonce": "wlbQc6pCJp",
"c_nonce_expires_in": 86400
}
```
The transaction ID is intended to be used later when the wallet sends a deferred credential request to the deferred credential endpoint of the credential issuer.
##### 2.9.4.1.3. Credential Response with Error
If the credential request cannot be processed successfully, the credential endpoint will return an error response, with the type of error reflected in the value of the `"error"` response parameter. Below is a sample error excerpt from the specification.
```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
{
"error": "invalid_request"
}
```
If a required key proof is missing or incorrect due to reasons like the `nonce` claim's absence or expiration, the error code `"invalid_proof"` is used. Here is an example from the specification in such a case.
```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
{
"error": "invalid_proof",
"error_description":
"Credential Issuer requires key proof to be bound to a Credential Issuer provided nonce.",
"c_nonce": "8YE9hCnyV2",
"c_nonce_expires_in": 86400
}
```
##### 2.9.4.2. Credential Response in OID4VCI 1.0 Final
###### 2.9.4.2.1. Credential Response with Verifiable Credential
A credential response is an HTTP response containing JSON.
If the issuance of VCs is successful, the VCs are included in the JSON as
the value of the `credentials` property. Each element of the `credentials`
property is a JSON object, and each contains a `credential` property. The
value of the `credential` property represents each VC. The format of the
`credential` property depends on the format of the VC.
For example, in the case of an SD-JWT-based VC compliant with
[SD-JWT VC][SD_JWT_VC], the value of the `"credential"` property is a JSON
string in SD-JWT format.
Now, let us take a closer look at the details of SD-JWT-based VCs.
An SD-JWT consists of an issuer-signed JWT, zero or more disclosures, and
an optional key binding JWT. Tildes (`~`) are used as delimiters between
the components. Note that because a key binding JWT is generated by a wallet,
verifiable credentials do not have a key binding JWT when they are issued
by a credential issuer.
```
~...~
```
The first component in an SD-JWT is an issuer-signed JWT. As a standard JWT,
the header and payload of the issuer-signed JWT can be base64url-decoded.
The following are important points to note.
1. The value of the `typ` header parameter is `"dc+sd-jwt"`.
2. The payload contains `"cnf"."jwk"` for key binding.
3. The payload contains the `"_sd_alg"` property, which indicates the hash algorithm used for disclosures.
4. The payload does not contain user claims like `"given_name"`. Instead, it contains the `"_sd"` array, which holds digest values of disclosures for user claims.
The example of SD-JWT-based verifiable credential contains four disclosures.
By base64url-decoding the disclosures, the original JSON arrays will appear.
The digest values of the disclosures are computed using the hash algorithm
indicated by the `"_sd_alg"` property and are listed in the `"_sd"` array.
The order of the digest values in the array must be independent of the
order of the disclosures in the SD-JWT. In this example, the digest values
are listed in ASCII-code order.
The diagram below is an overview of a credential response with an SD-JWT-based
verifiable credential.
###### 2.9.4.2.2. Credential Response with Transaction ID
When the requested verifiable credential is not ready, the credential endpoint
returns a transaction ID instead of a verifiable credential. The transaction ID
is included in the credential response as the value of the `"transaction_id"`
response parameter. The following is an example from the OID4VCI specification.
```http theme={null}
HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: no-store
{
"transaction_id": "8xLOxBtZp8",
"interval": 3600
}
```
The transaction ID is intended to be used later when the wallet sends a
deferred credential request to the deferred credential endpoint of the
credential issuer.
##### 2.9.4.2.3. Credential Response with Error
If the credential request cannot be processed successfully, the credential
endpoint will return an error response, with the type of error reflected in
the value of the `"error"` response parameter. Below is a sample error
excerpt from the specification.
```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
{
"error": "unknown_credential_configuration"
}
```
The error codes defined in OID4VCI 1.0 ID1 and OID4VCI 1.0 Final differ
slightly. The following table shows the error codes defined in each
specification.
| Error Code | OID4VCI 1.0 ID1 | OID4VCI 1.0 Final |
| :- | :-: | :-: |
| `credential_request_denied` | undefined | ✓ |
| `invalid_credential_request` | ✓ | ✓ |
| `invalid_encryption_parameters` | ✓ | ✓ |
| `invalid_nonce` | undefined | ✓ |
| `invalid_proof` | ✓ | ✓ |
| `unknown_credential_configuration` | undefined | ✓ |
| `unknown_credential_identifier` | undefined | ✓ |
| `unsupported_credential_format` | ✓ | deprecated |
| `unsupported_credential_type` | ✓ | deprecated |
In OID4VCI 1.0 ID1, a credential request may include the `format`
parameter. The error code `unsupported_credential_format`
is defined for this parameter. However, in OID4VCI 1.0 Final, the
`format` parameter has been removed. Accordingly, the error code
`unsupported_credential_format` has also been removed.
#### 2.9.5. Deferred Credential Request
The wallet can send a request to the deferred credential endpoint using a transaction ID. This request should be an HTTP POST request containing JSON with a `"transaction_id"` property holding the transaction ID.
```http theme={null}
POST /deferred_credential HTTP/1.1
Host: issuer.example.com
Content-Type: application/json
Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW
{
"transaction_id": "8xLOxBtZp8"
}
```
#### 2.9.6. Deferred Credential Response
**\[BREAKING CHANGE]**
The deferred credential response specification has been changed in a breaking
manner.
In OID4VCI 1.0 ID1, the value of the `credential` parameter in a
deferred credential response represents the issued VC. In contrast, in OID4VCI
1.0 Final, the `credential` parameter has been removed and replaced
with the `credentials` parameter. The value of this parameter is an
array containing the issued VCs. In addition, the
`notification_id` parameter may also be included in the
response.
If the requested VC is still not ready, in OID4VCI 1.0 ID1 the deferred
credential endpoint returns an HTTP error with the
`issuance_pending` error code. In contrast, in OID4VCI 1.0
Final, it returns a 202 Accepted response containing
`transaction_id` and `interval`.
In OID4VCI 1.0 ID1, the deferred credential response includes the
`c_nonce` parameter, but in OID4VCI 1.0 Final it does not, as
`c_nonce` is issued by the nonce endpoint.
Below, the deferred credential responses in OID4VCI 1.0 ID1 and OID4VCI 1.0
Final are explained separately.
##### 2.9.6.1. Deferred Credential Response in OID4VCI 1.0 ID1
The deferred credential endpoint will respond with an HTTP response containing JSON. If a verifiable credential has been issued successfully, this JSON includes the `"credential"` response parameter, representing the verifiable credential.
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"credential": "eyJ......CJd~"
}
```
In the event of an unsuccessful issuance, an error response will be returned with the `"error"` parameter. Particularly, when the requested verifiable credential is not yet ready, the error code `"issuance_pending"` is used.
```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
{
"error": "issuance_pending"
}
```
##### 2.9.6.2. Deferred Credential Response in OID4VCI 1.0 Final
The deferred credential endpoint returns an HTTP response containing JSON.
If the VCs have been successfully issued, the JSON includes a `"credentials"`
property. The value of the `"credentials"` property is a JSON array, and each
element is a JSON object. Each JSON object contains a `"credential"` property,
whose value represents the issued VC.
```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
{
"credentials": [
{
"credential": "LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L"
},
{
"credential": "YXNkZnNhZGZkamZqZGFza23....29tZTIzMjMyMzIzMjMy"
}
],
"notification_id": "3fwe98js"
}
```
If the requested VCs are still not ready, the deferred credential endpoint
returns a 202 Accepted response containing `transaction_id` and
`interval`.
```http theme={null}
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"transaction_id": "8xLOxBtZp8",
"interval": 86400
}
```
#### 2.9.7. Batch Credential Request
**\[BREAKING CHANGE]**
The batch credential endpoint has been removed in OID4VCI 1.0 Final. The
following describes the batch credential request in OID4VCI 1.0 ID1.
A wallet can request multiple verifiable credentials at a time by sending a batch credential request to the batch credential endpoint of the credential issuer.
A batch credential request is an HTTP POST request containing JSON, which includes a `"credential_requests"` JSON array. The array is a list of JSON objects, each of which represents a credential request.
Each credential request contains credential information and may contain an optional key proof.
It is allowed for credential requests in a batch credential request to specify different credential formats and have different key proofs. The opposite is also true. The credential requests may specify the same credential format and have the same key proof.
The diagram below is an overview of a batch credential request.
#### 2.9.8. Batch Credential Response
**\[BREAKING CHANGE]**
The batch credential endpoint has been removed in OID4VCI 1.0 Final. The
following describes the batch credential response in OID4VCI 1.0 ID1.
A batch credential response is an HTTP response containing JSON, which includes a `"credential_responses"` JSON array. The array is a list of JSON objects, each of which represents a credential response. The elements in the array correspond to the elements in the `"credential_requests"` array in the preceding batch credential request.
Each credential response contains either a verifiable credential or a transaction ID.
In addition, a batch credential response may contain `c_nonce` and `c_nonce_expires_in` as top-level properties for cases where the wallet sends a credential request or a batch credential request with a key proof in the future.
The diagram below is an overview of a batch credential response.
### 2.10. Public Key Distribution
To verify the signature of a verifiable credential or a verifiable presentation, verifiers need to obtain the public key that corresponds to the private key which the credential issuer used to sign the verifiable credential.
How to distribute public keys for verifying the signatures of verifiable credentials is outside the scope of the OID4VCI specification. However, here we describe a few proposed methods.
#### 2.10.1. Embedding X.509 Certificate
One method for public key distribution is to embed an X.509 certificate for the public key within the verifiable credential.
In the case of JWT-based verifiable credentials, it is likely that the `"x5c"` header parameter ([RFC 7515, 4.1.6][RFC_7515_4_1_6]) will be used for that purpose.
#### 2.10.2. Embedding within Entity Configuration
Another method utilizes the [OpenID Federation][OIDFED] specification and embeds the public key within the entity configuration of the credential issuer.
The Italian ecosystem has defined `openid_credential_issuer`
as a new entity type identifier, which represents a credential issuer,
and the ecosystem uses the `"jwks"` metadata to place the credential
issuer's public keys within.
#### 2.10.3. jwt-vc-issuer
Another proposal for public key distribution is `/.well-known/jwt-vc-issuer`.
The new well-known path is intended to serve as the starting point for searching for the public key.
The well-known path returns JSON containing the JWT VC issuer's metadata.
The `"jwks_uri"` property in the JSON points to the location of the JWK Set of the issuer.
Verifiers can find the target public key in the JWK Set.
The JWT VC issuer metadata may contain the `"jwks"` property
rather than the `"jwks_uri"` property. This can happen when
the JWT VC issuer has a difficulty in providing a separate endpoint for
its JWK Set for various reasons.
### 2.11. Specification Summary
The content of this section describes the situation as it was in 2023.
Although circumstances have since changed, it is retained as-is for
historical reference.
The OID4VCI specification defines rules for the issuance of verifiable credentials. The two major topics in the specification are "access token issuance" and "credential issuance".
For access token issuance, the specification defines several methods for
specifying issuable credentials, which include,
(1) using the pre-authorized code in a credential offer,
(2) using the issuer state in a credential offer,
(3) using RAR objects with `"type":"openid_credential"`, and
(4) using `scope` values referencing entries in the `credential_configurations_supported` metadata.
For credential issuance, the specification introduces three endpoints, namely, (1) the credential endpoint, (2) the batch credential endpoint, and (3) the deferred credential endpoint.
Pieces of credential information appear at some locations such as (1) the
`credential_configurations_supported` issuer metadata,
(2) RAR objects, (3) credential
requests, and (4) batch credential requests. Due to the lack of consistency
and identifiability among them, the specification may not fully achieve its
intended goal. However, in exchange for sacrificing full interoperability,
real-world ecosystems will be able to issue verifiable credential for their
specific needs based on the specification along with their supplementary
specifications.
The specification does not go into the specifics of verifiable credential formats,
but it does establish rules related to the `jwt_vc_json`, `jwt_vc_json-ld`,
`ldp_vc`, `mso_mdoc`, and `vc+sd-jwt` formats. Among them, the formats that
have recently been garnering the most attention are "SD-JWT VC" and "mdoc"
([ISO/IEC 18013-5:2021][ISO_IEC_18013_5]). eIDAS 2.0 mandates support for
SD-JWT-based and mdoc-based formats.
Public key distribution is also not covered in the specification. Some
recognized proposals for public key distribution include (1) embedding
an X.509 certificate in the verifiable credential itself, (2) using
`"openid_credential_issuer"."jwks"` in
the entity configuration of the credential issuer, and (3) using
`/.well-known/jwt-vc-issuer`.
While the OID4VCI specification still has room for improvements, real-world
ecosystems can leverage it for their specific needs with practical compromises
and local supplementary specifications.
## 3. OID4VCI Implementation
### 3.1. Authlete Overview
Developers can build their own credential issuers and authorization servers /
OpenID providers conforming to the OID4VCI specification by utilizing Authlete.
While most vendors directly provide implementations of frontend servers such as
an authorization server, Authlete takes a different approach. Authlete provides
a set of Web APIs with which developers themselves can implement their own
frontend servers. Authlete sits behind such frontend servers and is invisible
from end users.
The Authlete architecture inevitably requires developers to build frontend servers,
but in return, developers receive the following benefits.
1. **Any technical components of developer's choice**
* User authentication method
* User management system
* API gateway
* Programming language
* Web framework
* Cloud service
2. **Full control over user data**
* No need to upload user data to the OAuth/OIDC vendor's server.
* Manageable compliance with various regulations for the protection of user data.
3. **Full control over end-user facing frontend servers**
* Corporate brand management across all aspects of UI/UX.
4. **Enforced proper layer separation in system design**
* API authorization is separated from user management and user authentication.
* OAuth/OIDC protocol processing is separated from API gateway and frontend servers.
Architects in enterprises have begun to realize that they need full control over
user identity management and the Web APIs of their services for the success of
their business and recognize that the "OAuth/OIDC component as a Service"
approach offered by Authlete is the ideal solution. Please watch
"Security Offered as
Components Empowering Enterprises to Gain Control", a panel discussion
on this topic held at EIC 2023.
### 3.2. Authlete Configuration
#### 3.2.1. Authlete Version
OID4VCI 1.0 ID1 has been supported since the initial release of Authlete 3.0,
while OID4VCI 1.0 Final has been supported since Authlete 3.0.28.
#### 3.2.2. Authlete Server Configuration
The "**Verifiable Credentials**" feature must be enabled on the Authlete server.
If you are using the on-premises version of Authlete, please confirm that the
configuration file (`authlete-server.properties`) includes the
following line to enable this feature.
```java-properties theme={null}
feature.verifiable_credentials.enabled=true
```
#### 3.2.3. Authlete Service Configuration
Property
Type
Description
`verifiableCredentialsEnabled`
boolean
This flag controls availability of features related to Verifiable Credentials
such as support of the OID4VCI specification.
`oid4vciVersion`
string
This property indicates the version of the OpenID for Verifiable
Credential Issuance specification to be used. If the value of this
property is unset or `"1.0-ID1"`, the service operates
according to the OID4VCI 1.0 ID1 specification. If the value is
`"1.0-Final"` or `"1.0"`, the service operates
according to the OID4VCI 1.0 Final specification.
`credentialIssuerMetadata`
`credentialIssuer`
string
The identifier of the credential issuer when this service acts
as a credential issuer. This property corresponds to the
`credential_issuer` metadata defined in the OID4VCI
specification.
The value must be a valid URL with the `https` scheme and without
the query part and the fragment part. In addition, Authlete limits the value
to ASCII only and a maximum length of 200 characters.
To act as a credential issuer, this property must be set.
`authorizationServers`
string array
The identifiers of the authorization servers the credential issuer
relies on for authorization when this service acts as a credential issuer.
This property corresponds to the `authorization_servers`
metadata defined in the OID4VCI specification.
The values must be HTTP-accessible URLs.
`credentialEndpoint`
string
The URL of the credential endpoint when this service acts as a credential
issuer. This property corresponds to the `credential_endpoint`
metadata defined in the OID4VCI specification.
The value must be a valid URL with the `https` scheme and without
the fragment part. In addition, Authlete limits the value to ASCII only and
a maximum length of 200 characters.
To act as a credential issuer, this property must be set.
`batchCredentialEndpoint`
string
The URL of the batch credential endpoint when this service acts as a
credential issuer. This property corresponds to the
`batch_credential_endpoint` metadata defined in the
OID4VCI 1.0 ID1 specification.
The value must be a valid URL with the `https` scheme and without
the fragment part. In addition, Authlete limits the value to ASCII only and
a maximum length of 200 characters.
It is optional whether to implement the batch credential endpoint.
Note that in OID4VCI 1.0 Final, the batch credential endpoint has been removed.
`deferredCredentialEndpoint`
string
The URL of the deferred credential endpoint when this service acts as
a credential issuer. This property corresponds to the
`deferred_credential_endpoint` metadata defined in the
OID4VCI specification.
The value must be a valid URL with the `https` scheme and without
the fragment part. In addition, Authlete limits the value to ASCII only and
a maximum length of 200 characters.
If the credential endpoint and/or the batch credential endpoint of your
credential issuer may issue transaction IDs, you must implement the deferred
credential endpoint.
`notificationEndpoint`
string
The URL of the notification endpoint when this service acts as
a credential issuer. This property corresponds to the
`notification_endpoint` metadata defined in the
OID4VCI specification.
The value must be a valid URL with the `https` scheme and without
the fragment part. In addition, Authlete limits the value to ASCII only and
a maximum length of 200 characters.
`nonceEndpoint`
string
The URL of the nonce endpoint when this service acts as
a credential issuer. This property corresponds to the
`nonce_endpoint` metadata defined in the
OID4VCI 1.0 Final specification.
The value must be a valid URL with the `https` scheme and without
the fragment part. In addition, Authlete limits the value to ASCII only and
a maximum length of 200 characters.
According to the requirements of the OID4VCI 1.0 Final specification,
if a nonce endpoint is configured, key proofs that do not include a nonce
will be rejected.
`credentialRequestEncryptionJwks`
string
A JWK Set document containing the public keys for encrypting credential
requests and the private keys for decrypting encrypted credential
requests. The public portion of this property corresponds to the
`credential_request_encryption.jwks` metadata
defined in the OID4VCI 1.0 Final specification.
`credentialRequestEncryptionEncValuesSupported`
string array
The JWE `enc` algorithms supported for credential request
encryption. This property corresponds to the
`credential_request_encryption.enc_values_supported`
metadata defined in the OID4VCI 1.0 Final specification.
The valid values are the names of JWEEnc enum entries such as `"A256GCM"`.
`credentialRequestEncryptionZipValuesSupported`
string array
The JWE `zip` algorithms supported for credential request
encryption. This property corresponds to the
`credential_request_encryption.zip_values_supported`
metadata defined in the OID4VCI 1.0 Final specification.
The valid values are the names of JWEZip enum entries such as `"DEF"`.
`requireCredentialEncryptionRequest`
boolean
The flag indicating whether to always encrypt credential requests.
This property corresponds to the
`credential_request_encryption.encryption_required`
metadata defined in the OID4VCI 1.0 Final specification.
If this property is set to true, all credential requests are required
to be encrypted.
`credentialResponseEncryptionAlgValuesSupported`
string array
The JWE `alg` algorithms supported for credential response
encryption. This property corresponds to the
`credential_response_encryption.alg_values_supported`
metadata defined in the OID4VCI specification.
The valid values are the names of JWEAlg enum entries such as `"ECDH_ES"`.
Only asymmetric algorithms are accepted.
`credentialResponseEncryptionEncValuesSupported`
string array
The JWE `enc` algorithms supported for credential response
encryption. This property corresponds to the
`credential_response_encryption.enc_values_supported`
metadata defined in the OID4VCI specification.
The valid values are the names of JWEEnc enum entries such as `"A256GCM"`.
`credentialResponseEncryptionZipValuesSupported`
string array
The JWE `zip` algorithms supported for credential response
encryption. This property corresponds to the
`credential_response_encryption.zip_values_supported`
metadata defined in the OID4VCI 1.0 Final specification.
The valid values are the names of JWEZip enum entries such as `"DEF"`.
`requireCredentialEncryptionResponse`
boolean
The flag indicating whether to always encrypt credential responses.
This property corresponds to the
`credential_response_encryption.encryption_required`
metadata defined in the OID4VCI specification.
If this property is set to true, every credential request is required
to include the `credential_response_encryption`
JSON object.
`batchSize`
integer
The maximum size of the `proofs` array in a credential
request. This property corresponds to the
`batch_credential_issuance.batch_size`
metadata defined in the OID4VCI 1.0 Final specification.
If the value of this property is set to 2 or greater, the
`/api/{service-id}/vci/metadata`
API of Authlete will insert a
`batch_credential_issuance` object into the
generated credential issuer metadata. Within that object, the value
of the `batch_size` field will be set to the value
of this `batchSize` property.
`display`
string
A JSON array representing the display name of the credential issuer.
This property corresponds to the `display` metadata
defined in the OID4VCI specification.
`credentialsSupported`
string
Credentials supported by the credential issuer when this service acts as
a credential issuer. This property corresponds to the
`credential_configurations_supported` metadata defined
in the OID4VCI specification.
The value must be a JSON object. Non-ASCII characters may be contained,
but Authlete limits the maximum number of characters to 16383.
To act as a credential issuer, this property must be set.
For backward compatibility, the name of this property remains
`credentialsSupported` and will not be renamed to
`credentialConfigurationsSupported`.
`credentialOfferDuration`
integer
The default duration of credential offers in seconds.
When an API to the `/vci/offer/create` API does not contain
the `duration` request parameter or the value of the parameter
is 0 or negative, the value of this property is used as the default value.
If the value of this property is 0 or negative, the default value per
Authlete server is used as the default value.
`preAuthorizedGrantAnonymousAccessSupported`
boolean
This property indicates whether token requests using the pre-authorized
code flow by unidentifiable client applications are allowed.
This property corresponds to the
`pre-authorized_grant_anonymous_access_supported`
metadata defined in the OID4VCI specification.
`cnonceDuration`
integer
The duration or time window of a `c_nonce`, expressed in
seconds.
In OID4VCI 1.0 ID1, `c_nonce` is issued by the token endpoint
or the credential endpoint. The value of this property is used as the
validity period of the `c_nonce`.
In OID4VCI 1.0 Final, `c_nonce` is issued by a dedicated nonce
endpoint. The value of this property is used as the nonce time window.
All nonces issued within the same time window have the same value.
If the value of this property is 0 or less, the default value configured
on the Authlete server is used.
`credentialTransactionDuration`
integer
The default duration of transaction IDs in seconds that may be issued
as a result of a credential request or a batch credential request.
If the value of this property is 0 or negative, the default value per
Authlete server is used.
`credentialDuration`
integer
The default duration of verifiable credentials in seconds.
Some Authlete APIs such as the `/vci/single/issue` API and
the `/vci/batch/issue` API may issue one or more verifiable
credentials. The value of this property specifies the default duration
of such verifiable credentials.
The value 0 indicates that verifiable credentials will not expire.
In the case, verifiable credentials will not have a property that
indicates the expiration time. For example, JWT-based verifiable
credentials will not contain the `exp` claim
(RFC 7519,
Section 4.1.4).
Authlete APIs that may issue verifiable credentials recognize a request
parameter that can override the duration. For example, a request to the
`/vci/single/issue` API contains an `order` object
that has a `credentialDuration` parameter that can override
the default duration.
`credentialJwks`
string
The JWK Set document containing private keys that are used to sign
verifiable credentials.
Some Authlete APIs such as the `/vci/single/issue` API and
the `/vci/batch/issue` API may issue one or more verifiable
credentials. The content of this property is referred to by such APIs.
Authlete APIs that may issue verifiable credentials recognize a request
parameter that can specify the key ID of a private key that should be
used for signing. For example, a request to the `/vci/single/issue`
API contains an `order` object that has a
`signingKeyId` parameter that can specify the key
ID of a private key to be used for signing. When a key ID is not specified,
Authlete will select a private key automatically.
If JWKs in the JWK Set do not contain the `kid` property
(RFC 7517,
Section 4.5) when this `credentialJwks` property
is updated, Authlete will automatically insert the `kid`
property into such JWKs. The JWK thumbprint
(RFC 7638)
computed with the SHA-256 hash algorithm is used as the value of the
`kid` property.
`credentialJwksUri`
string
The URL at which the JWK Set document of the credential issuer is exposed.
This URL is used as the value of the `jwks_uri` property in the
JWT issuer metadata. The metadata itself is published at
`/.well-known/jwt-issuer`. See
SD-JWT-based Verifiable Credentials (SD-JWT VC) for details about the
JWT issuer metadata.
### 3.3. Authlete APIs
#### 3.3.1. Overall Picture of Authlete APIs for OID4VCI
The following diagram illustrates the relationship between the endpoints of
the frontend servers (the credential issuer and the authorization server) and
Authlete APIs. The details of the Authlete APIs are explained in the following
sections.
Although not shown in the diagram above, there is also a
`/vci/nonce` API for implementing the nonce endpoint defined
in the OID4VCI 1.0 Final specification.
#### 3.3.2. Authlete API Call
A significant difference between Authlete 2.x and Authlete 3.0 is how to call
Authlete APIs.
In Authlete 2.x and older versions, developers call Authlete APIs using a pair
of an API key and an API secret (e.g., a service API key and a service API secret).
In Authlete 3.0, on the other hand, developers call Authlete APIs with an access token.
Developers can obtain access tokens for Authlete APIs using the new Web console,
which is significantly different from the previous ones. In Authlete 2.x and
older versions, there are two separate Web consoles: the service owner console
(for managing services corresponding to authorization servers and OpenID providers)
and the developer console (for managing client applications). In Authlete 3.0,
however, a single Web console is provided, and its appearance and functionality
change based to on the privileges of the presented access token.
| | Authlete 2.x | Authlete 3.0 |
| :- | :- | :- |
| Protection | API key & API secret | Access token |
| Web Console | The service owner console and the developer console | A single console |
Another difference is found in the path component of Authlete APIs. In Authlete 3.0, most Authlete APIs include a service ID as part of the path, such as `/api/`*`{ServiceID}`*`/auth/authorization`, where *`{ServiceID}`* represents the identifier of a service (i.e., the service API key in Authlete 2.x).
| Authlete Version | API Path Example |
| :- | :- |
| Authlete 2.x | `/api/auth/authorization` |
| Authlete 3.0 | `/api/`*`{ServiceID}`*`/auth/authorization` |
In this document, as well as in other documents, the prefix `/api/{ServiceID}` for API paths is omitted.
These changes are not insignificant, but their impact on programs can be
minimized by absorbing the differences at the library layer. For instance,
developers using the sample authorization server written in Java
([authlete/java-oauth-server][JAVA_OAUTH_SERVER]) and switching from
Authlete 2.x to Authlete 3.0 only need to modify the content of the
configuration file (`authlete.properties`) from:
```java-properties theme={null}
# For Authlete 2.x
base_url = ...
service.api_key = ..
service.api_secret = ...
```
to:
```java-properties theme={null}
# For Authlete 3.0
api_version = V3
base_url = ...
service.api_key = ...
service.access_token = ...
```
### 3.4. Credential Offer Issuance
As mentioned before, the process of issuing credential offers varies among
credential issuers.
For example, after interacting with a user via a web browser, the credential
issuer may display a QR code like below:
that represents "`openid-credential-offer://?credential_offer={CredentialOffer}`"
where *`{CredentialOffer}`* holds the following credential offer.
```json theme={null}
{
"credential_issuer": "https://trial.authlete.net",
"credential_configuration_ids": [
"IdentityCredential",
"org.iso.18013.5.1.mDL"
],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "NH9udMon5pTuuvbsNsHUNWf8tpU__9wt-gsO9LeYthc"
}
}
}
```
The credential issuer may instead show a hyperlink like below:
* `openid-credential-offer://?credential_offer_uri=`*`{CredentialOfferUri}`*
where *`{CredentialOfferUri}`* holds a URL-encoded URL like
`https%3A%2F%2Ftrial.authlete.net%2Fapi%2Foffer%2FTctoiNm9lYASTBT6XRGb8RQsrClKczCxDtqLY1jLvpk`.
#### 3.4.1. The `/vci/offer/create` API
Regardless, credential issuers supporting credential offers must be able to
create them. For the functionality, Authlete provides the `/vci/offer/create`
API. The following table summarizes the API.
Request to the /vci/offer/create API
HTTP Method and Content-Type
GET
(query parameters)
POST
`application/json`
POST
`application/x-www-form-urlencoded`
Request Parameters
`credentialConfigurationIds`
A string array, which will be used as the value of
the `"credential_configuration_ids"` property of a credential offer.
This request parameter is mandatory.
`authorizationCodeGrantIncluded`
A boolean value (`true` or `false`) indicating
whether to include the `"authorization_code"` object in the
`"grants"` object.
`issuerStateIncluded`
A boolean value (`true` or `false`) indicating
whether to include the `"issuer_state"` property in the
`"authorization_code"` object in the `"grants"`
object.
When this parameter is `true`, Authlete generates an issuer
state and puts it in the `"authorization_code"` object as
the value of the `"issuer_state"` property.
`preAuthorizedCodeGrantIncluded`
A boolean value (`true` or `false`) indicating
whether to include the
`"urn:ietf:params:oauth:grant-type:pre-authorized_code"`
object in the `"grants"` object.
When this parameter is `true`, Authlete generates a
pre-authorized code and puts it in the
`"urn:ietf:params:oauth:grant-type:pre-authorized_code"`
object as the value of the `"pre-authorized_code"` property.
`txCode`
A transaction code that should be associated with the pre-authorized code.
If this parameter is not empty, a `tx_code` object will be
embedded in the
`"urn:ietf:params:oauth:grant-type:pre-authorized_code"`
object. Consequently, the token request using the pre-authorized code
will have to include the `tx_code` request parameter with
the value specified by this parameter.
`txCodeInputMode`
The input mode of the transaction code. The value specified by this
parameter will be used as the value of the `input_mode`
property in the `tx_code` object.
The predefined values listed in the OID4VCI specification are
`"numeric"` and `"text"` only, but the
`/vci/offer/create` API accepts other values
for the future extension in addition to the predefined ones.
`txCodeDescription`
The description of the transaction code. The value specified by this
parameter will be used as the value of the `description`
property in the `tx_code` object.
`subject`
The subject (the unique identifier) of the user associated with
the credential offer.
This parameter is mandatory.
`duration`
The duration of the credential offer in seconds.
If this parameter holds a positive integer, the value is used as the
duration of the credential offer being issued. Otherwise, the value
of the `credentialOfferDuration` property of the
service is used.
`context`
The general-purpose arbitrary string associated with the credential
offer.
Developers can utilize this parameter as they like. Authlete does not
care about the content of this parameter.
`properties`
The extra properties associated with the credential offer, which are
general-purpose key-value pairs.
The extra properties will be eventually associated with an access token
which will be created based on the credential offer.
`jwtAtClaims`
The additional claims in JSON object format that are added to the payload
part of the JWT access token.
This parameter has a meaning only when the format of access tokens issued
by the service is JWT. In other words, it has a meaning only when the
`accessTokenSignAlg` property of the service
holds a non-null value.
The additional claims will be eventually associated with an access token
which will be created based on the credential offer.
`authTime`
The time when the user authentication was performed during the course
of issuing the credential offer.
The time is represented as seconds since the Unix epoch.
`acr`
The Authentication Context Class Reference of the user authentication
performed during the course of issuing the credential offer.
For example, the following command lines create a credential offer.
```shell theme={null}
$ BASE_URL=https://nextdev-api.authlete.net
$ SERVICE_ID=986126671
$ ACCESS_TOKEN=${YOUR_ACCESS_TOKEN}
$ curl -s ${BASE_URL}/api/${SERVICE_ID}/vci/offer/create \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
--data '
{
"credentialConfigurationIds": [ "IdentityCredential" ],
"preAuthorizedCodeGrantIncluded": true,
"txCode": "123456",
"txCodeInputMode": "numeric",
"subject": "1001"
}
'
```
The `/vci/offer/create` API returns JSON like below.
```json theme={null}
{
"resultCode": "A366001",
"resultMessage": "[A366001] A credential offer was created successfully.",
"action": "CREATED",
"info": {
"authTime": 0,
"authorizationCodeGrantIncluded": false,
"credentialConfigurationIds": [
"IdentityCredential"
],
"credentialIssuer": "https://trial.authlete.net",
"credentialOffer": "{\"credential_issuer\":\"https://trial.authlete.net\",\"credential_configuration_ids\":[\"IdentityCredential\"],\"grants\":{\"urn:ietf:params:oauth:grant-type:pre-authorized_code\":{\"pre-authorized_code\":\"rS8D7asTTL8MaXM5yLjQvaAMmPmierRW6oeK-4JP4Uk\",\"tx_code\":{\"length\":6,\"input_mode\":\"numeric\"}}}}",
"expiresAt": 1703929674224,
"identifier": "9gjVvas8Q5BkkrkSfZv-DbsBYJvlw6ZPMK-TeCkQDEc",
"issuerStateIncluded": false,
"preAuthorizedCode": "rS8D7asTTL8MaXM5yLjQvaAMmPmierRW6oeK-4JP4Uk",
"preAuthorizedCodeGrantIncluded": true,
"subject": "1001",
"txCode": "123456",
"txCodeInputMode": "numeric"
}
}
```
The `"info"` object in the API response contains information about the created
credential offer. The `"credentialOffer"` property in the `"info"` object is a
string representing the created credential offer. The value of the
`"credentialOffer"` property in the above example looks like the following when
formatted in a human-readable manner.
```json theme={null}
{
"credential_issuer": "https://trial.authlete.net",
"credential_configuration_ids": [
"IdentityCredential"
],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "rS8D7asTTL8MaXM5yLjQvaAMmPmierRW6oeK-4JP4Uk",
"tx_code": {
"length": 6,
"input_mode": "numeric"
}
}
}
}
```
With the value of the `"credentialOffer"` property, you can construct a URL
by concatenating the following components:
1. A credential offer endpoint. For example, `openid-credential-offer://`.
2. `?credential_offer=`.
3. URL-encoded `"credentialOffer"` value.
````
openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftrial.authlete.net%22%2C%22credential_configuration_ids%22%3A%5B%22IdentityCredential%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22rS8D7asTTL8MaXM5yLjQvaAMmPmierRW6oeK-4JP4Uk%22%2C%22tx_code%22%3A%7B%22length%22%3A6%2C%22input_mode%22%3A%22numeric%22%7D%7D%7D%7D
The request to and the response from the `/vci/offer/create` API are represented
by the `CredentialOfferCreateRequest` and
`CredentialOfferCreateResponse` Java
classes in the [authlete-java-common][AUTHLETE_JAVA_COMMON] library, respectively.
Please refer to the library's [JavaDoc][AUTHLETE_JAVA_COMMON_JAVADOC] for details.
#### 3.4.2. The `/vci/offer/info` API
The `/vci/offer/info` API returns information about a credential offer.
This API accepts the `identifier` request parameter that specifies the
identifier of a credential offer.
Request to the /vci/offer/info API
HTTP Method and Content-Type
GET
(path parameters)
POST
`application/json`
POST
`application/x-www-form-urlencoded`
Request Parameters
`identifier`
The identifier of a credential offer. When the API call is an HTTP GET
request, the identifier is specified as the last path component like
`/vci/offer/info/{identifier}`.
The identifier is included in the response from the `/vci/offer/create` API.
The value of the `"identifier"` property in the `"info"` object is the identifier.
In the example in the previous section, its value is
`9gjVvas8Q5BkkrkSfZv-DbsBYJvlw6ZPMK-TeCkQDEc`.
The following command lines query information about the credential offer
created in the previous section.
```shell
$ CREDENTIAL_OFFER_IDENTIFIER=9gjVvas8Q5BkkrkSfZv-DbsBYJvlw6ZPMK-TeCkQDEc
$ curl -s ${BASE_URL}/api/${SERVICE_ID}/vci/offer/info/${CREDENTIAL_OFFER_IDENTIFIER} \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
````
The `/vci/offer/info` API returns JSON like below, which is almost the same
as the response from the `/vci/offer/create` API.
```json theme={null}
{
"resultCode": "A368001",
"resultMessage": "[A368001] Information about the credential offer was obtained successfully.",
"action": "OK",
"info": {
"authTime": 0,
"authorizationCodeGrantIncluded": false,
"credentialConfigurationIds": [
"IdentityCredential"
],
"credentialIssuer": "https://trial.authlete.net",
"credentialOffer": "{\"credential_issuer\":\"https://trial.authlete.net\",\"credential_configuration_ids\":[\"IdentityCredential\"],\"grants\":{\"urn:ietf:params:oauth:grant-type:pre-authorized_code\":{\"pre-authorized_code\":\"rS8D7asTTL8MaXM5yLjQvaAMmPmierRW6oeK-4JP4Uk\",\"tx_code\":{\"length\":6,\"input_mode\":\"numeric\"}}}}",
"expiresAt": 1703929674000,
"identifier": "9gjVvas8Q5BkkrkSfZv-DbsBYJvlw6ZPMK-TeCkQDEc",
"issuerStateIncluded": false,
"preAuthorizedCode": "rS8D7asTTL8MaXM5yLjQvaAMmPmierRW6oeK-4JP4Uk",
"preAuthorizedCodeGrantIncluded": true,
"subject": "1001",
"txCode": "123456",
"txCodeInputMode": "numeric"
}
}
```
The main purpose of the `/vci/offer/info` API is to assist developers in
implementing an endpoint on their credential issuer that provides information
about a credential offer when queried by a wallet.
If such endpoint is available, you can construct a URL by concatenating the
following components:
1. A credential offer endpoint. For example, `openid-credential-offer://`.
2. `?credential_offer_uri=`.
3. URL-encoded URL of the endpoint including the identifier of the credential offer. For example, `https://trial.authlete.net/api/offer/9gjVvas8Q5BkkrkSfZv-DbsBYJvlw6ZPMK-TeCkQDEc`.
```
openid-credential-offer://?credential_offer_uri=https%3A%2F%2Ftrial.authlete.net%2Fapi%2Foffer%2F9gjVvas8Q5BkkrkSfZv-DbsBYJvlw6ZPMK-TeCkQDEc
```
#### 3.4.3. Credential Offer Issuance Example
The sample authorization server implementation written in Java,
[authlete/java-oauth-server][JAVA_OAUTH_SERVER], can function as a credential
issuer. Its `/api/offer/issue` endpoint provides an HTML page for developers
to create custom credential offers. A java-oauth-server instance using
Authlete 3.0 is currently running at [https://trial.authlete.net](https://trial.authlete.net), and the
endpoint is active for trial purposes at [https://trial.authlete.net/api/offer/issue](https://trial.authlete.net/api/offer/issue).
The HTML page requires user authentication. The test accounts embedded in
java-oauth-server can be used. However, if you want to issue an mdoc-based
VC, please use `inga`.
| Subject | Login ID | Password |
| :-: | :-: | :-: |
| 1001 | `john` | `john` |
| 1002 | `jane` | `jane` |
| 1003 | `max` | `max` |
| 1004 | `inga` | `inga` |
### 3.5. Credential Endpoint Implementation
The credential endpoint can be implemented using the following Authlete APIs.
| | Authlete API | Description |
| :- | :- | :- |
| 1 | `/auth/introspection` | validates the presented access token, and returns the information about the access token. |
| 2 | `/vci/single/parse` | parses and validates the received credential request, and returns the information about the credential request. |
| 3 | `/vci/single/issue` | issues a verifiable credential or a transaction ID, and prepares the credential response. |
Let's go through the processing steps within a credential endpoint implementation.
As the first step, the implementation of the credential endpoint receives
a credential request from a wallet.
The implementation extracts the access token from the credential request and
passes it to Authlete's `/auth/introspection` API.
The `/auth/introspection` API validates the access token, and returns
information about the access token.
If the access token is valid, the endpoint implementation sends the
access token and the message body of the credential request to the
`/vci/single/parse` API.
You might wonder why the access token is sent to the `/vci/single/parse` API,
even though its validation has already been completed. The reason for this is that,
in Authlete's implementation, `c_nonce` is associated with an access token.
To validate the `nonce` claim in a key proof that may be included in the
credential request, Authlete needs to know the value of `c_nonce` associated
with the access token.
Note: The above explanation applies only to OID4VCI 1.0 ID1. In OID4VCI 1.0
Final, it is not possible, in principle, to manage `c_nonce` in
association with an access token, so the value of `c_nonce` is
computed independently of the access token.
The `/vci/single/parse` API parses and validates the credential request, and returns the information about the credential request.
The endpoint implementation prepares a "**credential issuance order**", which
contains necessary information for Authlete to issue a verifiable credential.
The details about this preparation will be discussed later.
The endpoint implementation sends the credential issuance order and the access
token to the `/vci/single/issue` API.
The reason for sending the access token to Authlete again is the need to refresh
the `c_nonce` value associated with the access token in case it has expired.
The `/vci/single/issue` API issues a verifiable credential or a transaction ID
according to the credential issuance order, and prepares the content of the
credential response.
The endpoint implementation builds an HTTP response that represents the credential
response from the endpoint to the wallet. The response content prepared by the
`/vci/single/issue` API can be used as the message body of the credential
response.
Finally, the credential endpoint returns the credential response to the wallet.
The following diagram illustrates the processing steps within a credential
endpoint implementation.
#### 3.5.1. Credential Issuance Order
The steps to prepare a credential issuance order are as follows.
##### 3.5.1.1. Credential Issuance Order Step 1
Get the subject (= unique identifier) of the user associated with the access
token from the access token information. The `"subject"` property in the response
from the `/auth/introspection` API (cf. [IntrospectionResponse][IntrospectionResponse])
holds the value of the subject.
##### 3.5.1.2. Credential Issuance Order Step 2
Retrieve information about the user identified by the subject from the user database.
##### 3.5.1.3. Credential Issuance Order Step 3
Get the information about the issuable credentials associated with the access
token from the access token information. The `"issuableCredentials"` property in
the response from the `/auth/introspection` API holds the information as a string.
This string needs to be parsed as a JSON array.
Each element of the JSON array represented by the `"issuableCredentials"`
property is a JSON object containing the following keys:
| Key | Type | Descryption |
| :- | :-: | :- |
| `credential_configuration_id` | string | If this issuable credential corresponds to a credential configuration, the ID of that configuration. In OID4VCI 1.0 Final, this value is always available because credentials without a corresponding configuration cannot be requested. In OID4VCI 1.0 ID1, it may be unavailable. |
| `credential_identifiers` | string array | If this issuable credential corresponds to a credential configuration, the set of credential identifiers calculated from that configuration. The calculation method is Authlete-specific. In OID4VCI 1.0 Final, this value is always available; in OID4VCI 1.0 ID1, it may be unavailable. |
| `format` | string | A string representing the VC format, such as `dc+sd-jwt` or `mso_mdoc`. This value is always available regardless of whether OID4VCI 1.0 ID1 or OID4VCI 1.0 Final is used. |
| `display` | object array | If this issuable credential corresponds to a credential configuration, the display name(s) of the credential. In OID4VCI 1.0 ID1, this comes from the top-level display property of the configuration; in OID4VCI 1.0 Final, it comes from the `display` property inside the `credential_metadata` object. Since `display` is optional in both versions, it may not be present. |
| Others | varies by key | If this issuable credential corresponds to a configuration, any other properties included in that configuration except for the following: `format`, `scope`, `cryptographic_binding_methods_supported`, `credential_signing_alg_values_supported`, `proof_types_supported`. This includes, if present, `credential_metadata` and `credential_definition`.
Additionally, some keys are always present depending on the VC format (e.g., `vct` for `dc+sd-jwt` or `doctype` for `mso_mdoc`). For OID4VCI 1.0 ID1 specifically, arbitrary properties may also be included in the RAR object; if so, the corresponding keys are also available. |
##### 3.5.1.4. Credential Issuance Order Step 4
Get the credential information included in the credential request from the
credential request information. The `"info"` object (see.
[CredentialRequestInfo][CredentialRequestInfo]) in the response from the
`/vci/single/parse` API (see.
[CredentialSingleParseResponse][CredentialSingleParseResponse]) holds various
information about the credential request.
The `"info"` object contains the following keys:
| Key | Type | Description |
| :- | :-: | :- |
| `identifier` | string | A randomly assigned identifier for the credential request by Authlete. It is a 43-character string generated by base64url encoding a 256-bit entropy random value. |
| `format` | string | A string representing the VC format, such as `dc+sd-jwt` or `mso_mdoc`. |
| `bindingKey` | string | The public key (in JWK format) specified in the `proof` request parameter of OID4VCI 1.0 ID1. |
| `bindingKeys` | string array | The set of public keys (in JWK format) specified in the `proofs` request parameter of OID4VCI 1.0 Final. |
| `details` | string | A string representing the content of the credential request. The following keys are excluded: `credential_configuration_id`, `credential_identifier`, `credential_response_encryption`, `format`, `proof`, `proofs`. To use this value, it must be parsed as a JSON object. |
| `credentialConfigurationId` | string | The credential configuration ID specified in the `credential_configuration_id` request parameter. This parameter does not exist in OID4VCI 1.0 ID1, so this key is unavailable in that context. |
| `credentialIdentifier` | string | The credential identifier specified in the `credential_identifier` request parameter. Although this parameter exists in OID4VCI 1.0 ID1, Authlete's implementation of OID4VCI 1.0 ID1 does not support it, so this key is also unavailable in that context. |
##### 3.5.1.5. Credential Issuance Order Step 5
Confirm that the access token has the necessary permissions for the
credential request by checking if the credential information is a subset
of any issuable credentials.
However, if you are a programmer, you can understand that the current OID4VCI
specification makes it challenging to implement this step. Furthermore, in the
case of SD-JWT VC, there is a proposal to make `vct` determine the set of
claims and eliminate the need to specify individual claims one by one.
The proposal makes it impossible to check the access token's
permissions only by mechanically seeing the inclusion relationship between
JSON objects.
Therefore, the confirmation of whether the access token has sufficient
permissions is left to be implemented by each credential issuer according to
their respective policies. While permission checks based on inclusion
relationships are implemented in Authlete, they have been disabled.
The `checkPermissions` method of the
AbstractOrderProcessor class in
java-oauth-server
repository serves as an example implementation of permission-checking logic.
##### 3.5.1.6. Credential Issuance Order Step 6
Determine the set of user claims to embed in the VC being issued based on
the credential information, and get the values of the user claims from the
dataset retrieved from the user database.
##### 3.5.1.7. Credential Issuance Order Step 7
Build a credential issuance order using the collected data.
A credential issuance order is a JSON object that has the properties listed
in the following table.
| Property | Type | Description |
| :- | :- | :- |
| `requestIdentifier` | string | The identifier of the credential request which has been assigned by Authlete. The `info.identifier` property in the response from the `/vci/single/parse` API is the identifier. This property is mandatory. |
| `credentialPayload` | string | The additional payload added to the VC being issued. The format of this string must be a JSON object. The set of the user claims should be converted into JSON and set to this property. This parameter is optional. |
| `issuanceDeferred` | boolean | The flag indicating whether to defer the credential issuance. When this property is `true`, the `/vci/single/issue` API issues a transaction ID instead of a VC. |
| `credentialDuration` | integer | The duration of the VC in seconds. If the value of this property is a positive number, the value is used as the duration. If the value is 0, the default duration of the service is used. If the value is a negative number, the VC will not have an expiration time. |
| `signingKeyId` | string | The key ID of the private key that should be used for signing the VC being issued. If omitted, Authlete will select a key automatically. |
##### 3.5.1.8. Credential Issuance Order Step 8
Prepare a request to the `/vci/single/issue` API (cf. [CredentialSingleIssueRequest][CredentialSingleIssueRequest]).
Request to the /vci/single/issue API
HTTP Method and Content-Type
POST
`application/json`
Request Parameters
`accessToken`
The access token presented at the credential endpoint.
`order`
A credential issuance order that provides an instruction for issuing
a verifiable credential or a transaction ID.
##### 3.5.1.9. Credential Issuance Order Step 9
Send the prepared request to the `/vci/single/issue` API.
##### 3.5.1.10. Credential Issuance Order Steps Summary
The following diagram is a summary of the steps for preparing a credential issuance order.
### 3.6. Batch Credential Endpoint Implementation
To be written.
### 3.7. Configure `oid4vci` in the Authlete Management Console
As mentioned in `3.3.2. Authlete API Call`, Authlete 3 has a single console to configure services and clients.
You can configure the Verifiable Credentials such as support of the `OID4VCI` specification in the [Authlete Management Console](https://console.authlete.com/).
#### 3.7.1. Service Settings
**To enable `oid4vci` in Authlete Service Settings:**
1. Log in to the [Authlete Management Console](https://console.authlete.com/)
2. Click on your Organization name and choose your Service.
3. Navigate to **Service Settings > Verifiable Credentials > General**
4. Under the `Verifiable Credentials Feature` section, press `Enable` to turn on support for Verifiable Credentials.
5. Optionally, enable the `Anonymous Access` property if you want to allow token requests by unidentifiable client applications.
6. Click the `Save Changes` button to apply the updates.

**To configure `oid4vci` Credential Issuer Metadata Properties:**
1. Navigate to **Service Settings > Verifiable Credentials > Credential Issuer Metadata**
2. Configure the metadata properties to suit your requirements. The following properties correspond to the metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html):
* Authorization Servers
* Credential Issuer Identifier
* Credential Endpoint
* Batch Credential Endpoint
* Deferred Credential Endpoint
* Supported Credentials
* Anonymous Access
3. Click the `Save Changes` button to apply the updates.

#### 3.7.2. Client Settings
**To configure `oid4vci` in Authlete Client Settings:**
1. Log in to the [Authlete Management Console](https://console.authlete.com/)
2. Click on your Organization name and choose your Service.
3. Navigate to **Client Settings > Verifiable Credentials > General**
4. Under the `Credential Response Encryption` section, enable `Require` option to turn on support for response encryption.
5. Click the `Save Changes` button to apply the updates.

## 4. OID4VCI Demo
**\[BREAKING CHANGE]**
The examples in Sections 4.1 through 4.5 are based on OID4VCI 1.0 ID1. Please
note that following them exactly will not work with the current implementation.
For Authlete settings and demo procedures for OID4VCI 1.0 Final, please refer
to oid4vci-final-setup.md in the
oid4vci-demo repository.
### 4.1. Pre-Authorized Code Flow + Key Proof + SD-JWT VC
#### 4.1.1. Setup
Download the resources used in this demo.
```shell theme={null}
git clone git@github.com:authlete/oid4vci-demo.git
cd oid4vci-demo
```
Set up some shell variables for this demo.
```shell theme={null}
CLIENT_ID=218232426
TOKEN_ENDPOINT=https://trial.authlete.net/api/token
CREDENTIAL_ISSUER=https://trial.authlete.net
CREDENTIAL_ENDPOINT=https://trial.authlete.net/api/credential
```
#### 4.1.2. Pre-Authorized Code
Access [https://trial.authlete.net/api/offer/issue](https://trial.authlete.net/api/offer/issue) to generate a
"**credential offer**" that contains a "**pre-authorized code**".
The page displayed at the URL provides a form to create an arbitrary
credential offer for demo purposes. If "Pre-authorized code grant included"
in the form is checked, a pre-authorized code will be included in the
credential offer being issued.
Input `inga` and `inga` in the "Login ID" field and the "Password" field,
confirm that "Pre-authorized code grant included" is checked, and press
the "Submit" button. You will see a result page displayed.
The result page will show a QR code which represents a URL including a
credential offer. The content of the credential offer is shown in the
JSON placed under the QR code. The value of the `pre-authorized_code`
property in the JSON is the issued pre-authorized code.
Set the issued pre-authorized code to shell variable `PRE_AUTHORIZED_CODE`
to use it in the next step.
```shell theme={null}
PRE_AUTHORIZED_CODE=NH9udMon5pTuuvbsNsHUNWf8tpU__9wt-gsO9LeYthc
```
#### 4.1.3. Access Token
Send a **token request** using the pre-authorized code flow.
The client for this demo is a public client, so client authentication is not required.
That is, it's not necessary to add request parameters related to client authentication.
```shell theme={null}
curl -s $TOKEN_ENDPOINT \
-d client_id=$CLIENT_ID \
-d grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code \
-d pre-authorized_code=$PRE_AUTHORIZED_CODE
```
The token endpoint will return a response like below.
```json theme={null}
{
"access_token": "xj2YRmSV-_e15n7mTXSvkCH-Yw-XklRagEHF5WXE7R4",
"token_type": "Bearer",
"expires_in": 86400,
"scope": null,
"refresh_token": "Oq7H2GsuES4Z6d_63Dn7rWhJ9rCpgzmzwQ-BYtGR1yE",
"c_nonce": "EhTC8LA6kVrrO6_XiC7N6N_wXdma2Zs1LHAQBZ5E0T0",
"c_nonce_expires_in": 86400
}
```
The response will contain the `access_token` parameter and the `c_nonce` parameter.
Set the values of the response parameters to shell variables for later use.
```shell theme={null}
ACCESS_TOKEN=xj2YRmSV-_e15n7mTXSvkCH-Yw-XklRagEHF5WXE7R4
C_NONCE=EhTC8LA6kVrrO6_XiC7N6N_wXdma2Zs1LHAQBZ5E0T0
```
#### 4.1.4. Key Proof
Generate a "**key proof JWT**" using the holder key `holder.jwk` and the
`generate-key-proof` script. The JWK file and the script are contained in the
[oid4vci-demo repository][AUTHLETE_OID4VCI_DEMO].
```shell theme={null}
./generate-key-proof \
-i $CREDENTIAL_ISSUER \
-k holder.jwk \
-c $CLIENT_ID \
-n $C_NONCE
```
The `generate-key-proof` script will generate a key proof JWT like below.
```
eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiUFN4UXJEMnpsMF9tWGNBcXoxbWdxU2VCb0Jobm14Mnl4QkVwckJZOEYyMCIsInkiOiJ4VjhmYmkxRlNvc1V1bkxldUxOdUxrSmlxbVk2VEtpTW51ci1HbjJ3UjEwIn19.eyJpc3MiOiIyMTgyMzI0MjYiLCJhdWQiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsImlhdCI6MTcwMzg0NzM3Niwibm9uY2UiOiJFaFRDOExBNmtWcnJPNl9YaUM3TjZOX3dYZG1hMlpzMUxIQVFCWjVFMFQwIn0.6l8QnPTclDUoWH5PsVsZQDauA_HcIVDGxU9-TfezflIIAzTFgeC5nTr5rLBkEIgcfUvkUOwKqlM06LdVVwTZlw
```
Decoding the header and the payload of the key proof JWT by base64url will show the following JSONs.
```json theme={null}
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
"y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
}
}
```
```json theme={null}
{
"iss": "218232426",
"aud": "https://trial.authlete.net",
"iat": 1703847376,
"nonce": "EhTC8LA6kVrrO6_XiC7N6N_wXdma2Zs1LHAQBZ5E0T0"
}
```
The result of executing the `generate-key-proof` script can be directly set to
the shell variable `KEY_PROOF_JWT` by doing the following.
```shell theme={null}
KEY_PROOF_JWT=`./generate-key-proof -i $CREDENTIAL_ISSUER -k holder.jwk -c $CLIENT_ID -n $C_NONCE`
```
#### 4.1.5. SD-JWT VC
Send a "**credential request**" with the generated key proof JWT to the "**credential endpoint**".
```shell theme={null}
curl -s $CREDENTIAL_ENDPOINT \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"format": "vc+sd-jwt",
"vct": "https://credentials.example.com/identity_credential",
"proof": {
"proof_type": "jwt",
"jwt":"'${KEY_PROOF_JWT}'"
}
}'
```
The credential endpoint will return a response like below.
```json theme={null}
{
"credential": "eyJraWQiOiJKMUZ3SlA4N0M2LVFOX1dTSU9tSkFRYzZuNUNRX2JaZGFGSjVHRG5XMVJrIiwidHlwIjoidmMrc2Qtand0IiwiYWxnIjoiRVMyNTYifQ.eyJfc2QiOlsiMERFMXBjUHo3LURtYlc5NlRLaFlHUFlENi05dnNtUzFra21EWVQ4NnF1NCIsIjdXeHNTejhXSWtBaHdLZmQ0aUVXRFBrOUhuMFdqZ1V6N0pHX3hqekVwTjQiLCJCVVdOQlh2bkJwZ1ZWaUpaLVdPTWFxZHhiOTRfSUR1OEhNaEZnUjU2aXd3IiwiR1BqSG1lOFhaS2JvWFhLMllPU2Y4cE10QXNzSGlKQU5QdW91WkI1QTZuNCIsIklhN3FqdVNDSmdGWDRiX09uS2p4dWJsU0tTWmRWcUhFdEpVRHZHRWtEWVEiLCJQenUtUEVqUlE5bF9vNkVEUmp0QnBpaWZqUVNMV0RNZ2ExM2VscTZpRW44IiwiVTRQWHdKMDMtenlEeUtpZFlPQWsxUkMzNWNZT2FCdnZ5bG9BQnM3bXZ1RSIsImxrT1BCTm9qNFk0OGE4a1F4VmlTSkJlQWdieE5YTEdSaEI5dkliejJCdjgiLCJuUDdMcmk2QmlqVHF0VVI2cTRfakwxTzlyWnd2OE9sdGlEX1lUWGlTa2ZrIiwidF9WbWxQQmxNcENiRG5NUjhzYUdDX2ZqMVZCb3FCLVUyZk1NYWZNbVRNayJdLCJ2Y3QiOiJodHRwczovL2NyZWRlbnRpYWxzLmV4YW1wbGUuY29tL2lkZW50aXR5X2NyZWRlbnRpYWwiLCJfc2RfYWxnIjoic2hhLTI1NiIsImlzcyI6Imh0dHBzOi8vdHJpYWwuYXV0aGxldGUubmV0IiwiY25mIjp7Imp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2Iiwia2lkIjoiNE05a0lyQjlXWXp0MUdRZ0wxMmx6ZEJac0d5ZVYzbGdQS292MjhvVDVMNCIsIngiOiJQU3hRckQyemwwX21YY0FxejFtZ3FTZUJvQmhubXgyeXhCRXByQlk4RjIwIiwieSI6InhWOGZiaTFGU29zVXVuTGV1TE51TGtKaXFtWTZUS2lNbnVyLUduMndSMTAifX0sImlhdCI6MTcwMzg0NzYwNX0.2k4JTf61BOs461fLToA9VwZbY64i9TIfchZVasC0OCx2rDJYodeRc6uVjYuHBOZUHlMJ1WdTjCFfKuxE5juxMA~WyIxMjBTWFZhTzZKTWxoc0dIZUdVdktRIiwic3ViIiwiMTAwNCJd~WyJVUl9MTU5ocGFLUUVpbEQ1TXc5RnB3IiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ~WyJhT2huYjgySmFMbjZ0OHRZZXpGanl3IiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd~WyJGNzE0ZktfRFpSMm83ajFWRkpIUW5BIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd~",
"c_nonce": "EhTC8LA6kVrrO6_XiC7N6N_wXdma2Zs1LHAQBZ5E0T0",
"c_nonce_expires_in": 85956
}
```
The value of the `credential` parameter in the response is the issued SD-JWT VC.
If the SD-JWT VC is set to the shell variable `SD_JWT`, the content of the SD-JWT VC
can be decoded by invoking the `decode-sd-jwt` script as follows.
```shell theme={null}
./decode-sd-jwt $SD_JWT
```
The result will look like below.
```json theme={null}
{
"kid": "J1FwJP87C6-QN_WSIOmJAQc6n5CQ_bZdaFJ5GDnW1Rk",
"typ": "vc+sd-jwt",
"alg": "ES256"
}
{
"_sd": [
"0DE1pcPz7-DmbW96TKhYGPYD6-9vsmS1kkmDYT86qu4",
"7WxsSz8WIkAhwKfd4iEWDPk9Hn0WjgUz7JG_xjzEpN4",
"BUWNBXvnBpgVViJZ-WOMaqdxb94_IDu8HMhFgR56iww",
"GPjHme8XZKboXXK2YOSf8pMtAssHiJANPuouZB5A6n4",
"Ia7qjuSCJgFX4b_OnKjxublSKSZdVqHEtJUDvGEkDYQ",
"Pzu-PEjRQ9l_o6EDRjtBpiifjQSLWDMga13elq6iEn8",
"U4PXwJ03-zyDyKidYOAk1RC35cYOaBvvyloABs7mvuE",
"lkOPBNoj4Y48a8kQxViSJBeAgbxNXLGRhB9vIbz2Bv8",
"nP7Lri6BijTqtUR6q4_jL1O9rZwv8OltiD_YTXiSkfk",
"t_VmlPBlMpCbDnMR8saGC_fj1VBoqB-U2fMMafMmTMk"
],
"vct": "https://credentials.example.com/identity_credential",
"_sd_alg": "sha-256",
"iss": "https://trial.authlete.net",
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"kid": "4M9kIrB9WYzt1GQgL12lzdBZsGyeV3lgPKov28oT5L4",
"x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
"y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
}
},
"iat": 1703847605
}
{
"digest": "lkOPBNoj4Y48a8kQxViSJBeAgbxNXLGRhB9vIbz2Bv8",
"WyIxMjBTWFZhTzZKTWxoc0dIZUdVdktRIiwic3ViIiwiMTAwNCJd": [
"120SXVaO6JMlhsGHeGUvKQ",
"sub",
"1004"
]
}
{
"digest": "0DE1pcPz7-DmbW96TKhYGPYD6-9vsmS1kkmDYT86qu4",
"WyJVUl9MTU5ocGFLUUVpbEQ1TXc5RnB3IiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ": [
"UR_LMNhpaKQEilD5Mw9Fpw",
"given_name",
"Inga"
]
}
{
"digest": "nP7Lri6BijTqtUR6q4_jL1O9rZwv8OltiD_YTXiSkfk",
"WyJhT2huYjgySmFMbjZ0OHRZZXpGanl3IiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd": [
"aOhnb82JaLn6t8tYezFjyw",
"family_name",
"Silverstone"
]
}
{
"digest": "U4PXwJ03-zyDyKidYOAk1RC35cYOaBvvyloABs7mvuE",
"WyJGNzE0ZktfRFpSMm83ajFWRkpIUW5BIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd": [
"F714fK_DZR2o7j1VFJHQnA",
"birthdate",
"1991-11-06"
]
}
```
### 4.2. Authorization Code Flow + PAR + DPoP + mdoc
#### 4.2.1. Setup
Download the resources used in this demo.
```shell theme={null}
git clone git@github.com:authlete/oid4vci-demo.git
cd oid4vci-demo
```
Set up some shell variables for this demo.
```shell theme={null}
CLIENT_ID=218232426
TOKEN_ENDPOINT=https://trial.authlete.net/api/token
CREDENTIAL_ISSUER=https://trial.authlete.net
CREDENTIAL_ENDPOINT=https://trial.authlete.net/api/credential
PAR_ENDPOINT=https://trial.authlete.net/api/par
```
#### 4.2.2. Request URI
Generate a "**DPoP proof JWT**" ([RFC 9449][RFC_9449]) using `dpop.jwk`, a private key for DPoP,
and the `generate-dpop-proof` script.
```shell theme={null}
DPOP_PROOF_JWT=`./generate-dpop-proof -k dpop.jwk -m POST -u $PAR_ENDPOINT`
```
The `generate-dpop-proof` script will generate a DPoP proof JWT like below.
```
eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiaEdmcXpHWGdhbzFRZ1ZJVFk2a2lIWU9LYmFMWEJ4VHFQSmE0RU9pbXhoSSIsInkiOiJFMUtpQV9mQTJ4OElycnlzb0dkbkJUTUI1LW8zRUpUX01nUUFfSG1HdTlNIn19.eyJqdGkiOiJoRm9HQkFBN3ZXTHExbWJ6IiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vdHJpYWwuYXV0aGxldGUubmV0L2FwaS9wYXIiLCJpYXQiOjE3MDM4NjQ5ODR9.9VZtrwjASCEeO6v0SuGqEttYtoHORtGMNn95mSx4uNv04oA8hSDDBo4CoPQiaGsEjunJ_d_zKR7VsrF9M8BBZA
```
Decoding the header and the payload of the DPoP proof JWT by base64url will
show the following JSONs. Note that the `htu` claim in the payload holds the
URL of the PAR endpoint.
```json theme={null}
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "hGfqzGXgao1QgVITY6kiHYOKbaLXBxTqPJa4EOimxhI",
"y": "E1KiA_fA2x8IrrysoGdnBTMB5-o3EJT_MgQA_HmGu9M"
}
}
```
```json theme={null}
{
"jti": "hFoGBAA7vWLq1mbz",
"htm": "POST",
"htu": "https://trial.authlete.net/api/par",
"iat": 1703864984
}
```
Send a **PAR request** to the PAR endpoint. The points here are (1) that the PAR
request contains the `DPoP` header and (2) that the `scope` parameter contains
`org.iso.18013.5.1.mDL`.
```shell theme={null}
curl -s $PAR_ENDPOINT \
-H "DPoP: $DPOP_PROOF_JWT" \
-d client_id=$CLIENT_ID \
-d response_type=code \
-d scope=org.iso.18013.5.1.mDL
```
This `scope` value assumes that the `credential_configurations_supported`
JSON object in the credential issuer metadata contains at least one credential configuration
whose `scope` property holds `org.iso.18013.5.1.mDL`.
```json theme={null}
{
"credential_configurations_supported": {
"org.iso.18013.5.1.mDL": {
"format": "mso_mdoc",
"doctype": "org.iso.18013.5.1.mDL",
"scope": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": {},
...
}
}
}
},
...
}
```
The PAR endpoint will return a response like below. The value of the
`request_uri` parameter in the response is the issued **request URI**.
It will be used in the next step.
```json theme={null}
{
"expires_in": 600,
"request_uri": "urn:ietf:params:oauth:request_uri:du-ptCtuukbVDi2MgOjYwwb99cl-ho0bzzLb0X0u1n0"
}
```
#### 4.2.3. Authorization Code
Send an **authorization request** to the authorization endpoint using a web browser.
Don't forget to replace `$REQUEST_URI` in the URL with the actual request URI
you received from the PAR endpoint in the previous step.
```
https://trial.authlete.net/api/authorization?client_id=218232426&request_uri=$REQUEST_URI
```
The authorization page will be displayed. Input `inga` and `inga` in the
"Login ID" field and the "Password" field there, and press the "Authorize"
button.
You will be redirected to the redirection endpoint.
The page displayed at this endpoint will show you the value of the issued
**authorization code**. It will be used in the next step.
#### 4.2.4. Access Token
Generate a DPoP proof JWT to access the token endpoint. Make sure that the argument
given to the `-u` option of the `generate-dpop-proof` script is `$TOKEN_ENDPOINT`
(not `$PAR_ENDPOINT`).
```shell theme={null}
DPOP_PROOF_JWT=`./generate-dpop-proof -k dpop.jwk -m POST -u $TOKEN_ENDPOINT`
```
Decoding the header and the payload of the DPoP proof JWT by base64url will
show the following JSONs. The `htu` claim in the payload should hold the
URL of the token endpoint.
```json theme={null}
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "hGfqzGXgao1QgVITY6kiHYOKbaLXBxTqPJa4EOimxhI",
"y": "E1KiA_fA2x8IrrysoGdnBTMB5-o3EJT_MgQA_HmGu9M"
}
}
```
```json theme={null}
{
"jti": "KHV6KaCjtqwfnb8r",
"htm": "POST",
"htu": "https://trial.authlete.net/api/token",
"iat": 1703865524
}
```
Send a **token request** using the authorization code flow to the token endpoint.
Don't forget to set the authorization code issued in the previous step to the shell
variable `AUTHORIZATION_CODE` before executing the following command.
```shell theme={null}
AUTHORIZATION_CODE=QaPvTUqX-aPDnrcFoCcYDZHW66RzC_vfi6EDq7derNs
```
```shell theme={null}
curl -s $TOKEN_ENDPOINT \
-H "DPoP: $DPOP_PROOF_JWT" \
-d client_id=$CLIENT_ID \
-d grant_type=authorization_code \
-d code=$AUTHORIZATION_CODE
```
The token endpoint will return a response like below.
```json theme={null}
{
"access_token": "T01u7-43MOA17hB8DqW-dEaBqUpStWtitYoVW1ewlH4",
"token_type": "DPoP",
"expires_in": 86400,
"scope": "org.iso.18013.5.1.mDL",
"refresh_token": "17on7yghEeGPbaXmjTEMgEGPG8S79DweIBtCq0MZOHE",
"c_nonce": "LQwqEX7sT1uJkzzTdULvLsHcUvXfUaT79bkzLWEi7jw",
"c_nonce_expires_in": 86400
}
```
The response will contain the `access_token` parameter. Please set the value
of the parameter to the shell variable `ACCESS_TOKEN` for the next step.
```shell theme={null}
ACCESS_TOKEN=T01u7-43MOA17hB8DqW-dEaBqUpStWtitYoVW1ewlH4
```
#### 4.2.5. mdoc
Generate a DPoP proof JWT to access the credential endpoint. Make sure that (1)
the argument given to the `-u` option of the `generate-dpop-proof` script is
`$CREDENTIAL_ENDPOINT` and (2) the `-a` option must be given to embed the
`ath` claim in the DPoP proof JWT.
```shell theme={null}
DPOP_PROOF_JWT=`./generate-dpop-proof -k dpop.jwk -m POST -u $CREDENTIAL_ENDPOINT -a $ACCESS_TOKEN`
```
Decoding the header and the payload of the DPoP proof JWT by base64url will
show the following JSONs. The payload contains the `ath` claim.
```json theme={null}
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "hGfqzGXgao1QgVITY6kiHYOKbaLXBxTqPJa4EOimxhI",
"y": "E1KiA_fA2x8IrrysoGdnBTMB5-o3EJT_MgQA_HmGu9M"
}
}
```
```json theme={null}
{
"jti": "caK7sLDiAF9HbOi2",
"htm": "POST",
"htu": "https://trial.authlete.net/api/credential",
"iat": 1703865668,
"ath": "DUuszLa1NHTMoREsPQE0gB9znn1BRKYPUpOwBpVcLJo"
}
```
Send a "**credential request**" with the DPoP proof JWT and the access token
to the "**credential endpoint**".
```shell theme={null}
curl -s $CREDENTIAL_ENDPOINT \
-H "DPoP: $DPOP_PROOF_JWT" \
-H "Authorization: DPoP $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"format": "mso_mdoc",
"doctype": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": {},
"given_name": {},
"birth_date": {},
"issue_date": {},
"expiry_date": {},
"issuing_country": {},
"document_number": {},
"driving_privileges": {}
}
}
}'
```
The credential endpoint will return a response like below.
```json theme={null}
{
"credential": "omdkb2NUeXBldW9yZy5pc28uMTgwMTMuNS4xLm1ETGxpc3N1ZXJTaWduZWSiam5hbWVTcGFjZXOhcW9yZy5pc28uMTgwMTMuNS4xiNgYWFukaGRpZ2VzdElEAWZyYW5kb21QJ6e6efO1W1kaYmdXHjpSY3FlbGVtZW50SWRlbnRpZmllcmppc3N1ZV9kYXRlbGVsZW1lbnRWYWx1ZdkD7GoyMDIzLTEyLTI52BhYXKRoZGlnZXN0SUQCZnJhbmRvbVBlLj1uVWnDyBHmzifHfxjhcWVsZW1lbnRJZGVudGlmaWVya2V4cGlyeV9kYXRlbGVsZW1lbnRWYWx1ZdkD7GoyMDI0LTEyLTI52BhYWqRoZGlnZXN0SUQDZnJhbmRvbVAMR8e_GTnz4n7RFXKXgrAQcWVsZW1lbnRJZGVudGlmaWVya2ZhbWlseV9uYW1lbGVsZW1lbnRWYWx1ZWtTaWx2ZXJzdG9uZdgYWFKkaGRpZ2VzdElEBGZyYW5kb21QCHsjtktJ-PkSnEpzHOOCnHFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1lbGVsZW1lbnRWYWx1ZWRJbmdh2BhYW6RoZGlnZXN0SUQFZnJhbmRvbVAj-grPYzaQs1Np8Jom4yNpcWVsZW1lbnRJZGVudGlmaWVyamJpcnRoX2RhdGVsZWxlbWVudFZhbHVl2QPsajE5OTEtMTEtMDbYGFhVpGhkaWdlc3RJRAZmcmFuZG9tUByAlsGIDKRVgTjKy9AgNMxxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ5bGVsZW1lbnRWYWx1ZWJVU9gYWFukaGRpZ2VzdElEB2ZyYW5kb21QpIldNj4zHUGw0vcfVFMsDXFlbGVtZW50SWRlbnRpZmllcm9kb2N1bWVudF9udW1iZXJsZWxlbWVudFZhbHVlaDEyMzQ1Njc42BhYoqRoZGlnZXN0SUQIZnJhbmRvbVByP2ESVgTLndyQCDgw1khMcWVsZW1lbnRJZGVudGlmaWVycmRyaXZpbmdfcHJpdmlsZWdlc2xlbGVtZW50VmFsdWWBo3V2ZWhpY2xlX2NhdGVnb3J5X2NvZGVhQWppc3N1ZV9kYXRl2QPsajIwMjMtMDEtMDFrZXhwaXJ5X2RhdGXZA-xqMjA0My0wMS0wMWppc3N1ZXJBdXRohEOhASahGCFZAWEwggFdMIIBBKADAgECAgYBjJHZwhkwCgYIKoZIzj0EAwIwNjE0MDIGA1UEAwwrSjFGd0pQODdDNi1RTl9XU0lPbUpBUWM2bjVDUV9iWmRhRko1R0RuVzFSazAeFw0yMzEyMjIxNDA2NTZaFw0yNDEwMTcxNDA2NTZaMDYxNDAyBgNVBAMMK0oxRndKUDg3QzYtUU5fV1NJT21KQVFjNm41Q1FfYlpkYUZKNUdEblcxUmswWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQCilV5ugmlhHJzDVgqSRE5d8KkoQqX1jVg8WE4aPjFODZQ66fFPFIhWRP3ioVUi67WGQSgTY3F6Vmjf7JMVQ4MMAoGCCqGSM49BAMCA0cAMEQCIGcWNJwFy8RGV4uMwK7k1vEkqQ2xr-BCGRdN8OZur5PeAiBVrNuxV1C9mCW5z2clhDFaXNdP2Lp_7CBQrHQoJhuPcNgYWQHopWd2ZXJzaW9uYzEuMG9kaWdlc3RBbGdvcml0aG1nU0hBLTI1Nmx2YWx1ZURpZ2VzdHOhcW9yZy5pc28uMTgwMTMuNS4xqAFYIKroraryzIKijf6A0qMLKKEXhO4JkfgeofLS35R-tV-CAlggA1kTuTcqhUUzLu689TY7XaXTKDTQTy-X0CTtJbOgqJsDWCA9I2CynkOK1sgi3P7ZmCZJENPGQl8QsWImdXSNXsqEEARYIJ7fk0E18dWrB3R_X0NGXdshFSplLC2WiMGrqotXnbOVBVggs4p4mEragqpfssdgOpeGrmxeBq8kDp7rpVf-8mngGV4GWCAAGjwiUCuOj0CnVGknOCa217wUJthsqNS8CoxZ5BWQEQdYIDwygoPCSFXyutize7ktwCSKY5T7V4mWpizCFoVSkBy7CFggTWvL8cD_12lYMvje8i2T6YLEwNJNfje5lG6jfeS5I31nZG9jVHlwZXVvcmcuaXNvLjE4MDEzLjUuMS5tRExsdmFsaWRpdHlJbmZvo2ZzaWduZWTAdDIwMjMtMTItMjlUMTY6MDE6NTdaaXZhbGlkRnJvbcB0MjAyMy0xMi0yOVQxNjowMTo1N1pqdmFsaWRVbnRpbMB0MjAyNC0xMi0yOVQxNjowMTo1N1pYQPJyDU5h4tDesMtjRzbxcm77l-np35iKtKAKQj67Vh0XRHJsNxKoX_QJRLPrL1u58HuJDbwyA6c1ewBulm4AUAM",
"c_nonce": "LQwqEX7sT1uJkzzTdULvLsHcUvXfUaT79bkzLWEi7jw",
"c_nonce_expires_in": 86256
}
```
The value of the `credential` parameter in the response is the issued mdoc.
The website "**CBOR Zone**" ([https://cbor.zone/](https://cbor.zone/)) can be used to decode the mdoc.
Copy the value of the `credential` parameter, paste it to the textarea of
the "Input" section in the CBOR Zone, choose the `base64url` radio button, and
press the "Generate" button. You'll see the content of the mdoc in the CBOR
Diagnostic Notation ([RFC 8949, 8. Diagnostic Notation][RFC_8949_8],
[RFC 8610, Appendix G. Extended Diagnostic Notation][RFC_8610_G]).
```
{
"docType": "org.iso.18013.5.1.mDL",
"issuerSigned": {
"nameSpaces": {
"org.iso.18013.5.1": [
24(<<
{
"digestID": 1,
"random": h'27a7ba79f3b55b591a6267571e3a5263',
"elementIdentifier": "issue_date",
"elementValue": 1004("2023-12-29")
}
>>),
24(<<
{
"digestID": 2,
"random": h'652e3d6e5569c3c811e6ce27c77f18e1',
"elementIdentifier": "expiry_date",
"elementValue": 1004("2024-12-29")
}
>>),
24(<<
{
"digestID": 3,
"random": h'0c47c7bf1939f3e27ed115729782b010',
"elementIdentifier": "family_name",
"elementValue": "Silverstone"
}
>>),
24(<<
{
"digestID": 4,
"random": h'087b23b64b49f8f9129c4a731ce3829c',
"elementIdentifier": "given_name",
"elementValue": "Inga"
}
>>),
24(<<
{
"digestID": 5,
"random": h'23fa0acf633690b35369f09a26e32369',
"elementIdentifier": "birth_date",
"elementValue": 1004("1991-11-06")
}
>>),
24(<<
{
"digestID": 6,
"random": h'1c8096c1880ca4558138cacbd02034cc',
"elementIdentifier": "issuing_country",
"elementValue": "US"
}
>>),
24(<<
{
"digestID": 7,
"random": h'a4895d363e331d41b0d2f71f54532c0d',
"elementIdentifier": "document_number",
"elementValue": "12345678"
}
>>),
24(<<
{
"digestID": 8,
"random": h'723f61125604cb9ddc90083830d6484c',
"elementIdentifier": "driving_privileges",
"elementValue": [
{
"vehicle_category_code": "A",
"issue_date": 1004("2023-01-01"),
"expiry_date": 1004("2043-01-01")
}
]
}
>>)
]
},
"issuerAuth": [
h'a10126',
{
33: h'3082015d30820104a0030201020206018c91d9c219300a06082a8648ce3d04030230363134303206035504030c2b4a3146774a50383743362d514e5f5753494f6d4a415163366e3543515f625a6461464a3547446e5731526b301e170d3233313232323134303635365a170d3234313031373134303635365a30363134303206035504030c2b4a3146774a50383743362d514e5f5753494f6d4a415163366e3543515f625a6461464a3547446e5731526b3059301306072a8648ce3d020106082a8648ce3d03010703420004028a5579ba09a58472730d582a49113977c2a4a10a97d63560f1613868f8c5383650eba7c53c52215913f78a85548baed61904a04d8dc5e959a37fb24c550e0c300a06082a8648ce3d040302034700304402206716349c05cbc446578b8cc0aee4d6f124a90db1afe04219174df0e66eaf93de022055acdbb15750bd9825b9cf672584315a5cd74fd8ba7fec2050ac7428261b8f70'
},
24(<<
{
"version": "1.0",
"digestAlgorithm": "SHA-256",
"valueDigests": {
"org.iso.18013.5.1": {
1: h'aae8adaaf2cc82a28dfe80d2a30b28a11784ee0991f81ea1f2d2df947eb55f82',
2: h'035913b9372a8545332eeebcf5363b5da5d32834d04f2f97d024ed25b3a0a89b',
3: h'3d2360b29e438ad6c822dcfed998264910d3c6425f10b1622675748d5eca8410',
4: h'9edf934135f1d5ab07747f5f43465ddb21152a652c2d9688c1abaa8b579db395',
5: h'b38a78984ada82aa5fb2c7603a9786ae6c5e06af240e9eeba557fef269e0195e',
6: h'001a3c22502b8e8f40a75469273826b6d7bc1426d86ca8d4bc0a8c59e4159011',
7: h'3c328283c24855f2bad8b37bb92dc0248a6394fb578996a62cc2168552901cbb',
8: h'4d6bcbf1c0ffd7695832f8def22d93e982c4c0d24d7e37b9946ea37de4b9237d'
}
},
"docType": "org.iso.18013.5.1.mDL",
"validityInfo": {
"signed": 0("2023-12-29T16:01:57Z"),
"validFrom": 0("2023-12-29T16:01:57Z"),
"validUntil": 0("2024-12-29T16:01:57Z")
}
}
>>),
h'f2720d4e61e2d0deb0cb634736f1726efb97e9e9df988ab4a00a423ebb561d1744726c3712a85ff40944b3eb2f5bb9f07b890dbc3203a7357b006e966e005003'
]
}
}
```
### 4.3. POTENTIAL Interop Event / Track 1 / Light Profile
[POTENTIAL][POTENTIAL] is a European organization dedicated to European Digital Identity.
The organization has been hosting an interoperability event since spring 2024. The event
is divided into six tracks. Tracks 1 and 2 are designated for testing the inteoperability
of credential issuers. In Track 1, mdoc is used as the format for verifiable credentials,
while SD-JWT VC is used in Track 2.
Track 1 defines two profiles. One is called the "light" profile. The other is called the
"full" profile. This section explains the steps for the light profile.
#### 4.3.1. Settings
##### 4.3.1.1. Authorization Server Settings
| Parameter | Value |
| :- | :- |
| Issuer Identifier | `https://trial.authlete.net` |
| Authorization Endpoint | `https://trial.authlete.net/api/authorization` |
| Token Endpoint | `https://trial.authlete.net/api/token` |
| Discovery Endpoint | `[https://trial.authlete.net/.well-known/openid-configuration](https://trial.authlete.net/.well-known/openid-configuration)` |
| Entity Configuration | `[https://trial.authlete.net/.well-known/openid-federation](https://trial.authlete.net/.well-known/openid-federation)` |
The source code of the **authorization server** is available at
[https://github.com/authlete/java-oauth-server](https://github.com/authlete/java-oauth-server). Note that this implementation
is a sample and is not intended for commercial use.
##### 4.3.1.2. Credential Issuer Settings
| Parameter | Value |
| :- | :- |
| Issuer Identifier | `https://trial.authlete.net` |
| Credential Endpoint | `https://trial.authlete.net/api/credential` |
| Metadata Endpoint | `[https://trial.authlete.net/.well-known/openid-credential-issuer](https://trial.authlete.net/.well-known/openid-credential-issuer)` |
| Entity Configuration | `[https://trial.authlete.net/.well-known/openid-federation](https://trial.authlete.net/.well-known/openid-federation)` |
The source code of the **credential issuer** is the same as that of the
authorization server.
##### 4.3.1.3. Client Settings
| Parameter | Value |
| :- | :- |
| Client ID | `track1_light` |
| Client Type | public (= [client authentication][CLIENT_AUTHENTICATION] is not required) |
| Redirect URIs |
|
If you need to register additional redirect URIs to this client, or if you need
an independent client dedicated to your use, please [contact us][CONTACT_US].
To Authlete members: New clients should be created under
"Trial" (organization=283415) /
"NextDev" (server=2704222) /
"Authlete Trial" (service=986126671).
#### 4.3.2. Demo Steps
##### 4.3.2.1. Step 1 : Credential Offer
The sample implementation of credential issuer provides a web page where
developers can generate an arbitrary **credential offer** for testing.
The URL of the page is [https://trial.authlete.net/api/offer/issue](https://trial.authlete.net/api/offer/issue).
Accessing the web page, you will find a form to configure the content of
a credential offer.
Edit the form as instructed below.
1. Input `inga` and `inga` into the "Login ID" field and the "Password" field.
2. Edit the "Credential Configuration IDs". The value should be a JSON array containing the string `potential.light.profile`.
3. Check the "include?" checkbox next to "Authorization Code Grant".
4. Uncheck the "include?" checkbox next to "Pre-Authorized Code Grant".
Press the "Submit" button after editing the form, and you will find a QR code
that represents a URL containing the generated credential offer.
The JSON under the QR code represents the content of the credential offer.
The value of the `issuer_state` property in the JSON is the issued
**issuer state**. In the above example, the value of the issuer state is
`tXkAkhSu5N9ORSNES9T64Bd9PAiKn9OmEOT5qDL0lkA`.
The issuer state is to be included in the **authorization request** you will
make later.
##### 4.3.2.2. Step 2 : Code Verifier and Code Challenge
Generate a **code verifier** and compute the corresponding **code challenge**
(cf. [RFC 7636][RFC_7636]) by using the `pkce` command
```shell theme={null}
git clone git@github.com:authlete/oid4vci-demo.git
cd oid4vci-demo
./pkce
```
The `pkce` command will print a generated code verifier and the computed code
challenge like below.
```shell theme={null}
CODE_VERIFIER=gzRNlV7DLS_HyKMQKQMrzgYQ8aY3H2rVJ3iIlYK0cjE
CODE_CHALLENGE=j_4gpG9Kr3M7ilMO-MRoSROP-W3h2EZem0KSEU-RAhM
```
The computed code challenge is to be included in the **authorization request**
in the next step, and the generated code verifier is to be included in the
**token request** that will be made after the authorization request.
##### 4.3.2.3. Step 3 : Authorization Request
Make an **authorization request** using the **authorization code flow**
(cf. [RFC 6749, 4.1][RFC_6749_4_1]) by inputting the following URL in the
address bar of your web browser. Don't forget to replace `${ISSUER_STATE}` and
`${CODE_CHALLENGE}` in the URL with the actual values of the issuer state and
the code challenge you have created in the previous steps.
```
https://trial.authlete.net/api/authorization?client_id=track1_light&response_type=code&issuer_state=${ISSUER_STATE}&redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection&code_challenge=${CODE_CHALLENGE}&code_challenge_method=S256&prompt=login
```
Accessing the URL will show you an **authorization page**. The page contains
a login form. Input `inga` and `inga` into the Login ID field and the Password
field in the login form, and press the "Authorize" button.
You will be redirected to the **redirection endpoint** (cf.
[RFC 6749, 3.1.2][RFC_6749_3_1_2]).
This redirection endpoint displays key-value pairs that it has received.
The value of the `code` parameter displayed there is the issued
**authorization code**. In this example, the value of the authorization code is
`gR43MQf2olvhMt6KekVDkUOdQPrVYgBiKXMwu_UFnB8`.
The authorization code is to be used in the token request in the next section.
Note that the authorization code will expire in 10 minutes, so you have to make
a token request promptly.
##### 4.3.2.4. Step 4 : Token Request
Make a **token request** using the authorization code flow. Don't forget to
replace `${AUTHORIZATION_CODE}` and `${CODE_VERIFIER}` in the `curl` command
below with the actual values you have obtained in the previous steps.
```shell theme={null}
curl -s https://trial.authlete.net/api/token \
-d client_id=track1_light \
-d grant_type=authorization_code \
-d code=${AUTHORIZATION_CODE} \
-d redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection \
-d code_verifier=${CODE_VERIFIER}
```
If the token request is valid, the token endpoint returns JSON like below.
```json theme={null}
{
"access_token": "tNR1stglRuBVUtS2sZ7tiThPcpNDENxY0LniVpRdp0E",
"token_type": "Bearer",
"expires_in": 86400,
"scope": null,
"refresh_token": "nt3ba0H42UqMBw8FMY2keDgGAF65pBHY45-kydZFmSE",
"c_nonce": "v-1b-n82kEJGbHROSekGsmR-xEuamCxY_T0tXtQN-dY",
"c_nonce_expires_in": 86400
}
```
The value of the `access_token` property in the JSON is the issued
**access token**. It needs to be presented when you make a **credential request**.
The value of the `c_nonce` property is a nonce that must be included in a
**key proof**.
##### 4.3.2.5. Step 5 : CWT Key Proof
The [authlete/cbor][AUTHLETE_CBOR] library contains a utility class,
`CWTKeyProofBuilder`, that can generate a **CWT key proof**. The shell script,
`bin/generate-cwt-key-proof`, which is included in the repository of the
library, is a wrapper to invoke the utility class from the command line.
A CWT key proof can be generated as shown below. Don't forget to replace
`${NONCE}` in the command line with the actual value of `c_nonce` that has
been issued from the token endpoint in the previous step, and to replace
`${PRIVATE_KEY_FILE}` with the actual path of a file containing a private
key in the JWK format (cf. [RFC 7517][RFC_7517]). The file, `holder.jwk`, in
the [authlete/oid4vci-demo][AUTHLETE_OID4VCI_DEMO] repository can be used
for the purpose.
```shell theme={null}
git clone git@github.com:authlete/cbor.git
cd cbor
mvn compile
./bin/generate-cwt-key-proof \
--issuer https://trial.authlete.net \
--key ${PRIVATE_KEY_FILE} \
--client track1_light \
--nonce ${NONCE}
```
The `generate-cwt-key-proof` script will print a CWT key proof like below.
```
2D3ShFifowEmA3RvcGVuaWQ0dmNpLXByb29mK2N3dGhDT1NFX0tleVh7pgECAlgrMWU1QVk5RXlCMDFYblV6YTZMcEp6azAybjZZX0FtbW5TYjBGQmVOVlZyVQMmIAEhWCA9LFCsPbOXT-ZdwCrPWaCpJ4GgGGebHbLEESmsFjwXbSJYIMVfH24tRUqLFLpy3rizbi5CYqpmOkyojJ7q_hp9sEddoFhgpAFsdHJhY2sxX2xpZ2h0A3gaaHR0cHM6Ly90cmlhbC5hdXRobGV0ZS5uZXQGGmZf3KsKWCt2LTFiLW44MmtFSkdiSFJPU2VrR3NtUi14RXVhbUN4WV9UMHRYdFFOLWRZWEDVVsA-MQ9dAiaIRThTJ5JgmND4RZuhxcIiNx04TZ7fSqlQYJlRW9AyNqXeJHIEl1KqQs_yZtlPd98kRbvziTEi
```
The following is the result of decoding the CWT key proof above using [CBOR Zone][CBOR_ZONE].
```
61(18(/ COSE_Sign1 / [
/ protected / <<
{
1: -7,
3: "openid4vci-proof+cwt",
"COSE_Key": h'a6010202582b3165354159394579423031586e557a61364c704a7a6b30326e36595f416d6d6e5362304642654e56567255032620012158203d2c50ac3db3974fe65dc02acf59a0a92781a018679b1db2c41129ac163c176d225820c55f1f6e2d454a8b14ba72deb8b36e2e4262aa663a4ca88c9eeafe1a7db0475d'
}
>>,
/ unprotected / {
},
h'a4016c747261636b315f6c6967687403781a68747470733a2f2f747269616c2e617574686c6574652e6e6574061a665fdcab0a582b762d31622d6e38326b454a476248524f53656b47736d522d784575616d4378595f5430745874514e2d6459',
h'd556c03e310f5d02268845385327926098d0f8459ba1c5c222371d384d9edf4aa9506099515bd03236a5de2472049752aa42cff266d94f77df2445bbf3893122'
]))
```
The value of `COSE_Key` in the protected header is a CBOR byte string, which
wraps the COSE key. The content of the byte string is decoded as follows:
```
{
1: 2,
2: h'3165354159394579423031586e557a61364c704a7a6b30326e36595f416d6d6e5362304642654e56567255',
3: -7,
-1: 1,
-2: h'3d2c50ac3db3974fe65dc02acf59a0a92781a018679b1db2c41129ac163c176d',
-3: h'c55f1f6e2d454a8b14ba72deb8b36e2e4262aa663a4ca88c9eeafe1a7db0475d'
}
```
##### 4.3.2.6. Step 6 : Credential Request
Make a **credential request** with the access token and the CWT key proof.
Don't forget to replace `${ACCESS_TOKEN}` and `${CWT_KEY_PROOF}` in the
command line with the actual values.
```shell theme={null}
curl -s https://trial.authlete.net/api/credential \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"format": "mso_mdoc",
"doctype": "org.iso.18013.5.1.mDL",
"claims": {
"org.iso.18013.5.1": {
"family_name": {},
"given_name": {},
"birth_date": {},
"issue_date": {},
"expiry_date": {},
"issuing_country": {},
"document_number": {},
"driving_privileges": {}
}
},
"proof": {
"proof_type": "cwt",
"cwt":"'${CWT_KEY_PROOF}'"
}
}'
```
If the credential request is valid, the credential endpoint returns JSON
like below. The value of the `credential` property in the JSON is the
issued **verifiable credential**.
```json theme={null}
{
"credential": "ompuYW1lU3BhY2VzoXFvcmcuaXNvLjE4MDEzLjUuMYjYGFhbpGhkaWdlc3RJRAFmcmFuZG9tUEJDfxiBFQGMwsBY7jE6mkdxZWxlbWVudElkZW50aWZpZXJqaXNzdWVfZGF0ZWxlbGVtZW50VmFsdWXZA-xqMjAyNC0wNi0wNdgYWFykaGRpZ2VzdElEAmZyYW5kb21QuWRGth4zjRXOJN_iGNTy0nFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZWxlbGVtZW50VmFsdWXZA-xqMjAyNS0wNi0wNdgYWFqkaGRpZ2VzdElEA2ZyYW5kb21Q7Zx7xYZtB0D02nL-x0UGFXFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZWxlbGVtZW50VmFsdWVrU2lsdmVyc3RvbmXYGFhSpGhkaWdlc3RJRARmcmFuZG9tUPMV5L8B03Uuj0GRMFZvWpJxZWxlbWVudElkZW50aWZpZXJqZ2l2ZW5fbmFtZWxlbGVtZW50VmFsdWVkSW5nYdgYWFukaGRpZ2VzdElEBWZyYW5kb21Q3fLHe4K4bUMJDFsSYKJ513FlbGVtZW50SWRlbnRpZmllcmpiaXJ0aF9kYXRlbGVsZW1lbnRWYWx1ZdkD7GoxOTkxLTExLTA22BhYVaRoZGlnZXN0SUQGZnJhbmRvbVDIzuaMNAe24KBZ3QQpP5o8cWVsZW1lbnRJZGVudGlmaWVyb2lzc3VpbmdfY291bnRyeWxlbGVtZW50VmFsdWViVVPYGFhbpGhkaWdlc3RJRAdmcmFuZG9tUJV-wSoCvKEqVh_g3844LmdxZWxlbWVudElkZW50aWZpZXJvZG9jdW1lbnRfbnVtYmVybGVsZW1lbnRWYWx1ZWgxMjM0NTY3ONgYWKKkaGRpZ2VzdElECGZyYW5kb21QZHMJeAleAPyXtFA-TiWBD3FlbGVtZW50SWRlbnRpZmllcnJkcml2aW5nX3ByaXZpbGVnZXNsZWxlbWVudFZhbHVlgaN1dmVoaWNsZV9jYXRlZ29yeV9jb2RlYUFqaXNzdWVfZGF0ZdkD7GoyMDIzLTAxLTAxa2V4cGlyeV9kYXRl2QPsajIwNDMtMDEtMDFqaXNzdWVyQXV0aIRDoQEmoRghWQFhMIIBXTCCAQSgAwIBAgIGAYyR2cIZMAoGCCqGSM49BAMCMDYxNDAyBgNVBAMMK0oxRndKUDg3QzYtUU5fV1NJT21KQVFjNm41Q1FfYlpkYUZKNUdEblcxUmswHhcNMjMxMjIyMTQwNjU2WhcNMjQxMDE3MTQwNjU2WjA2MTQwMgYDVQQDDCtKMUZ3SlA4N0M2LVFOX1dTSU9tSkFRYzZuNUNRX2JaZGFGSjVHRG5XMVJrMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAopVeboJpYRycw1YKkkROXfCpKEKl9Y1YPFhOGj4xTg2UOunxTxSIVkT94qFVIuu1hkEoE2NxelZo3-yTFUODDAKBggqhkjOPQQDAgNHADBEAiBnFjScBcvERleLjMCu5NbxJKkNsa_gQhkXTfDmbq-T3gIgVazbsVdQvZgluc9nJYQxWlzXT9i6f-wgUKx0KCYbj3BZArLYGFkCraZndmVyc2lvbmMxLjBvZGlnZXN0QWxnb3JpdGhtZ1NIQS0yNTZsdmFsdWVEaWdlc3RzoXFvcmcuaXNvLjE4MDEzLjUuMagBWCBtyGKRLbYNCtXpIqSixji4RYcXb4Vf7IDoQta4QfRWsAJYIJVrdxQWwcfqiFi75y5R3Saj8YpA8miZxmeoQL_JtB5-A1ggnoXpxMgIsgiIr8HlJ9JzfalESFVLgFxmES9SqSIsIG0EWCD-4Mo98S8qg8SJ8R-PMO7oCHW3wbdCfU8GGS0nG7VahwVYIE9yvCITC8M8p7-m2M4A5MwokXN3oS97uLkhk2AIj6GRBlgg8IaPGI_7Tp2rf2fLhEq0dDzm71FmTZUPc16BdJsCDgkHWCAKnWjJaTmwvgq1Yon8cLwPaPS1-lOEVASldrxYkeKLcwhYIEXVTVGxIhP9R64iPFGseCD_adyfhYZdCw-eOO8ckRjzbWRldmljZUtleUluZm-iaWRldmljZUtleaYBAgJYKzFlNUFZOUV5QjAxWG5VemE2THBKemswMm42WV9BbW1uU2IwRkJlTlZWclUDJiABIVggPSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20iWCDFXx9uLUVKixS6ct64s24uQmKqZjpMqIye6v4afbBHXXFrZXlBdXRob3JpemF0aW9uc6FqbmFtZVNwYWNlc4Fxb3JnLmlzby4xODAxMy41LjFnZG9jVHlwZXVvcmcuaXNvLjE4MDEzLjUuMS5tRExsdmFsaWRpdHlJbmZvo2ZzaWduZWTAdDIwMjQtMDYtMDVUMDM6MzU6MTRaaXZhbGlkRnJvbcB0MjAyNC0wNi0wNVQwMzozNToxNFpqdmFsaWRVbnRpbMB0MjAyNS0wNi0wNVQwMzozNToxNFpYQAs0d0MAErcaA1auodhHxivYcqiSXdQW9KtG9HpZoxo_oEPfkf7_dRQm_Z-ffhZn2qbLTc2Op3x0a1R-gif9Mtg",
"c_nonce": "v-1b-n82kEJGbHROSekGsmR-xEuamCxY_T0tXtQN-dY",
"c_nonce_expires_in": 86205
}
```
#### 4.3.3. Verifiable Credential Format
The CBOR Diagnostic Notation representation of the verifiable credential in
the previous section is as follows.
```
{
"nameSpaces": {
"org.iso.18013.5.1": [
24(<<
{
"digestID": 1,
"random": h'42437f188115018cc2c058ee313a9a47',
"elementIdentifier": "issue_date",
"elementValue": 1004("2024-06-05")
}
>>),
24(<<
{
"digestID": 2,
"random": h'b96446b61e338d15ce24dfe218d4f2d2',
"elementIdentifier": "expiry_date",
"elementValue": 1004("2025-06-05")
}
>>),
24(<<
{
"digestID": 3,
"random": h'ed9c7bc5866d0740f4da72fec7450615',
"elementIdentifier": "family_name",
"elementValue": "Silverstone"
}
>>),
24(<<
{
"digestID": 4,
"random": h'f315e4bf01d3752e8f419130566f5a92',
"elementIdentifier": "given_name",
"elementValue": "Inga"
}
>>),
24(<<
{
"digestID": 5,
"random": h'ddf2c77b82b86d43090c5b1260a279d7',
"elementIdentifier": "birth_date",
"elementValue": 1004("1991-11-06")
}
>>),
24(<<
{
"digestID": 6,
"random": h'c8cee68c3407b6e0a059dd04293f9a3c',
"elementIdentifier": "issuing_country",
"elementValue": "US"
}
>>),
24(<<
{
"digestID": 7,
"random": h'957ec12a02bca12a561fe0dfce382e67',
"elementIdentifier": "document_number",
"elementValue": "12345678"
}
>>),
24(<<
{
"digestID": 8,
"random": h'64730978095e00fc97b4503e4e25810f',
"elementIdentifier": "driving_privileges",
"elementValue": [
{
"vehicle_category_code": "A",
"issue_date": 1004("2023-01-01"),
"expiry_date": 1004("2043-01-01")
}
]
}
>>)
]
},
"issuerAuth": [
h'a10126',
{
33: h'3082015d30820104a0030201020206018c91d9c219300a06082a8648ce3d04030230363134303206035504030c2b4a3146774a50383743362d514e5f5753494f6d4a415163366e3543515f625a6461464a3547446e5731526b301e170d3233313232323134303635365a170d3234313031373134303635365a30363134303206035504030c2b4a3146774a50383743362d514e5f5753494f6d4a415163366e3543515f625a6461464a3547446e5731526b3059301306072a8648ce3d020106082a8648ce3d03010703420004028a5579ba09a58472730d582a49113977c2a4a10a97d63560f1613868f8c5383650eba7c53c52215913f78a85548baed61904a04d8dc5e959a37fb24c550e0c300a06082a8648ce3d040302034700304402206716349c05cbc446578b8cc0aee4d6f124a90db1afe04219174df0e66eaf93de022055acdbb15750bd9825b9cf672584315a5cd74fd8ba7fec2050ac7428261b8f70'
},
h'd8185902ada66776657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473a1716f72672e69736f2e31383031332e352e31a80158206dc862912db60d0ad5e922a4a2c638b84587176f855fec80e842d6b841f456b0025820956b771416c1c7ea8858bbe72e51dd26a3f18a40f26899c667a840bfc9b41e7e0358209e85e9c4c808b20888afc1e527d2737da94448554b805c66112f52a9222c206d045820fee0ca3df12f2a83c489f11f8f30eee80875b7c1b7427d4f06192d271bb55a870558204f72bc22130bc33ca7bfa6d8ce00e4cc28917377a12f7bb8b9219360088fa191065820f0868f188ffb4e9dab7f67cb844ab4743ce6ef51664d950f735e81749b020e090758200a9d68c96939b0be0ab56289fc70bc0f68f4b5fa53845404a576bc5891e28b7308582045d54d51b12213fd47ae223c51ac7820ff69dc9f85865d0b0f9e38ef1c9118f36d6465766963654b6579496e666fa2696465766963654b6579a6010202582b3165354159394579423031586e557a61364c704a7a6b30326e36595f416d6d6e5362304642654e56567255032620012158203d2c50ac3db3974fe65dc02acf59a0a92781a018679b1db2c41129ac163c176d225820c55f1f6e2d454a8b14ba72deb8b36e2e4262aa663a4ca88c9eeafe1a7db0475d716b6579417574686f72697a6174696f6e73a16a6e616d6553706163657381716f72672e69736f2e31383031332e352e3167646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e666fa3667369676e6564c074323032342d30362d30355430333a33353a31345a6976616c696446726f6dc074323032342d30362d30355430333a33353a31345a6a76616c6964556e74696cc074323032352d30362d30355430333a33353a31345a',
h'0b3477430012b71a0356aea1d847c62bd872a8925dd416f4ab46f47a59a31a3fa043df91feff751426fd9f9f7e1667daa6cb4dcd8ea77c746b547e8227fd32d8'
]
}
```
In this example, the verifiable credential represents the `IssuerSigned` structure,
which is defined in the "8.3.2.1.2.2 Device retrieval mdoc response" section of
[ISO/IEC 18013-5:2021][ISO_IEC_18013_5] as below.
```
IssuerSigned = {
? "nameSpaces" : IssuerNameSpaces,
"issuerAuth" : IssuerAuth
}
```
Unexpectedly, the OID4VCI specification does not specify which CBOR structure
the value of the `credential` property represents when the format
is `mso_mdoc`. This is unlikely to be intentional and is simply a
specification flaw.
Due to this ambiguity, in the previous implementation of Authlete, the
`Document` structure was used instead of the `IssuerSigned`
structure. Interestingly, it appears that the implementation of EUDI Wallet
expects neither the `IssuerSigned` structure nor the `Document`
structure. Instead, it seems to expect the `DeviceResponse` structure.
The reason for changing the implementation of Authlete to use the
`IssuerSigned` structure is because the document for POTENTIAL
Interop Event Track 1 explicitly states that the `IssuerSigned`
structure should be used.
And, the `IssuerAuth` structure and some other relevant structures are defined
as below.
```
IssuerAuth = COSE_Sign1 ; The payload is MobileSecurityObjectBytes
MobileSecurityObjectBytes = #6.24(bstr .cbor MobileSecurityObject)
MobileSecurityObject = {
"version" : tstr, ; Version of the MobileSecurityObject
"digestAlgorithm" : tstr, ; Message digest algorithm used
"valueDigests" : ValueDigests, ; Digests of all data elements per namespace
"deviceKeyInfo" : DeviceKeyInfo,
"docType" : tstr, ; docType as used in Documents
"validityInfo" : ValidityInfo
}
DeviceKeyInfo = {
"deviceKey" : DeviceKey
? "keyAuthorizations" : KeyAuthorizations,
? "keyInfo" : KeyInfo
}
DeviceKey = COSE_Key
```
The definitions of `IssuerAuth` and `MobileSecurityObjectBytes`
give the impression that `MobileSecurityObjectBytes`
(which starts with a CBOR tag) is directly used as the payload of `COSE_Sign1`.
However, it is necessary to further convert `MobileSecurityObjectBytes`
into a byte string.
Therefore, the third element in the `"issuerAuth"` array, which starts with
`h'd81859`, represents a byte string containing
`MobileSecurityObjectBytes`.
You can see the following CBOR structure by decoding that byte string.
```
24(<<
{
"version": "1.0",
"digestAlgorithm": "SHA-256",
"valueDigests": {
"org.iso.18013.5.1": {
1: h'6dc862912db60d0ad5e922a4a2c638b84587176f855fec80e842d6b841f456b0',
2: h'956b771416c1c7ea8858bbe72e51dd26a3f18a40f26899c667a840bfc9b41e7e',
3: h'9e85e9c4c808b20888afc1e527d2737da94448554b805c66112f52a9222c206d',
4: h'fee0ca3df12f2a83c489f11f8f30eee80875b7c1b7427d4f06192d271bb55a87',
5: h'4f72bc22130bc33ca7bfa6d8ce00e4cc28917377a12f7bb8b9219360088fa191',
6: h'f0868f188ffb4e9dab7f67cb844ab4743ce6ef51664d950f735e81749b020e09',
7: h'0a9d68c96939b0be0ab56289fc70bc0f68f4b5fa53845404a576bc5891e28b73',
8: h'45d54d51b12213fd47ae223c51ac7820ff69dc9f85865d0b0f9e38ef1c9118f3'
}
},
"deviceKeyInfo": {
"deviceKey": {
1: 2,
2: h'3165354159394579423031586e557a61364c704a7a6b30326e36595f416d6d6e5362304642654e56567255',
3: -7,
-1: 1,
-2: h'3d2c50ac3db3974fe65dc02acf59a0a92781a018679b1db2c41129ac163c176d',
-3: h'c55f1f6e2d454a8b14ba72deb8b36e2e4262aa663a4ca88c9eeafe1a7db0475d'
},
"keyAuthorizations": {
"nameSpaces": [
"org.iso.18013.5.1"
]
}
},
"docType": "org.iso.18013.5.1.mDL",
"validityInfo": {
"signed": 0("2024-06-05T03:35:14Z"),
"validFrom": 0("2024-06-05T03:35:14Z"),
"validUntil": 0("2025-06-05T03:35:14Z")
}
}
>>)
```
The point to note is that the public key embedded in the CWT key proof
appears in the verifiable credential as the value of `deviceKey`. Please
confirm that the value of `COSE_Key` in the CWT key proof is identical
to the value of `deviceKey` in the VC. Both hold the following COSE Key.
```
{
/ kty / 1: 2 / EC2 /,
/ kid / 2: h'3165354159394579423031586e557a61364c704a7a6b30326e36595f416d6d6e5362304642654e56567255',
/ alg / 3: -7 / ES256 /,
/ crv / -1: 1 / P-256 /,
/ x / -2: h'3d2c50ac3db3974fe65dc02acf59a0a92781a018679b1db2c41129ac163c176d',
/ y / -3: h'c55f1f6e2d454a8b14ba72deb8b36e2e4262aa663a4ca88c9eeafe1a7db0475d'
}
```
Refer to [IANA: CBOR Object Signing and Encryption (COSE)][IANA_COSE] for the
meanings of the integer labels and integer values in the COSE Key.
### 4.4. POTENTIAL Interop Event / Track 2 / Light Profile
[POTENTIAL][POTENTIAL] is a European organization dedicated to European Digital Identity.
The organization has been hosting an interoperability event since spring 2024. The event
is divided into six tracks. Tracks 1 and 2 are designated for testing the inteoperability
of credential issuers. In Track 1, mdoc is used as the format for verifiable credentials,
while SD-JWT VC is used in Track 2.
Track 2 defines two profiles. One is called the "light" profile. The other is called the
"full" profile. This section explains the steps for the light profile.
#### 4.4.1. Settings
##### 4.4.1.1. Authorization Server Settings
| Parameter | Value |
| :- | :- |
| Issuer Identifier | `https://trial.authlete.net` |
| Authorization Endpoint | `https://trial.authlete.net/api/authorization` |
| Token Endpoint | `https://trial.authlete.net/api/token` |
| Discovery Endpoint | `[https://trial.authlete.net/.well-known/openid-configuration](https://trial.authlete.net/.well-known/openid-configuration)` |
| Entity Configuration | `[https://trial.authlete.net/.well-known/openid-federation](https://trial.authlete.net/.well-known/openid-federation)` |
The source code of the **authorization server** is available at
[https://github.com/authlete/java-oauth-server](https://github.com/authlete/java-oauth-server). Note that this implementation
is a sample and is not intended for commercial use.
##### 4.4.1.2. Credential Issuer Settings
| Parameter | Value |
| :- | :- |
| Issuer Identifier | `https://trial.authlete.net` |
| Credential Endpoint | `https://trial.authlete.net/api/credential` |
| Metadata Endpoint | `[https://trial.authlete.net/.well-known/openid-credential-issuer](https://trial.authlete.net/.well-known/openid-credential-issuer)` |
| Entity Configuration | `[https://trial.authlete.net/.well-known/openid-federation](https://trial.authlete.net/.well-known/openid-federation)` |
The source code of the **credential issuer** is the same as that of the
authorization server.
##### 4.4.1.3. Client Settings
| Parameter | Value |
| :- | :- |
| Client ID | `track2_light` |
| Client Type | public (= [client authentication][CLIENT_AUTHENTICATION] is not required) |
| Redirect URIs |
|
If you need to register additional redirect URIs to this client, or if you need
an independent client dedicated to your use, please [contact us][CONTACT_US].
To Authlete members: New clients should be created under
"Trial" (organization=283415) /
"NextDev" (server=2704222) /
"Authlete Trial" (service=986126671).
#### 4.4.2. Demo Steps
##### 4.4.2.1. Step 1 : Code Verifier and Code Challenge
Generate a **code verifier** and compute the corresponding **code challenge**
(cf. [RFC 7636][RFC_7636]) by using the `pkce` command
```shell theme={null}
git clone git@github.com:authlete/oid4vci-demo.git
cd oid4vci-demo
./pkce
```
The `pkce` command will print a generated code verifier and the computed code
challenge like below.
```shell theme={null}
CODE_VERIFIER=gzRNlV7DLS_HyKMQKQMrzgYQ8aY3H2rVJ3iIlYK0cjE
CODE_CHALLENGE=j_4gpG9Kr3M7ilMO-MRoSROP-W3h2EZem0KSEU-RAhM
```
The computed code challenge is to be included in the **authorization request**
in the next step, and the generated code verifier is to be included in the
**token request** that will be made after the authorization request.
##### 4.4.2.2. Step 2 : Authorization Request
Make an **authorization request** using the **authorization code flow**
(cf. [RFC 6749, 4.1][RFC_6749_4_1]) by inputting the following URL in the
address bar of your web browser. Don't forget to replace `${CODE_CHALLENGE}`
in the URL with the actual value of the code challenge you have created in
the previous step.
```
https://trial.authlete.net/api/authorization?client_id=track2_light&response_type=code&scope=potential.track2.light.profile&redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection&code_challenge=${CODE_CHALLENGE}&code_challenge_method=S256&prompt=login
```
Accessing the URL will show you an **authorization page**. The page contains
a login form. Input `inga` and `inga` into the Login ID field and the Password
field in the login form, and press the "Authorize" button.
You will be redirected to the **redirection endpoint** (cf.
[RFC 6749, 3.1.2][RFC_6749_3_1_2]).
This redirection endpoint displays key-value pairs that it has received.
The value of the `code` parameter displayed there is the issued
**authorization code**. In this example, the value of the authorization code is
`gR43MQf2olvhMt6KekVDkUOdQPrVYgBiKXMwu_UFnB8`.
The authorization code is to be used in the token request in the next section.
Note that the authorization code will expire in 10 minutes, so you have to make
a token request promptly.
##### 4.4.2.3. Step 3 : Token Request
Make a **token request** using the authorization code flow. Don't forget to
replace `${AUTHORIZATION_CODE}` and `${CODE_VERIFIER}` in the `curl` command
below with the actual values you have obtained in the previous steps.
```shell theme={null}
curl -s https://trial.authlete.net/api/token \
-d client_id=track2_light \
-d grant_type=authorization_code \
-d code=${AUTHORIZATION_CODE} \
-d redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection \
-d code_verifier=${CODE_VERIFIER}
```
If the token request is valid, the token endpoint returns JSON like below.
```json theme={null}
{
"access_token": "FvF1eeUbbWwteF5v0nfsO6vb0jlWifgsP5U666qKSGs",
"token_type": "Bearer",
"expires_in": 86400,
"scope": "potential.track2.light.profile",
"refresh_token": "WUCNjsuU02XhOMWN5qHRkKKbZDX5g2HjJx52GWC_1Gw",
"c_nonce": "rvw3Mo_ZHyEgYOoWQ9GEotemwFbhRvVVdI6e5Z0lhEs",
"c_nonce_expires_in": 86400
}
```
The value of the `access_token` property in the JSON is the issued
**access token**. It needs to be presented when you make a **credential request**.
The value of the `c_nonce` property is a nonce that must be included in a
**key proof**.
##### 4.4.2.4. Step 4 : JWT Key Proof
Generate a **JWT Key Proof** using the holder key `holder.jwk` and the `generate-key-proof` script.
The JWK file and the script are contained in the [oid4vci-demo repository][AUTHLETE_OID4VCI_DEMO].
Don't forget to replace `$C_NONCE` in the following command line with the actual value of the
`c_nonce` property in the token response you received in the previous step.
```shell theme={null}
./generate-key-proof \
-i https://trial.authlete.net \
-k holder.jwk \
-c track2_light \
-n $C_NONCE
```
The `generate-key-proof` script will generate a JWT Key Proof like below.
```
eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiUFN4UXJEMnpsMF9tWGNBcXoxbWdxU2VCb0Jobm14Mnl4QkVwckJZOEYyMCIsInkiOiJ4VjhmYmkxRlNvc1V1bkxldUxOdUxrSmlxbVk2VEtpTW51ci1HbjJ3UjEwIn19.eyJpc3MiOiJ0cmFjazJfbGlnaHQiLCJhdWQiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsImlhdCI6MTcxNzYxNzk1Nywibm9uY2UiOiJydnczTW9fWkh5RWdZT29XUTlHRW90ZW13RmJoUnZWVmRJNmU1WjBsaEVzIn0.E-9pdaSW2oaFqI2V0N1aRiSRI3LzOxwQFNR5tewaLXxP8R7ZHrU9-M7TLuqP5OmWRecdFrJ9yQAM83kbc4f5-A
```
Decoding the header and the payload of the JWT Key Proof by base64url will show the following JSONs.
```json theme={null}
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
"y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
}
}
```
```json theme={null}
{
"iss": "track2_light",
"aud": "https://trial.authlete.net",
"iat": 1717617957,
"nonce": "rvw3Mo_ZHyEgYOoWQ9GEotemwFbhRvVVdI6e5Z0lhEs"
}
```
The result of executing the `generate-key-proof` script can be directly set to
the shell variable `JWT_KEY_PROOF` by doing the following.
```shell theme={null}
JWT_KEY_PROOF=`./generate-key-proof -i https://trial.authlete.net -k holder.jwk -c track2_light -n $C_NONCE`
```
##### 4.4.2.5. Step 5 : Credential Request
Make a **credential request** with the access token and the JWT key proof.
Don't forget to replace `${ACCESS_TOKEN}` and `${JWT_KEY_PROOF}` in the
command line with the actual values.
```shell theme={null}
curl -s https://trial.authlete.net/api/credential \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"format": "vc+sd-jwt",
"vct": "urn:eu.europa.ec.eudi:pid:1",
"proof": {
"proof_type": "jwt",
"jwt":"'${JWT_KEY_PROOF}'"
}
}'
```
If the credential request is valid, the credential endpoint returns JSON
like below. The value of the `credential` property in the JSON is the
issued **verifiable credential**.
```json theme={null}
{
"credential": "eyJraWQiOiJKMUZ3SlA4N0M2LVFOX1dTSU9tSkFRYzZuNUNRX2JaZGFGSjVHRG5XMVJrIiwidHlwIjoidmMrc2Qtand0IiwiYWxnIjoiRVMyNTYifQ.eyJwbGFjZV9vZl9iaXJ0aCI6eyJfc2QiOlsiRUFiQW1SN1owN1dpMVczczZLSVROMFdhamFzUTlObEJmYm9ocjgtU3hfcyIsImJCZ19NSWlKNnJhTU9jZ0dEd2ZtSUFMSEpFU2NJTEMyRjhCTmpOVW1MdG8iXX0sIl9zZCI6WyIxQThmRmJKMVlhM3RmQ3ZEUXpLNHNMSUhMRFJERWJEYk1oUEVmcF9TRzVVIiwiMjhqTDd3SFV0cTBuMXdaQUpmY1doTDVOWkt5QmtMMFRVM3NpaVNPbGV6WSIsIjJjcl9FNTM2cU1RMDB3Z1BTMURfbHZyZUFhc21tc3B4YU52T1Ffd3ZtTEEiLCI1dXE2ei13QWhaZUJEMkt3VjFONU5lcURqSWpXVHpqQi0xVU91anZOUllZIiwiNndhVlRLelZpMWZoMUxQc0ZfbURKdWJrcmFhZGpnUEtYTUdJN1B6blYwZyIsIjc4cmN0UnNtRWFuV1oybnZPVFdyNVlvWEZ5eW50ZklxMU1OOGp1bXJCOG8iLCI4OHZ3c1VVbHI1RmFiUGhPNVhhY3dIdG5nTXVTdjREVmFfa0E5OUUxa3lzIiwiOUd6YU4xZWc1NlczVEN4aGxwOWZLU093SFNfbmhEeEYxVnJTbEg3Z3ExRSIsIkJ3WkZRVENqVk0xRW5Ic1VIeXQzZ3JjdGhWMlJIVnFkTGJUdjMtLUVrOGciLCJFZDdVbFhYS0sydXdsblJNdHpSZHRnOFQwbVduUXhBOVVOblBldEt1T3VjIiwiRk56eFdmSGRhN0hXcEdkQWpJM1R3WnhXeE5YR0xwNHdqcEd1X0NqR0FYYyIsIkduSUZGOFg5TlpCbVRKQ2E5OUx4UFFST0c1YzZYUWsyemJ1NG9jbG45SWsiLCJMc2ZscHFUTFg4T0pYYk53eTdxcDU3MHRKY0I5T0dQeE9qTUxEU21BUTJJIiwiZHNKVjBHZ2JfZlVjeGlPalRvVUlDaEpGR1F5aFJMZHp2WWltQmNlSTN6YyIsImgydlN3UVN0SV9LcGtxU1djN011TG44VlB5a0VjelJFWjNEZ1hUd3QzM1EiLCJqQ0l4Z0lkY2JOUnVDbUJDeF9DQVQwZ2FvZURCU29VQnl4XzdfRFUxQmRvIiwia3RlSGlZdlhfbWphR0s0YkU4YjYyRktnUnZ2bFRCTVNxLXhpVlJQZTB6USIsInB5dXJrNU9zeGtYS2FoZ2lkbXdrTU56NTBUaWtGLV8zRkY0WG9SYlg0bDQiLCJ2VHc5VlpSOFpNQW0zWkhxZGJxNVBxSVBBZ0M2bVlyWGF4YmFkekFQOGpZIl0sImFkZHJlc3MiOnsiX3NkIjpbIjBXYUJrS3U4Q1lPV1NldjFDTThsYVNwS3VCbTZSTW1odktSR3kyWWRjaWMiLCJNbTdWX1dXRThVSk1TSHV0aV8xd0Nhb0puLUxKcTdaWG5FSDY1dXUxRTlrIiwiUFZKYmFmZjVzWmJCYm1HY2dkRWdOOXZsTWRndU9aal8yMjVYUk0xTHR3RSIsImlRT1pBN2RXQWVUaTg1RE1RQ1N0UmkySnVzSHB6YVJEN0FVRXZPQTJWN1UiLCJtMGVhYlEyTDViTEVPY3UwYjRRTkZQeWNvTHluUDAzYkxoRm9zTVdLeXRRIiwibXV0TU9aLVgxQlRoenM2OXdxQ3E3RFRieHFKNjRnWG9Yb2xHbnpyOUlmZyIsIm5LaXRPS3dBNW9oZ0QtNnVPNERIMW8wQnRoN0tWSndJaXlVSUhfcmJLLUUiLCJyQk15UlR6TFZkQV9uY291bDNLenN1cVluYWxLX0lnYWFMV0Z3aS14MjdzIiwidTgyUXhEemxyRVlsQWlSNUV1bGJ3ZWJSaFNnYWVaZ0o5dkEyRmd4M3ZvVSJdfSwidmN0IjoidXJuOmV1LmV1cm9wYS5lYy5ldWRpOnBpZDoxIiwiX3NkX2FsZyI6InNoYS0yNTYiLCJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsImtpZCI6IjRNOWtJckI5V1l6dDFHUWdMMTJsemRCWnNHeWVWM2xnUEtvdjI4b1Q1TDQiLCJ4IjoiUFN4UXJEMnpsMF9tWGNBcXoxbWdxU2VCb0Jobm14Mnl4QkVwckJZOEYyMCIsInkiOiJ4VjhmYmkxRlNvc1V1bkxldUxOdUxrSmlxbVk2VEtpTW51ci1HbjJ3UjEwIn19LCJpYXQiOjE3MTc2MTg2NjEsImFnZV9lcXVhbF9vcl9vdmVyIjp7Il9zZCI6WyIzZGo2NXpOMTMzS2haN1QwV1RidHczSVhialh4VHRIZUtoMlRWOWJVMzRjIiwiRFJUNi0tU2YtNDU4bGNaOEdobGd5TU5TZUVyNHRya3R1ZVhkNXNzNGtPRSJdfX0.EaUDXo2hWBNOqsdYBDAWpuHDYfGm9xNepZJn7rafTGAb3kHVxNJoSZTvOvLhk_sy30GpK2kLFWvVlR1nzxSMVg~WyJyeHR0ck5MbXUwNWc5cjVZVmc3bUtnIiwic3ViIiwiMTAwNCJd~WyJaR2lyT3Y3dUdLVXZOWUpVcFZ0QXJBIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd~WyIwcGt4eEVwVmIwdW5mQUQ1R1VsZXVnIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ~WyI2RWtWSGN2U0lIZ215akE0VW5fZlNRIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd~WyJhUzhNbVlFWHVWR1A5ODYwQV9lZDZRIiwiMTgiLHRydWVd~WyJMX1JRdm00Mll0XzdESHVjTi1IUHRRIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd~WyJmQmpkVWRTV0NBX0o4NXF2anlmcXVBIiwiZm9ybWF0dGVkIiwiMTE0IE9sZCBTdGF0ZSBId3kgMTI3LCBTaG9zaG9uZSwgQ0EgOTIzODQsIFVTQSJd~WyJIOGVpZ0UxckQ2d3o3RDA3b0pnNjl3Iiwic3RyZWV0X2FkZHJlc3MiLCIxMTQgT2xkIFN0YXRlIEh3eSAxMjciXQ~WyIxdngwVjJmTUVrTTBXNnVCYWg5VjRBIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd~WyJHVnZfZWV0VElPLW93NTgzRTRMSDBBIiwicG9zdGFsX2NvZGUiLCJDQSA5MjM4NCJd~WyJrYjJYUkhsMTQxazFyZG1xeGdDM2xRIiwiY291bnRyeSIsIlVTQSJd~WyJLU0VlbEV0dU5qbzZHQWR6NTFqSG93IiwiaXNzdWluZ19hdXRob3JpdHkiLCJVUyJd~WyJ0ZzBZQ3RNeHNPSW54aW9pUExDNV93IiwiaXNzdWluZ19jb3VudHJ5IiwiVVMiXQ~",
"c_nonce": "rvw3Mo_ZHyEgYOoWQ9GEotemwFbhRvVVdI6e5Z0lhEs",
"c_nonce_expires_in": 83596
}
```
#### 4.4.3. Verifiable Credential Format
The value of the `credential` parameter in the response is the issued SD-JWT VC.
If the SD-JWT VC is set to the shell variable `SD_JWT`, the content of the SD-JWT VC
can be decoded by invoking the `decode-sd-jwt` script as follows.
```shell theme={null}
./decode-sd-jwt $SD_JWT
```
The result will look like below.
```json theme={null}
{
"kid": "J1FwJP87C6-QN_WSIOmJAQc6n5CQ_bZdaFJ5GDnW1Rk",
"typ": "vc+sd-jwt",
"alg": "ES256"
}
{
"place_of_birth": {
"_sd": [
"EAbAmR7Z07Wi1W3s6KITN0WajasQ9NlBfbohr8-Sx_s",
"bBg_MIiJ6raMOcgGDwfmIALHJEScILC2F8BNjNUmLto"
]
},
"_sd": [
"1A8fFbJ1Ya3tfCvDQzK4sLIHLDRDEbDbMhPEfp_SG5U",
"28jL7wHUtq0n1wZAJfcWhL5NZKyBkL0TU3siiSOlezY",
"2cr_E536qMQ00wgPS1D_lvreAasmmspxaNvOQ_wvmLA",
"5uq6z-wAhZeBD2KwV1N5NeqDjIjWTzjB-1UOujvNRYY",
"6waVTKzVi1fh1LPsF_mDJubkraadjgPKXMGI7PznV0g",
"78rctRsmEanWZ2nvOTWr5YoXFyyntfIq1MN8jumrB8o",
"88vwsUUlr5FabPhO5XacwHtngMuSv4DVa_kA99E1kys",
"9GzaN1eg56W3TCxhlp9fKSOwHS_nhDxF1VrSlH7gq1E",
"BwZFQTCjVM1EnHsUHyt3grcthV2RHVqdLbTv3--Ek8g",
"Ed7UlXXKK2uwlnRMtzRdtg8T0mWnQxA9UNnPetKuOuc",
"FNzxWfHda7HWpGdAjI3TwZxWxNXGLp4wjpGu_CjGAXc",
"GnIFF8X9NZBmTJCa99LxPQROG5c6XQk2zbu4ocln9Ik",
"LsflpqTLX8OJXbNwy7qp570tJcB9OGPxOjMLDSmAQ2I",
"dsJV0Ggb_fUcxiOjToUIChJFGQyhRLdzvYimBceI3zc",
"h2vSwQStI_KpkqSWc7MuLn8VPykEczREZ3DgXTwt33Q",
"jCIxgIdcbNRuCmBCx_CAT0gaoeDBSoUByx_7_DU1Bdo",
"kteHiYvX_mjaGK4bE8b62FKgRvvlTBMSq-xiVRPe0zQ",
"pyurk5OsxkXKahgidmwkMNz50TikF-_3FF4XoRbX4l4",
"vTw9VZR8ZMAm3ZHqdbq5PqIPAgC6mYrXaxbadzAP8jY"
],
"address": {
"_sd": [
"0WaBkKu8CYOWSev1CM8laSpKuBm6RMmhvKRGy2Ydcic",
"Mm7V_WWE8UJMSHuti_1wCaoJn-LJq7ZXnEH65uu1E9k",
"PVJbaff5sZbBbmGcgdEgN9vlMdguOZj_225XRM1LtwE",
"iQOZA7dWAeTi85DMQCStRi2JusHpzaRD7AUEvOA2V7U",
"m0eabQ2L5bLEOcu0b4QNFPycoLynP03bLhFosMWKytQ",
"mutMOZ-X1BThzs69wqCq7DTbxqJ64gXoXolGnzr9Ifg",
"nKitOKwA5ohgD-6uO4DH1o0Bth7KVJwIiyUIH_rbK-E",
"rBMyRTzLVdA_ncoul3KzsuqYnalK_IgaaLWFwi-x27s",
"u82QxDzlrEYlAiR5EulbwebRhSgaeZgJ9vA2Fgx3voU"
]
},
"vct": "urn:eu.europa.ec.eudi:pid:1",
"_sd_alg": "sha-256",
"iss": "https://trial.authlete.net",
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"kid": "4M9kIrB9WYzt1GQgL12lzdBZsGyeV3lgPKov28oT5L4",
"x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
"y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
}
},
"iat": 1717618661,
"age_equal_or_over": {
"_sd": [
"3dj65zN133KhZ7T0WTbtw3IXbjXxTtHeKh2TV9bU34c",
"DRT6--Sf-458lcZ8GhlgyMNSeEr4trktueXd5ss4kOE"
]
}
}
{
"digest": "GnIFF8X9NZBmTJCa99LxPQROG5c6XQk2zbu4ocln9Ik",
"WyJyeHR0ck5MbXUwNWc5cjVZVmc3bUtnIiwic3ViIiwiMTAwNCJd": [
"rxttrNLmu05g9r5YVg7mKg",
"sub",
"1004"
]
}
{
"digest": "jCIxgIdcbNRuCmBCx_CAT0gaoeDBSoUByx_7_DU1Bdo",
"WyJaR2lyT3Y3dUdLVXZOWUpVcFZ0QXJBIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd": [
"ZGirOv7uGKUvNYJUpVtArA",
"family_name",
"Silverstone"
]
}
{
"digest": "h2vSwQStI_KpkqSWc7MuLn8VPykEczREZ3DgXTwt33Q",
"WyIwcGt4eEVwVmIwdW5mQUQ1R1VsZXVnIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ": [
"0pkxxEpVb0unfAD5GUleug",
"given_name",
"Inga"
]
}
{
"digest": "dsJV0Ggb_fUcxiOjToUIChJFGQyhRLdzvYimBceI3zc",
"WyI2RWtWSGN2U0lIZ215akE0VW5fZlNRIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd": [
"6EkVHcvSIHgmyjA4Un_fSQ",
"birthdate",
"1991-11-06"
]
}
{
"digest": "DRT6--Sf-458lcZ8GhlgyMNSeEr4trktueXd5ss4kOE",
"WyJhUzhNbVlFWHVWR1A5ODYwQV9lZDZRIiwiMTgiLHRydWVd": [
"aS8MmYEXuVGP9860A_ed6Q",
"18",
true
]
}
{
"digest": "EAbAmR7Z07Wi1W3s6KITN0WajasQ9NlBfbohr8-Sx_s",
"WyJMX1JRdm00Mll0XzdESHVjTi1IUHRRIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd": [
"L_RQvm42Yt_7DHucN-HPtQ",
"locality",
"Shoshone"
]
}
{
"digest": "rBMyRTzLVdA_ncoul3KzsuqYnalK_IgaaLWFwi-x27s",
"WyJmQmpkVWRTV0NBX0o4NXF2anlmcXVBIiwiZm9ybWF0dGVkIiwiMTE0IE9sZCBTdGF0ZSBId3kgMTI3LCBTaG9zaG9uZSwgQ0EgOTIzODQsIFVTQSJd": [
"fBjdUdSWCA_J85qvjyfquA",
"formatted",
"114 Old State Hwy 127, Shoshone, CA 92384, USA"
]
}
{
"digest": "iQOZA7dWAeTi85DMQCStRi2JusHpzaRD7AUEvOA2V7U",
"WyJIOGVpZ0UxckQ2d3o3RDA3b0pnNjl3Iiwic3RyZWV0X2FkZHJlc3MiLCIxMTQgT2xkIFN0YXRlIEh3eSAxMjciXQ": [
"H8eigE1rD6wz7D07oJg69w",
"street_address",
"114 Old State Hwy 127"
]
}
{
"digest": "Mm7V_WWE8UJMSHuti_1wCaoJn-LJq7ZXnEH65uu1E9k",
"WyIxdngwVjJmTUVrTTBXNnVCYWg5VjRBIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd": [
"1vx0V2fMEkM0W6uBah9V4A",
"locality",
"Shoshone"
]
}
{
"digest": "PVJbaff5sZbBbmGcgdEgN9vlMdguOZj_225XRM1LtwE",
"WyJHVnZfZWV0VElPLW93NTgzRTRMSDBBIiwicG9zdGFsX2NvZGUiLCJDQSA5MjM4NCJd": [
"GVv_eetTIO-ow583E4LH0A",
"postal_code",
"CA 92384"
]
}
{
"digest": "m0eabQ2L5bLEOcu0b4QNFPycoLynP03bLhFosMWKytQ",
"WyJrYjJYUkhsMTQxazFyZG1xeGdDM2xRIiwiY291bnRyeSIsIlVTQSJd": [
"kb2XRHl141k1rdmqxgC3lQ",
"country",
"USA"
]
}
{
"digest": "88vwsUUlr5FabPhO5XacwHtngMuSv4DVa_kA99E1kys",
"WyJLU0VlbEV0dU5qbzZHQWR6NTFqSG93IiwiaXNzdWluZ19hdXRob3JpdHkiLCJVUyJd": [
"KSEelEtuNjo6GAdz51jHow",
"issuing_authority",
"US"
]
}
{
"digest": "kteHiYvX_mjaGK4bE8b62FKgRvvlTBMSq-xiVRPe0zQ",
"WyJ0ZzBZQ3RNeHNPSW54aW9pUExDNV93IiwiaXNzdWluZ19jb3VudHJ5IiwiVVMiXQ": [
"tg0YCtMxsOInxioiPLC5_w",
"issuing_country",
"US"
]
}
```
### 4.5. POTENTIAL Interop Event / Track 2 / Full Profile
[POTENTIAL][POTENTIAL] is a European organization dedicated to European Digital Identity.
The organization has been hosting an interoperability event since spring 2024. The event
is divided into six tracks. Tracks 1 and 2 are designated for testing the inteoperability
of credential issuers. In Track 1, mdoc is used as the format for verifiable credentials,
while SD-JWT VC is used in Track 2.
Track 2 defines two profiles. One is called the "light" profile. The other is called the
"full" profile. This section explains the steps for the full profile.
#### 4.5.1. Settings
##### 4.5.1.1. Authorization Server Settings
| Parameter | Value |
| :- | :- |
| Issuer Identifier | `https://trial.authlete.net` |
| Authorization Endpoint | `https://trial.authlete.net/api/authorization` |
| Token Endpoint | `https://trial.authlete.net/api/token` |
| PAR Endpoint | `https://trial.authlete.net/api/par` |
| Discovery Endpoint | `[https://trial.authlete.net/.well-known/openid-configuration](https://trial.authlete.net/.well-known/openid-configuration)` |
| Entity Configuration | `[https://trial.authlete.net/.well-known/openid-federation](https://trial.authlete.net/.well-known/openid-federation)` |
The source code of the **authorization server** is available at
[https://github.com/authlete/java-oauth-server](https://github.com/authlete/java-oauth-server). Note that this implementation
is a sample and is not intended for commercial use.
##### 4.5.1.2. Credential Issuer Settings
| Parameter | Value |
| :- | :- |
| Issuer Identifier | `https://trial.authlete.net` |
| Credential Endpoint | `https://trial.authlete.net/api/credential` |
| Metadata Endpoint | `[https://trial.authlete.net/.well-known/openid-credential-issuer](https://trial.authlete.net/.well-known/openid-credential-issuer)` |
| Entity Configuration | `[https://trial.authlete.net/.well-known/openid-federation](https://trial.authlete.net/.well-known/openid-federation)` |
The source code of the **credential issuer** is the same as that of the
authorization server.
##### 4.5.1.3. Client Settings
| Parameter | Value |
| :- | :- |
| Client ID | `track2_full` |
| Client Type | confidential |
| Client Authentication Method | `attest_jwt_client_auth` |
| Redirect URIs |
|
If you need to register additional redirect URIs to this client, or if you need
an independent client dedicated to your use, please [contact us][CONTACT_US].
To Authlete members: New clients should be created under
"Trial" (organization=283415) /
"NextDev" (server=2704222) /
"Authlete Trial" (service=986126671).
#### 4.5.2. Demo Steps
##### 4.5.2.1. Step 1 : Code Verifier and Code Challenge
Generate a **code verifier** and compute the corresponding **code challenge**
(cf. [RFC 7636][RFC_7636]) by using the `pkce` command
```shell theme={null}
git clone git@github.com:authlete/oid4vci-demo.git
cd oid4vci-demo
./pkce
```
The `pkce` command will print a generated code verifier and the computed code
challenge like below.
```shell theme={null}
CODE_VERIFIER=gzRNlV7DLS_HyKMQKQMrzgYQ8aY3H2rVJ3iIlYK0cjE
CODE_CHALLENGE=j_4gpG9Kr3M7ilMO-MRoSROP-W3h2EZem0KSEU-RAhM
```
The computed code challenge is to be included in the **PAR request**
([RFC 9126][RFC_9126]), and the generated code verifier is to be included in
the **token request**, which will be made later.
##### 4.5.2.2. Step 2 : Client Attestation and Client Attestation PoP
In POTENTIAL's Track 2 Full Profile, a new client authentication method called
"**[OAuth 2.0 Attestation-Based Client Authentication][ATTESTATION_BASED_CLIENT_AUTH]**"
is used. For this method, two JWTs need to be prepared. The JWTs are called
"**Client Attestation**" and "**Client Attestation PoP**", respectively.
The [oid4vci-demo][AUTHLETE_OID4VCI_DEMO] repository includes two scripts,
`generate-client-attestation` and
`generate-client-attestation-pop`, for generating
the JWTs. Their usage is as follows:
```
Usage: generate-client-attestation [options]
--attester-id=ATTESTER_ID The identifier of the client attestation issuer.
--attester-key=FILE The file containing the private key of the client attestation issuer in the JWK format.
--client-id=CLIENT_ID The identifier of the client application.
--client-key=FILE The file containing the public key of the client application in the JWK format.
--duration=DURATION The duration of the client attestation in seconds. The default value is 86400.
```
```
Usage: generate-client-attestation-pop [options]
--as-id=AS_ID The identifier of the authorization server.
--client-id=CLIENT_ID The identifier of the client application.
--client-key=FILE The file containing the private key of the client application in the JWK format.
--duration=DURATION The duration of the client attestation PoP in seconds. The default value is 86400.
```
To generate a client attestation, a private key of the "**client attestation issuer**"
(hereinafter referred to as "**attester**") and a public key of the client application
are needed. The attester's key is used for signing the client attestation. The client's
key is embedded in the client attestation.
The [oid4vci-demo][AUTHLETE_OID4VCI_DEMO] repository includes an attester's private
key (`attester.jwk`) and a client's private key (`client.jwk`) for demo. With these
keys, a client attestation can be generated as follows:
```shell theme={null}
./generate-client-attestation \
--attester-id=https://attester.example.com \
--attester-key=attester.jwk \
--client-id=track2_full \
--client-key=client.jwk
```
The `generate-client-attestation` script uses only the
public part of the specified client key, even if a private key
is given. Therefore, the file containing the private key of the client
can be passed to the `--client-key` option directly.
The above example uses `https://attester.example.com` as the value
of the attester's identifier. Any value works because the current implementation
of Authlete does not care about the value.
Please note that the current draft (draft 03 as of this writing) of the "OAuth 2.0 Attestation-Based Client
Authentication" specification does not specify how to obtain the public key for
verifying the signature of a client attestation. It seems that the specification
expects ecosystems to determine their own rules for that part, respectively.
Therefore, the current implementation of Authlete does NOT verify the
signature of a client attestation, and so Authlete does not care about the
value of the attester's identifier. Verification of the signature of a client
attestation must be done by the implementation of your authorization server.
In the future, Authlete may provide mechanisms to aid in the verification step.
The `generate-client-attestation` script generates a
client attestation in the JWT format like below:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6InpYOXhiSnFCemlNV0s1dm1qWEhnWlRtaVkxNnhmblp4elNFYTBHcVk1X1EifQ.eyJpc3MiOiJodHRwczovL2F0dGVzdGVyLmV4YW1wbGUuY29tIiwic3ViIjoidHJhY2syX2Z1bGwiLCJpYXQiOjE3MTk0NzMzNTIsImV4cCI6MTcxOTU1OTc1MiwiY25mIjp7Imp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjFBbVZyNEdvSGRQZ2s0OExXZFMzVDltNm0xbVA0VlRjajl1c29TQm5DUWsiLCJ5IjoidGUtV0l1VUlxMnc4dFhtWHlkbEVYNHBlOWxOZS1QQmNvekE4bjd5OFhURSJ9fX0._jNM-9vEL14AOJrfk03ipIi7YECL-SGXUvqtdSiaYKVGhq7AK15QiqzzIwBj92Lij7Y4DvrtGix-0Pf4hwhPmQ
```
The following is the base64url-decoded header and payload of the JWT.
You can see that the attester's identifier is used as the value of
the `iss` claim and the public key of the client is embedded
as the value of the `cnf.jwk` property.
```json theme={null}
{
"typ": "JWT",
"alg": "ES256",
"kid": "zX9xbJqBziMWK5vmjXHgZTmiY16xfnZxzSEa0GqY5_Q"
}
```
```json theme={null}
{
"iss": "https://attester.example.com",
"sub": "track2_full",
"iat": 1719473352,
"exp": 1719559752,
"cnf": {
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "1AmVr4GoHdPgk48LWdS3T9m6m1mP4VTcj9usoSBnCQk",
"y": "te-WIuUIq2w8tXmXydlEX4pe9lNe-PBcozA8n7y8XTE"
}
}
}
```
To generate a client attestation PoP, a private key of the client is needed.
The client's key is used for signing the client attestation PoP.
With the client's private key (`client.jwk`) in the
[oid4vci-demo][AUTHLETE_OID4VCI_DEMO] repository, a client attestation PoP
can be generated as follows:
```shell theme={null}
./generate-client-attestation-pop \
--as-id=https://trial.authlete.net \
--client-id=track2_full \
--client-key=client.jwk
```
The `generate-client-attestation-pop` script
generates a client attestation PoP in the JWT format like below:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6Ijd5TmhJSFZlYVBGSUJfMGstWXBpRkFUb21DTHB4SXYtZTJBQUZtQ1JCTEUifQ.eyJpc3MiOiJ0cmFjazJfZnVsbCIsImlhdCI6MTcxOTQ3MzUxOSwiZXhwIjoxNzE5NTU5OTE5LCJqdGkiOiJvRmJGNXhhdnZLMTBRekJhIiwiYXVkIjoiaHR0cHM6Ly90cmlhbC5hdXRobGV0ZS5uZXQifQ.TXeCcijI8tICBH1bhOcNYTof9D0vDKAlu9Y0rl21EnPkUOCcsohiI8K7B1MlP1z82oC0LlYywXpcZ_gNnAC8BQ
```
The following is the base64url-decoded header and payload of the JWT.
You can see that the client's identifier is used as the value of the
`iss` claim and the authorization server's identifier is specified
as the value of the `aud` claim.
```json theme={null}
{
"typ": "JWT",
"alg": "ES256",
"kid": "7yNhIHVeaPFIB_0k-YpiFATomCLpxIv-e2AAFmCRBLE"
}
```
```json theme={null}
{
"iss": "track2_full",
"iat": 1719473519,
"exp": 1719559919,
"jti": "oFbF5xavvK10QzBa",
"aud": "https://trial.authlete.net"
}
```
##### 4.5.2.3. Step 3 : DPoP Proof JWT for PAR Request
To generate a DPoP Proof JWT, the `generate-dpop-proof`
script in the [oid4vci-demo][AUTHLETE_OID4VCI_DEMO] repository can be used.
With the key for DPoP demo (`dpop.jwk`) in the oid4vci-demo repository,
the script can be invoked like below.
```shell theme={null}
./generate-dpop-proof -k dpop.jwk -m POST \
-u https://trial.authlete.net/api/par
```
The `-u` option (the short version of the `--htu` option)
of the `generate-dpop-proof` script specifies the value of
the `htu` claim in the DPoP Proof JWT.
Because the DPoP Proof JWT being created here is for the PAR request, the URL of
the PAR endpoint (`https://trial.authlete.net/api/par`)
is specified by the `-u` option.
The `generate-dpop-proof` script generates a DPoP Proof JWT
in the JWT format like below:
```
eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiaEdmcXpHWGdhbzFRZ1ZJVFk2a2lIWU9LYmFMWEJ4VHFQSmE0RU9pbXhoSSIsInkiOiJFMUtpQV9mQTJ4OElycnlzb0dkbkJUTUI1LW8zRUpUX01nUUFfSG1HdTlNIn19.eyJqdGkiOiJxMWZJS21ad2FMNHlna1JQIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vdHJpYWwuYXV0aGxldGUubmV0L2FwaS9wYXIiLCJpYXQiOjE3MTk0NzQ2MzZ9.K8Mp44eK586UNCE-63bt5-m0v8B8KV840lDBe_5h2wLBcNWceS5x2fbFh9Koe7V7Rrbn6VT_hnCF8jYqkt6-dg
```
The following is the base64url-decoded header and payload of the DPoP Proof JWT.
The `htu` claim holds the URL of the PAR endpoint.
```json theme={null}
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "hGfqzGXgao1QgVITY6kiHYOKbaLXBxTqPJa4EOimxhI",
"y": "E1KiA_fA2x8IrrysoGdnBTMB5-o3EJT_MgQA_HmGu9M"
}
}
```
```json theme={null}
{
"jti": "q1fIKmZwaL4ygkRP",
"htm": "POST",
"htu": "https://trial.authlete.net/api/par",
"iat": 1719474636
}
```
##### 4.5.2.4. Step 4 : PAR Request
One of the advantages of the PAR endpoint over the authorization endpoint is
that the PAR endpoint can perform client authentication. In this demo, we use
attestation-based client authentication when accessing the PAR endpoint.
The client authentication method requires that two JWTs, namely, a client
attestation and a client attestation PoP, be specified by two HTTP headers,
`OAuth-Client-Attestation` and
`OAuth-Client-Attestation-PoP`.
With the client attestation and the client attestation PoP you created in Step 2,
and the DPoP Proof JWT you created in Step 3, you can make a PAR request by
executing the following command. Please replace `${CLIENT_ATTESTATION}`,
`${CLIENT_ATTESTATION_POP}`, `${DPOP_PROOF_JWT_FOR_PAR_REQUEST}` and
`${CODE_CHALLENGE}` in the command line with the actual values you created in
the previous steps.
```shell theme={null}
curl -s https://trial.authlete.net/api/par \
-H "OAuth-Client-Attestation: ${CLIENT_ATTESTATION}" \
-H "OAuth-Client-Attestation-PoP: ${CLIENT_ATTESTATION_POP}" \
-H "DPoP: ${DPOP_PROOF_JWT_FOR_PAR_REQUEST}" \
-d client_id=track2_full \
-d response_type=code \
-d redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection \
-d scope=potential.track2.full.profile \
-d code_challenge=${CODE_CHALLENGE} \
-d code_challenge_method=S256
```
The PAR endpoint will return JSON as a response like below. In a successful
case, the JSON contains the `request_uri` property. The value of this property
is a **request URI** that represents the pre-registered authorization request.
```json theme={null}
{
"expires_in": 600,
"request_uri": "urn:ietf:params:oauth:request_uri:-CYpNdxTlS3S7e0PQKJVehPMnC0iiIk4pqJpD25k0Ws"
}
```
In this example, the value of the request URI is
`urn:ietf:params:oauth:request_uri:-CYpNdxTlS3S7e0PQKJVehPMnC0iiIk4pqJpD25k0Ws`.
You are expected to specify this value as the value of the `request_uri`
parameter of the authorization request that you will make in the next step.
##### 4.5.2.5. Step 5 : Authorization Request
Please input the following URL in the address bar of your web browser. This
is an authorization request to the authorization endpoint of the authorization
server. Don't forget to replace `${REQUEST_URI}` in the URL with the actual
value of the request URI you obtained in the previous step.
```
https://trial.authlete.net/api/authorization?client_id=track2_full&request_uri=${REQUEST_URI}
```
The authorization endpoint will return an authorization page like below.
In the page, input `inga` and `inga` as Login ID and Password, and then
click the "Authorize" button.
You will be redirected to the redirection endpoint (cf.
[RFC 6749, 3.1.2][RFC_6749_3_1_2]).
This redirection endpoint displays key-value pairs that it has received.
The value of the `code` parameter displayed there is the issued
authorization code. In this example, the value of the authorization code is
`yn1W7SLX9OEGqFZ2D986iPowVnoQtIRpPByBEyiIrBk`.
The authorization code will be used in the token request you will make later.
##### 4.5.2.6. Step 6 : DPoP Proof JWT for Token Request
You need to create a new DPoP Proof JWT to access the token endpoint. You cannot
reuse the DPoP Proof JWT you created for the PAR request.
The following command line can generate the DPoP Proof JWT. Please note that the
value of the `-u` option is the URL of the token endpoint (not the PAR endpoint).
```shell theme={null}
./generate-dpop-proof -k dpop.jwk -m POST \
-u https://trial.authlete.net/api/token
```
The `generate-dpop-proof` script will generate a JWT like below.
```
eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiaEdmcXpHWGdhbzFRZ1ZJVFk2a2lIWU9LYmFMWEJ4VHFQSmE0RU9pbXhoSSIsInkiOiJFMUtpQV9mQTJ4OElycnlzb0dkbkJUTUI1LW8zRUpUX01nUUFfSG1HdTlNIn19.eyJqdGkiOiJFYzVhdklIcTN5Q25pUUFNIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vdHJpYWwuYXV0aGxldGUubmV0L2FwaS90b2tlbiIsImlhdCI6MTcxOTQ3NTAxNX0.0ahIcVPSpMY-g-_SfkB8C84wDhalE1FLfobyDciBoFv16W82gMIuoBw3kDIhHmGJAxxOVKOyTQFKMi25c30vlw
```
The following is the base64url-decoded header and payload of the DPoP Proof JWT.
The `htu` claim holds the URL of the token endpoint.
```json theme={null}
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "hGfqzGXgao1QgVITY6kiHYOKbaLXBxTqPJa4EOimxhI",
"y": "E1KiA_fA2x8IrrysoGdnBTMB5-o3EJT_MgQA_HmGu9M"
}
}
```
```json theme={null}
{
"jti": "Ec5avIHq3yCniQAM",
"htm": "POST",
"htu": "https://trial.authlete.net/api/token",
"iat": 1719475015
}
```
##### 4.5.2.7. Step 7 : Token Request
Now you have the following:
* Client Attestattion (JWT)
* Client Attestation PoP (JWT)
* DPoP Proof JWT for Token Request (JWT)
* Authorization Code
* Code Verifier
With these, you can make a token request as follows. Don't forget to replace
variables in the command line with the actual values you created in the
previous steps.
```shell theme={null}
curl -s https://trial.authlete.net/api/token \
-H "OAuth-Client-Attestation: ${CLIENT_ATTESTATION}" \
-H "OAuth-Client-Attestation-PoP: ${CLIENT_ATTESTATION_POP}" \
-H "DPoP: ${DPOP_PROOF_JWT_FOR_TOKEN_REQUEST}" \
-d client_id=track2_full \
-d grant_type=authorization_code \
-d code=${AUTHORIZATION_CODE} \
-d redirect_uri=https://nextdev-api.authlete.net/api/mock/redirection \
-d code_verifier=${CODE_VERIFIER}
```
The token endpoint will return JSON as a response like below.
```json theme={null}
{
"access_token": "_aPAJGv3bnb8UoSbkBCP9XF0tnCHoLDGBc6chbFSWkg",
"token_type": "DPoP",
"expires_in": 86400,
"scope": "potential.track2.full.profile",
"refresh_token": "NPJv7yAu5YKT7a6qeWes4mL8exDx-bPruXDQE3p8u80",
"c_nonce": "01bcC_9FtNM22wO9pnUqRkeFQuJNEE3sHA6M-oDnsHs",
"c_nonce_expires_in": 86400
}
```
The value of the `access_token` property is the issued access token. This needs
to be included in the credential request you will make later.
The value of the `c_nonce` property is the issued nonce. This value needs to be
included in the JWT Key Proofs you will create in the next step.
##### 4.5.2.8. Step 8 : JWT Key Proofs for Credential Request
In POTENTIAL's Track 2 Full Profile, a new feature introduced by
"[OpenID4VCI PR 293: rework credential and batch credential endpoint][OID4VCI_PR293]"
is used. This new feature enables a client application to request multiple
verifiable credentials in a single credential request.
To request multiple verifiable credentials, a credential request needs to
include multiple key proofs. Therefore, here we are going to generate two
JWT Key Proofs with different holder keys.
The [oid4vci-demo][AUTHLETE_OID4VCI_DEMO] repository contains two holder
keys, `holder.jwk` and `holder2.jwk`, for demo. With these keys and the
`generate-key-proof` script, you can generate JWT
Key Proofs as follows. Please replace `${C_NONCE}` in the command lines
with the actual value of the `c_nonce` property in the token response
you received in the previous step before executing the command lines.
```shell theme={null}
JWT_KEY_PROOF_1=`./generate-key-proof -i https://trial.authlete.net -k holder.jwk -c track2_full -n ${C_NONCE}`
JWT_KEY_PROOF_2=`./generate-key-proof -i https://trial.authlete.net -k holder2.jwk -c track2_full -n ${C_NONCE}`
```
The `c_nonce` property in a token response is optional, so the token
response from your authorization server may not include this property. In that case,
you cannot generate a valid key proof including `c_nonce` until you get
a valid `c_nonce` from your credential issuer.
When the `c_nonce` included in a key proof is invalid, the credential
issuer will include a valid `c_nonce` in the error response. With the
`c_nonce`, you can generate a valid key proof.
These steps described above imply that, if your authorization server does not
include `c_nonce` in a token response, your first credential request
with a key proof including a (fake) `c_nonce` inevitably fails.
Although you know the first request will fail, you need to make the request only
to get a valid `c_nonce`.
The `c_nonce` property in a token response is optional, so
authorization server implementations don't have to include the property in a
token response if they don't want to. However, there are several people in the
industry who dislike the feature that allows authorization servers to issue
`c_nonce` and are attempting to remove this feature itself from the
OID4VCI specification. If their attempt succeeded, all initial credential requests
containing a key proof with `c_nonce` worldwide would inevitably fail.
Authlete will continue to support the `c_nonce` property in a token
response for harmless backward compatibility with Implementer's Draft 1 of the
OID4VCI specification, unless the specification is revised to explicitly prohibit
including the property. However, introducing such prohibition would conflict with
RFC 6749, the core
specification of OAuth 2.0, which allows token responses to include arbitrary
response parameters.
The following is an example of JWT Key Proof with `holder.jwk`. Please note that
the content of the `jwk` claim in the header holds the public key corresponding
to the private key in `holder.jwk`.
```
eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiUFN4UXJEMnpsMF9tWGNBcXoxbWdxU2VCb0Jobm14Mnl4QkVwckJZOEYyMCIsInkiOiJ4VjhmYmkxRlNvc1V1bkxldUxOdUxrSmlxbVk2VEtpTW51ci1HbjJ3UjEwIn19.eyJpc3MiOiJ0cmFjazJfZnVsbCIsImF1ZCI6Imh0dHBzOi8vdHJpYWwuYXV0aGxldGUubmV0IiwiaWF0IjoxNzE5NDc2Mjg4LCJub25jZSI6IjAxYmNDXzlGdE5NMjJ3TzlwblVxUmtlRlF1Sk5FRTNzSEE2TS1vRG5zSHMifQ.lgI038MOL6DtbDnPOa8GZen0d2EoiZ-dgn_IU9hJYe5aWxL81hADNOS6v7ChJ9zxcU90sPDEJEyM12tDVm7VPQ
```
```json theme={null}
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
"y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
}
}
```
```json theme={null}
{
"iss": "track2_full",
"aud": "https://trial.authlete.net",
"iat": 1719476288,
"nonce": "01bcC_9FtNM22wO9pnUqRkeFQuJNEE3sHA6M-oDnsHs"
}
```
Likewise, the following is an example of JWT Key Proof with `holder2.jwk`.
The `jwk` claim in the header is the public key corresponding to the private
key in `holder2.jwk`.
```
eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiSkotN2YwQXNRNWZJUmRxaDJySXoxSS02SkpBTUowQjUzM1Iybm8tRmtfQSIsInkiOiJ6LTFFZnc2ZmRoZ2RLVEZISVhOSDI5bV9UTlpjWUpDLUxDSVU2WWRQdFI4In19.eyJpc3MiOiJ0cmFjazJfZnVsbCIsImF1ZCI6Imh0dHBzOi8vdHJpYWwuYXV0aGxldGUubmV0IiwiaWF0IjoxNzE5NDc2MzAwLCJub25jZSI6IjAxYmNDXzlGdE5NMjJ3TzlwblVxUmtlRlF1Sk5FRTNzSEE2TS1vRG5zSHMifQ.nNGWEYSDvH_B632TF_Z_ecG14fzmBR6QTjQS3Irjp5xiPLkHtm_XXGvKFeVLTc8erZpmWPvD_2e99DUvDsajyA
```
```json theme={null}
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "JJ-7f0AsQ5fIRdqh2rIz1I-6JJAMJ0B533R2no-Fk_A",
"y": "z-1Efw6fdhgdKTFHIXNH29m_TNZcYJC-LCIU6YdPtR8"
}
}
```
```json theme={null}
{
"iss": "track2_full",
"aud": "https://trial.authlete.net",
"iat": 1719476300,
"nonce": "01bcC_9FtNM22wO9pnUqRkeFQuJNEE3sHA6M-oDnsHs"
}
```
##### 4.5.2.9. Step 9 : DPoP Proof JWT for Credential Request
You need to create another DPoP Proof JWT again to access the credential endpoint.
In addition to specifying the URL of the credential endpoint as the value of
the `-u` option, this time you need to specify the access token value using
the `-a` option (short for `--at` option). This option is necessary to include
the `ath` claim in the DPoP Proof JWT.
```shell theme={null}
./generate-dpop-proof -k dpop.jwk -m POST \
-u https://trial.authlete.net/api/credential \
-a ${ACCESS_TOKEN}
```
The following is an example of DPoP Proof JWT for a credential request.
Please note that the payload part includes the `ath` claim, which
represents the hash value of the access token.
```
eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiaEdmcXpHWGdhbzFRZ1ZJVFk2a2lIWU9LYmFMWEJ4VHFQSmE0RU9pbXhoSSIsInkiOiJFMUtpQV9mQTJ4OElycnlzb0dkbkJUTUI1LW8zRUpUX01nUUFfSG1HdTlNIn19.eyJqdGkiOiI3Vm9zZW1YZ3ZLNUpVMVI0IiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vdHJpYWwuYXV0aGxldGUubmV0L2FwaS9jcmVkZW50aWFsIiwiaWF0IjoxNzE5NDc2ODQxLCJhdGgiOiIzQWV6TzBZMTFnZzgwVm5Zd3puLUpDbXFNVnZkc2pJZXlBMTRoc1BOdHVvIn0.zDZYZdgb2jo4KKM0_OariZK0yrEcIFmPdpImX0Q-pttqw8480-nVdW-lQlQqQvcMtDKEWwZtgcNTsNExgomZ2g
```
```json theme={null}
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"crv": "P-256",
"kty": "EC",
"x": "hGfqzGXgao1QgVITY6kiHYOKbaLXBxTqPJa4EOimxhI",
"y": "E1KiA_fA2x8IrrysoGdnBTMB5-o3EJT_MgQA_HmGu9M"
}
}
```
```json theme={null}
{
"jti": "7VosemXgvK5JU1R4",
"htm": "POST",
"htu": "https://trial.authlete.net/api/credential",
"iat": 1719476841,
"ath": "3AezO0Y11gg80VnYwzn-JCmqMVvdsjIeyA14hsPNtuo"
}
```
##### 4.5.2.10. Step 10 : Credential Request
Now you have the following:
* Access Token
* DPoP Proof JWT for Credential Request (JWT)
* JWT Key Proof 1 (JWT)
* JWT Key Proof 2 (JWT)
With these, you can make a credential request as follows.
```shell theme={null}
curl -s https://trial.authlete.net/api/credential \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "DPoP: ${DPOP_PROOF_JWT_FOR_CREDENTIAL_REQUEST}" \
-H "Content-Type: application/json" \
--data '{
"format": "vc+sd-jwt",
"vct": "urn:eu.europa.ec.eudi:pid:1",
"proofs": {
"jwt": [
"'${JWT_KEY_PROOF_1}'",
"'${JWT_KEY_PROOF_2}'"
]
}
}'
```
The key point here is that the message body of the credential request contains
the `proofs` property instead of the `proof` property. The `proofs` property
is a new addition introduced by [OpenID4VCI PR 293][OID4VCI_PR293], which
enables the inclusion of multiple key proofs.
The credential endpoint will return a response like below.
```json theme={null}
{
"credentials": [
"eyJraWQiOiJKMUZ3SlA4N0M2LVFOX1dTSU9tSkFRYzZuNUNRX2JaZGFGSjVHRG5XMVJrIiwidHlwIjoidmMrc2Qtand0IiwiYWxnIjoiRVMyNTYifQ.eyJwbGFjZV9vZl9iaXJ0aCI6eyJfc2QiOlsiTkVXa1ZYODhqaENvMVVFZ0ZiTktQNjYyNkVhVXp6TldCRzdId1VZOFY4VSIsIlRfaUs3UG9ISDRfalNBWkdaMVljbXc2ckl2ZE1fM1pHTFpMV0JxUTZCOTgiXX0sIl9zZCI6WyItOWNGNnJ1TkFTRzdMX09ndEdTZlhSX0pDT1VmQ3pkVmF6dVB2RFFsV3FnIiwiMXdVMDdzdHR2NnBMN2FHal95YjJ5Z0tqVUFTeVZpLVk1cUxPcWgzN3ZPQSIsIjR1TF8taHhQTUpJTzVZdTdwTm4wbU95SEJBWjVsR0cwYWNhM2pZaG84cFUiLCI3dUZXV05vQURlTUpzVzFmcnhiVDV4czlHM3h4Qy0wSWRYTGV3RE5yWGxnIiwiOV82Y3hMMXNlRzUxRVdiZjlOcjFIZklpRTVpajdnWU9ONllxRXdqOGl5SSIsIjljWkJFUURYUTY2RGhqd1JPMUhzcXZOLVBLRHRuYW5BWlk2QW03aXUxMEkiLCJBTlRHcEZkaXk2cFpiXzdvb1pXQmQtVVlGUHR3ZDRiNDRXcW5WeGUxQjFBIiwiQkJTTU03ZXdYb2t3cVdSY1lDUmlLazJzbW1pNWdrM05NU21EMlpONnlTcyIsIlA4WG0zMUNUSXN3UzVTNlFyYkstc2tfc1dTUGMtZmJuWGM2c1VfMUpDRkUiLCJUckRwYVZxcnpSVWxfM0ktejE0SWtZdmxEZndTY2djbXI5Ui1maEZjcVhFIiwiZXpPREtNdkNGWW9ER2c0bWNLRFJ1VzJPemlMaml4VjRKRkNRS0Z6QnRsTSIsIm0yYmRpOHNPcEhRZTg1NTNhamJYQTNwakNTRzN0Qjh4T3VvaUN5eVlVYmMiLCJ1dG1yaWxSWGdTRHBsblFkdW5OUzZ2Ym8xOFB2cktsNnhEa2tzVFZ3YXVZIiwieEozS3NOam5faFl6aTNMWlZITDBoRklOWVdFM3EtZVJoRUdGRWVUQlBIOCJdLCJhZGRyZXNzIjp7Il9zZCI6WyIzUTFVdk1GbGl0VFYxM0EwN016ZjI3UzZfVW1Na2JEVFBfa0N3RklkTlJnIiwiQTgzNG9MTlY1dEVPb0c2eVMtRnRFM0FhalR0UF9GV1VhUXhBOWdHVXlnWSIsIkZRdG5MLXBaVkNRUUhRQ1Y3TVl6M0hwaUpOX1JIRmZObDlVSms3SGgwTUkiLCJHVWxzcjBpcUlMTVk1QXk4TnlZMzFiczNkd1NqdkZrd09aZHQxRlR1QndnIiwiSXk1TEFhLVEtNDhkMWN2d0VDN3VxYjBEMFR5VmxWX2l0dkhzck1KZE1UdyIsIkxzYlBzTGg4N0FiU3dwellYRmdTRnZZNHNFTFdZZXB2S0JzeVNBRWxhMXciLCJVZlE0QktRR2ZmZF9wU1VCYkd1RUJQM2d6Rm9zNU9sTFNvMUlxd1k0TWxJIiwiVjQtRTc2V1NKUjBuMU1xZHdsbElTTmoxaEtnRGc4d09qdElyam5rQ2tZZyIsIlhSZkNFZTVPcy1KcFM5OVFXY1pqRF9udXQ3d0Jwc0QzX1djWjZ1amhseDAiLCJpQ05YazdMLTQ3MnZnUmo2R0RDZ0RqWE5SUVBKaXM4amJTb3lRV09KRC1BIiwibkc5LUY4RDVPZWZZNmdLQmxVSU1WdFBaZXluX0FISGhmMzZIRzNmQ2tWNCIsInBYYlJTUTFfeFNUYUhIRjRhaFEzZFZPenQ1ODI3TkYxa0ZLUmtnUEd3cVUiXX0sInZjdCI6InVybjpldS5ldXJvcGEuZWMuZXVkaTpwaWQ6MSIsIl9zZF9hbGciOiJzaGEtMjU2IiwiaXNzIjoiaHR0cHM6Ly90cmlhbC5hdXRobGV0ZS5uZXQiLCJjbmYiOnsiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJraWQiOiI0TTlrSXJCOVdZenQxR1FnTDEybHpkQlpzR3llVjNsZ1BLb3YyOG9UNUw0IiwieCI6IlBTeFFyRDJ6bDBfbVhjQXF6MW1ncVNlQm9CaG5teDJ5eEJFcHJCWThGMjAiLCJ5IjoieFY4ZmJpMUZTb3NVdW5MZXVMTnVMa0ppcW1ZNlRLaU1udXItR24yd1IxMCJ9fSwiaWF0IjoxNzE5NDc2ODU1LCJhZ2VfZXF1YWxfb3Jfb3ZlciI6eyJfc2QiOlsiS1pPZzhDUENVWkI4Tl85ZC1YTVVvQ0tYQ2xTbEh3NHBiTmlFOEE4Mzk5ZyIsImZmZ2lfTmhVV0JWbVNiZ3J5WmtrMXpSdmhJNXJHQjFhd2diT2ZiRC1oZU0iXX19.dlnBVF7xd9uFbaUS0ExMHN-62uOnmzAmGSNOYJrP3jNNpV1ihmu02hdDTFEFuhhMQ2DXeKYG3mnbYZ4gjQogVw~WyJubFJHOFlwVXRyWlZybnZMaGsxVVNRIiwic3ViIiwiMTAwNCJd~WyJ1U0pTVHhqZHBKLVZKYzVJTTY3cHNnIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd~WyJtWFg5ZHBLNHFZWC16WHRuczdnb2tnIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ~WyJ0Q0NxOUNvWHJRTjJSY3FoY1dUTG9nIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd~WyJlQU1BaG51QlAxQTY4Und3YU9YUU13IiwiMTgiLHRydWVd~WyJ0MVJ2YS1jeTVLQS0xT1hYQW93ZklRIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd~WyJpNXJNNDVZdjN1WHplRnZEekpxbjdnIiwiZm9ybWF0dGVkIiwiMTE0IE9sZCBTdGF0ZSBId3kgMTI3LCBTaG9zaG9uZSwgQ0EgOTIzODQsIFVTQSJd~WyJvaFhiOVdFQTdHTmc0SEVrem9yVG5BIiwic3RyZWV0X2FkZHJlc3MiLCIxMTQgT2xkIFN0YXRlIEh3eSAxMjciXQ~WyJFdmY1dkhCRFc4RmgzQk84SnZNOHR3IiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd~WyIyZFJVWFBfajlIUF9nX1VFYTZRVkh3IiwicG9zdGFsX2NvZGUiLCJDQSA5MjM4NCJd~WyJSbmNCeVBRdW5NbTNtb3hDRnVaOFpRIiwiY291bnRyeSIsIlVTQSJd~WyI1NmJLM1BKcG1wZ1kzdlA4WHNUYnZnIiwiaXNzdWluZ19hdXRob3JpdHkiLCJVUyJd~WyJjdXNabVM1MHVteUk3ZWc2SGhJYWNnIiwiaXNzdWluZ19jb3VudHJ5IiwiVVMiXQ~",
"eyJraWQiOiJKMUZ3SlA4N0M2LVFOX1dTSU9tSkFRYzZuNUNRX2JaZGFGSjVHRG5XMVJrIiwidHlwIjoidmMrc2Qtand0IiwiYWxnIjoiRVMyNTYifQ.eyJwbGFjZV9vZl9iaXJ0aCI6eyJfc2QiOlsiSXdtalFNd3ROTk9nVkJ5ZW5rYWYxemRyYnNSLWlsbFptN3pHNmFvem1NVSIsIlNxU25Rb0FnbWJqS3dkVzBNMDhvVnVfdktLLWk1bVA3RHNVNnlwdzFOenMiXX0sIl9zZCI6WyIzcXlKY0luTVRFOFZtb0FQdDVNUmNtdUREYXBUTF9BSmRHbGNxUzdJRVBFIiwiNkotbGQ5S3ktQ3hUSXhqbnlGNnYzX0JLajUyR01GQ3hndF9BSnZJTnNRVSIsIjZrY3dwdkRCZ05DVkUzM2RnUmd5YjJ1cERBNW45TUZhNS1ra1phSkZzZTQiLCJEWDRJT0hGVUVoVDZvNlNqTzVoUGZieUdPb3NmcC1HVjhNdmxkZnV2aXJZIiwiRWItMG1PRkNGWWViV2VPY3VNNmtQRWFpRFh4TnZETFBqcjkzbzNROUppcyIsIklLTzl5OEZ2TllpTGRpYUVwT0hBZWpYRGFYMVdXeVRPUnRUdnY3aUlTLU0iLCJJS1QwNGRxZ1NhM1B6aUlNZWRtU0NOWTl6MjRvcEEzRmh6LWUwbTdxV0VZIiwiSXlIemhjSDdpM3NkS182S0VJSWdDaVkzRWdDOVBUaU5zY1dpd1BGS3FHayIsIkpNeXg4QzRhakxVTzNLR3NTRDJobkMxcUJpSV80SThlc2QxZDNlcnNMU2siLCJTUUMzSUJDMlphVlBCZFU0QmYyZUZIcFVDUjJaRU1uZkFQUW1OMmVraWlvIiwiWEVJVmo4X2NXSjRBV1N3cTZWMlpOblA5Z3JfaVU1cUJkcGxTQXIwdC1JOCIsIlo0NXZjM19acUk2Uk1GN0xaaE5XZVp2YlIwRVA3NUNtLXdNcUVuSkdaVkEiLCJfRjZWTE56dTN3cVNTOHBRV1p5eXpOLVRybEZWX1h5LXp1WmNRbDFWd2NRIiwiYXl2UlVTdmc4a3N5Mnk0QjhGUk16NGxoTVdyWFRkcFVZT2p0aVlnVVFEUSIsImc4UjhhZnBRVERzZVFkZjZqbXJfTGNoVUxUc2V1enJ1SWxUVWllNFRXVjAiLCJnd2lwTUJESFRDanFsMHl0b1c2djdBUGo1Vm5sbVpMTWwyUlNYZVZTRXdJIiwiaEJ4UXhqT3QwQzN4SUpZYmNPaFJwU2Y5ajZjSm0wRTNoUU5WVFNPT3oyQSIsImw1UkZ5RVJRUHRFLUx6a2RFUGhXeXo0d215dzFGLU1RSTBBdjFIZER6NEUiLCJxNHZXNHRLNGQydS1vZHkybDNtNl9qYm14NVpTUmlVTzVHN0lwMTNOSjF3IiwidVhpTzQ3WFdXbEdQRGE2cS1zSk5Qc0R6THdXTGxpMTQyN2NQNkEzQzNIVSIsInlCb1B4aURuWjVhaWdJdjVZek9VMTEyQVdHT3BVaVNDRjNKZHgydjZEdDgiLCJ6T1E2alU3X094MDdxWXFJSjdvVzIxNmJiUWRzbXlfQXlHcTBpbW5ueUFjIl0sImFkZHJlc3MiOnsiX3NkIjpbIjJDdml3VXo1eEwwUllGc1BhSFlZeFVKVXgwY09vRFhCYUlsZnNvWHJoem8iLCI1NmswNWpXTmNaLUVwZ1loaDNjUnBESjhuSkVXOC04WnFvSWNyWFVicEFNIiwiN3d5cjd4MzlvMlN6Vjd0cmJ1SUN4d0xqTVBiaXdRdnU0eHVHN09sQUxpWSIsIkpsOEJveURITXkxeVpKQWEwRHliU0JzbUJSZ1hFVU9FUTQxb2xwS29uOHciLCJLc1h4c0JIWW9fb3dVa2J4VlVRT0ZjUHRoOWVqRG5BZzhjYVNXQVd4R2FJIiwiWXpsdWY0bTRMdGFoMEVzaGszQ0Y3aVMtcTJ0Um0yaVdfVXVZaWNtTzlLcyIsIm4talZaSEU2ZDN0ODVaQ1BXYTFOQm5KTVNFdDgweU9MMUphdFF1ZVhmd28iLCJvbUF4a2xVS3JyMk9sNW9nRlk2WGdJLVhtNHY2M1VrVmIxX09XYVhra0VRIiwid1dCa1dQZU1SdUdGM2I2S0drQnhyeGdZY2tqVzNxUERET3RyOVBOd0NiRSJdfSwidmN0IjoidXJuOmV1LmV1cm9wYS5lYy5ldWRpOnBpZDoxIiwiX3NkX2FsZyI6InNoYS0yNTYiLCJpc3MiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsImtpZCI6IkI4b3M4ZE5CTTF2aU9OWU96T0hmUEtKd3AzU0dsTVlLU05BQ1NXdVpScXMiLCJ4IjoiSkotN2YwQXNRNWZJUmRxaDJySXoxSS02SkpBTUowQjUzM1Iybm8tRmtfQSIsInkiOiJ6LTFFZnc2ZmRoZ2RLVEZISVhOSDI5bV9UTlpjWUpDLUxDSVU2WWRQdFI4In19LCJpYXQiOjE3MTk0NzY4NTUsImFnZV9lcXVhbF9vcl9vdmVyIjp7Il9zZCI6WyJJOEdHR2VXQXJFRzAwUHFiNV9qZkJnMGhYRndET1k5aTRnbU9ZTGp1OS1ZIiwidUhmbDRiZ00wV2I2YTBWSHUzWWx0YU1mSGR1TXZIbzIyMVVBUUJkMDhYVSJdfX0.oI6tgincnKaRqACfYe46PweL1kvbrbjJcnwKnBtGDpUfWbvRTj2B8pn4JWVQ2XPBDEnAJaeO0tJ7B1U1infWMA~WyJBMFYzTDlwdzQ3U1BfSk5XYTVFOGhBIiwic3ViIiwiMTAwNCJd~WyJLZy1VTFN4N2JlOGRlYm5yMngyVjRnIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd~WyJZLUVCUkw0OTJyUm54T3BGaWtUOE1BIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ~WyJreUFTdHg1SWNUaXVfQ3RNdTBDck5RIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd~WyI4WGJCNGRRZENZbkh6a3BuR094YU1RIiwiMTgiLHRydWVd~WyJYM0RBUHdFWTRGaEQ2UUNNSU5idXBnIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd~WyJ3SDhKTE5tc1dkcVdfZ3Q3RmtQbGpRIiwiZm9ybWF0dGVkIiwiMTE0IE9sZCBTdGF0ZSBId3kgMTI3LCBTaG9zaG9uZSwgQ0EgOTIzODQsIFVTQSJd~WyJEVUJaRXNkWVVPREhoZTJ3TUxFMnpBIiwic3RyZWV0X2FkZHJlc3MiLCIxMTQgT2xkIFN0YXRlIEh3eSAxMjciXQ~WyJ6dXQzd0pnbjN1MExCcTJRNTlXZEFRIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd~WyJsVzdXQTkxakRON1RGWkpRRzJNUi1BIiwicG9zdGFsX2NvZGUiLCJDQSA5MjM4NCJd~WyJlczdiUHpNeXdNaXJON0tzemtpMFdRIiwiY291bnRyeSIsIlVTQSJd~WyJtOEVlUUM2akhIeUlSWGVDQXhERi1nIiwiaXNzdWluZ19hdXRob3JpdHkiLCJVUyJd~WyIxMzhhaHkwMVdvNFFxekZyNFlpSld3IiwiaXNzdWluZ19jb3VudHJ5IiwiVVMiXQ~"
],
"c_nonce": "01bcC_9FtNM22wO9pnUqRkeFQuJNEE3sHA6M-oDnsHs",
"c_nonce_expires_in": 84589
}
```
In a successful case, the response contains the `credentials` property instead
of the `credential` property. Elements in the `credentials` array are issued
verifiable credentials. In this example, the `credentials` array contains two
verifiable credentials because the credential request contained two key proofs.
The format of the verifiable credentials is SD-JWT. They can be decoded using
the `decode-sd-jwt` script in the
[oid4vci-demo][AUTHLETE_OID4VCI_DEMO] repository.
The first verifiable credetial is decoded as follows:
```json theme={null}
{
"kid": "J1FwJP87C6-QN_WSIOmJAQc6n5CQ_bZdaFJ5GDnW1Rk",
"typ": "vc+sd-jwt",
"alg": "ES256"
}
{
"place_of_birth": {
"_sd": [
"NEWkVX88jhCo1UEgFbNKP6626EaUzzNWBG7HwUY8V8U",
"T_iK7PoHH4_jSAZGZ1Ycmw6rIvdM_3ZGLZLWBqQ6B98"
]
},
"_sd": [
"-9cF6ruNASG7L_OgtGSfXR_JCOUfCzdVazuPvDQlWqg",
"1wU07sttv6pL7aGj_yb2ygKjUASyVi-Y5qLOqh37vOA",
"4uL_-hxPMJIO5Yu7pNn0mOyHBAZ5lGG0aca3jYho8pU",
"7uFWWNoADeMJsW1frxbT5xs9G3xxC-0IdXLewDNrXlg",
"9_6cxL1seG51EWbf9Nr1HfIiE5ij7gYON6YqEwj8iyI",
"9cZBEQDXQ66DhjwRO1HsqvN-PKDtnanAZY6Am7iu10I",
"ANTGpFdiy6pZb_7ooZWBd-UYFPtwd4b44WqnVxe1B1A",
"BBSMM7ewXokwqWRcYCRiKk2smmi5gk3NMSmD2ZN6ySs",
"P8Xm31CTIswS5S6QrbK-sk_sWSPc-fbnXc6sU_1JCFE",
"TrDpaVqrzRUl_3I-z14IkYvlDfwScgcmr9R-fhFcqXE",
"ezODKMvCFYoDGg4mcKDRuW2OziLjixV4JFCQKFzBtlM",
"m2bdi8sOpHQe8553ajbXA3pjCSG3tB8xOuoiCyyYUbc",
"utmrilRXgSDplnQdunNS6vbo18PvrKl6xDkksTVwauY",
"xJ3KsNjn_hYzi3LZVHL0hFINYWE3q-eRhEGFEeTBPH8"
],
"address": {
"_sd": [
"3Q1UvMFlitTV13A07Mzf27S6_UmMkbDTP_kCwFIdNRg",
"A834oLNV5tEOoG6yS-FtE3AajTtP_FWUaQxA9gGUygY",
"FQtnL-pZVCQQHQCV7MYz3HpiJN_RHFfNl9UJk7Hh0MI",
"GUlsr0iqILMY5Ay8NyY31bs3dwSjvFkwOZdt1FTuBwg",
"Iy5LAa-Q-48d1cvwEC7uqb0D0TyVlV_itvHsrMJdMTw",
"LsbPsLh87AbSwpzYXFgSFvY4sELWYepvKBsySAEla1w",
"UfQ4BKQGffd_pSUBbGuEBP3gzFos5OlLSo1IqwY4MlI",
"V4-E76WSJR0n1MqdwllISNj1hKgDg8wOjtIrjnkCkYg",
"XRfCEe5Os-JpS99QWcZjD_nut7wBpsD3_WcZ6ujhlx0",
"iCNXk7L-472vgRj6GDCgDjXNRQPJis8jbSoyQWOJD-A",
"nG9-F8D5OefY6gKBlUIMVtPZeyn_AHHhf36HG3fCkV4",
"pXbRSQ1_xSTaHHF4ahQ3dVOzt5827NF1kFKRkgPGwqU"
]
},
"vct": "urn:eu.europa.ec.eudi:pid:1",
"_sd_alg": "sha-256",
"iss": "https://trial.authlete.net",
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"kid": "4M9kIrB9WYzt1GQgL12lzdBZsGyeV3lgPKov28oT5L4",
"x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
"y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
}
},
"iat": 1719476855,
"age_equal_or_over": {
"_sd": [
"KZOg8CPCUZB8N_9d-XMUoCKXClSlHw4pbNiE8A8399g",
"ffgi_NhUWBVmSbgryZkk1zRvhI5rGB1awgbOfbD-heM"
]
}
}
{
"digest": "4uL_-hxPMJIO5Yu7pNn0mOyHBAZ5lGG0aca3jYho8pU",
"WyJubFJHOFlwVXRyWlZybnZMaGsxVVNRIiwic3ViIiwiMTAwNCJd": [
"nlRG8YpUtrZVrnvLhk1USQ",
"sub",
"1004"
]
}
{
"digest": "9_6cxL1seG51EWbf9Nr1HfIiE5ij7gYON6YqEwj8iyI",
"WyJ1U0pTVHhqZHBKLVZKYzVJTTY3cHNnIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd": [
"uSJSTxjdpJ-VJc5IM67psg",
"family_name",
"Silverstone"
]
}
{
"digest": "m2bdi8sOpHQe8553ajbXA3pjCSG3tB8xOuoiCyyYUbc",
"WyJtWFg5ZHBLNHFZWC16WHRuczdnb2tnIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ": [
"mXX9dpK4qYX-zXtns7gokg",
"given_name",
"Inga"
]
}
{
"digest": "BBSMM7ewXokwqWRcYCRiKk2smmi5gk3NMSmD2ZN6ySs",
"WyJ0Q0NxOUNvWHJRTjJSY3FoY1dUTG9nIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd": [
"tCCq9CoXrQN2RcqhcWTLog",
"birthdate",
"1991-11-06"
]
}
{
"digest": "KZOg8CPCUZB8N_9d-XMUoCKXClSlHw4pbNiE8A8399g",
"WyJlQU1BaG51QlAxQTY4Und3YU9YUU13IiwiMTgiLHRydWVd": [
"eAMAhnuBP1A68RwwaOXQMw",
"18",
true
]
}
{
"digest": "T_iK7PoHH4_jSAZGZ1Ycmw6rIvdM_3ZGLZLWBqQ6B98",
"WyJ0MVJ2YS1jeTVLQS0xT1hYQW93ZklRIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd": [
"t1Rva-cy5KA-1OXXAowfIQ",
"locality",
"Shoshone"
]
}
{
"digest": "V4-E76WSJR0n1MqdwllISNj1hKgDg8wOjtIrjnkCkYg",
"WyJpNXJNNDVZdjN1WHplRnZEekpxbjdnIiwiZm9ybWF0dGVkIiwiMTE0IE9sZCBTdGF0ZSBId3kgMTI3LCBTaG9zaG9uZSwgQ0EgOTIzODQsIFVTQSJd": [
"i5rM45Yv3uXzeFvDzJqn7g",
"formatted",
"114 Old State Hwy 127, Shoshone, CA 92384, USA"
]
}
{
"digest": "nG9-F8D5OefY6gKBlUIMVtPZeyn_AHHhf36HG3fCkV4",
"WyJvaFhiOVdFQTdHTmc0SEVrem9yVG5BIiwic3RyZWV0X2FkZHJlc3MiLCIxMTQgT2xkIFN0YXRlIEh3eSAxMjciXQ": [
"ohXb9WEA7GNg4HEkzorTnA",
"street_address",
"114 Old State Hwy 127"
]
}
{
"digest": "GUlsr0iqILMY5Ay8NyY31bs3dwSjvFkwOZdt1FTuBwg",
"WyJFdmY1dkhCRFc4RmgzQk84SnZNOHR3IiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd": [
"Evf5vHBDW8Fh3BO8JvM8tw",
"locality",
"Shoshone"
]
}
{
"digest": "LsbPsLh87AbSwpzYXFgSFvY4sELWYepvKBsySAEla1w",
"WyIyZFJVWFBfajlIUF9nX1VFYTZRVkh3IiwicG9zdGFsX2NvZGUiLCJDQSA5MjM4NCJd": [
"2dRUXP_j9HP_g_UEa6QVHw",
"postal_code",
"CA 92384"
]
}
{
"digest": "XRfCEe5Os-JpS99QWcZjD_nut7wBpsD3_WcZ6ujhlx0",
"WyJSbmNCeVBRdW5NbTNtb3hDRnVaOFpRIiwiY291bnRyeSIsIlVTQSJd": [
"RncByPQunMm3moxCFuZ8ZQ",
"country",
"USA"
]
}
{
"digest": "utmrilRXgSDplnQdunNS6vbo18PvrKl6xDkksTVwauY",
"WyI1NmJLM1BKcG1wZ1kzdlA4WHNUYnZnIiwiaXNzdWluZ19hdXRob3JpdHkiLCJVUyJd": [
"56bK3PJpmpgY3vP8XsTbvg",
"issuing_authority",
"US"
]
}
{
"digest": "ezODKMvCFYoDGg4mcKDRuW2OziLjixV4JFCQKFzBtlM",
"WyJjdXNabVM1MHVteUk3ZWc2SGhJYWNnIiwiaXNzdWluZ19jb3VudHJ5IiwiVVMiXQ": [
"cusZmS50umyI7eg6HhIacg",
"issuing_country",
"US"
]
}
```
The key point here is that the value of the `cnf.jwk` property matches the
public key embedded in the first key proof.
```json theme={null}
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"kid": "4M9kIrB9WYzt1GQgL12lzdBZsGyeV3lgPKov28oT5L4",
"x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
"y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
}
}
```
Likewise, the second verifiable credential is decoded as follows:
```json theme={null}
{
"kid": "J1FwJP87C6-QN_WSIOmJAQc6n5CQ_bZdaFJ5GDnW1Rk",
"typ": "vc+sd-jwt",
"alg": "ES256"
}
{
"place_of_birth": {
"_sd": [
"IwmjQMwtNNOgVByenkaf1zdrbsR-illZm7zG6aozmMU",
"SqSnQoAgmbjKwdW0M08oVu_vKK-i5mP7DsU6ypw1Nzs"
]
},
"_sd": [
"3qyJcInMTE8VmoAPt5MRcmuDDapTL_AJdGlcqS7IEPE",
"6J-ld9Ky-CxTIxjnyF6v3_BKj52GMFCxgt_AJvINsQU",
"6kcwpvDBgNCVE33dgRgyb2upDA5n9MFa5-kkZaJFse4",
"DX4IOHFUEhT6o6SjO5hPfbyGOosfp-GV8MvldfuvirY",
"Eb-0mOFCFYebWeOcuM6kPEaiDXxNvDLPjr93o3Q9Jis",
"IKO9y8FvNYiLdiaEpOHAejXDaX1WWyTORtTvv7iIS-M",
"IKT04dqgSa3PziIMedmSCNY9z24opA3Fhz-e0m7qWEY",
"IyHzhcH7i3sdK_6KEIIgCiY3EgC9PTiNscWiwPFKqGk",
"JMyx8C4ajLUO3KGsSD2hnC1qBiI_4I8esd1d3ersLSk",
"SQC3IBC2ZaVPBdU4Bf2eFHpUCR2ZEMnfAPQmN2ekiio",
"XEIVj8_cWJ4AWSwq6V2ZNnP9gr_iU5qBdplSAr0t-I8",
"Z45vc3_ZqI6RMF7LZhNWeZvbR0EP75Cm-wMqEnJGZVA",
"_F6VLNzu3wqSS8pQWZyyzN-TrlFV_Xy-zuZcQl1VwcQ",
"ayvRUSvg8ksy2y4B8FRMz4lhMWrXTdpUYOjtiYgUQDQ",
"g8R8afpQTDseQdf6jmr_LchULTseuzruIlTUie4TWV0",
"gwipMBDHTCjql0ytoW6v7APj5VnlmZLMl2RSXeVSEwI",
"hBxQxjOt0C3xIJYbcOhRpSf9j6cJm0E3hQNVTSOOz2A",
"l5RFyERQPtE-LzkdEPhWyz4wmyw1F-MQI0Av1HdDz4E",
"q4vW4tK4d2u-ody2l3m6_jbmx5ZSRiUO5G7Ip13NJ1w",
"uXiO47XWWlGPDa6q-sJNPsDzLwWLli1427cP6A3C3HU",
"yBoPxiDnZ5aigIv5YzOU112AWGOpUiSCF3Jdx2v6Dt8",
"zOQ6jU7_Ox07qYqIJ7oW216bbQdsmy_AyGq0imnnyAc"
],
"address": {
"_sd": [
"2CviwUz5xL0RYFsPaHYYxUJUx0cOoDXBaIlfsoXrhzo",
"56k05jWNcZ-EpgYhh3cRpDJ8nJEW8-8ZqoIcrXUbpAM",
"7wyr7x39o2SzV7trbuICxwLjMPbiwQvu4xuG7OlALiY",
"Jl8BoyDHMy1yZJAa0DybSBsmBRgXEUOEQ41olpKon8w",
"KsXxsBHYo_owUkbxVUQOFcPth9ejDnAg8caSWAWxGaI",
"Yzluf4m4Ltah0Eshk3CF7iS-q2tRm2iW_UuYicmO9Ks",
"n-jVZHE6d3t85ZCPWa1NBnJMSEt80yOL1JatQueXfwo",
"omAxklUKrr2Ol5ogFY6XgI-Xm4v63UkVb1_OWaXkkEQ",
"wWBkWPeMRuGF3b6KGkBxrxgYckjW3qPDDOtr9PNwCbE"
]
},
"vct": "urn:eu.europa.ec.eudi:pid:1",
"_sd_alg": "sha-256",
"iss": "https://trial.authlete.net",
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"kid": "B8os8dNBM1viONYOzOHfPKJwp3SGlMYKSNACSWuZRqs",
"x": "JJ-7f0AsQ5fIRdqh2rIz1I-6JJAMJ0B533R2no-Fk_A",
"y": "z-1Efw6fdhgdKTFHIXNH29m_TNZcYJC-LCIU6YdPtR8"
}
},
"iat": 1719476855,
"age_equal_or_over": {
"_sd": [
"I8GGGeWArEG00Pqb5_jfBg0hXFwDOY9i4gmOYLju9-Y",
"uHfl4bgM0Wb6a0VHu3YltaMfHduMvHo221UAQBd08XU"
]
}
}
{
"digest": "l5RFyERQPtE-LzkdEPhWyz4wmyw1F-MQI0Av1HdDz4E",
"WyJBMFYzTDlwdzQ3U1BfSk5XYTVFOGhBIiwic3ViIiwiMTAwNCJd": [
"A0V3L9pw47SP_JNWa5E8hA",
"sub",
"1004"
]
}
{
"digest": "IKT04dqgSa3PziIMedmSCNY9z24opA3Fhz-e0m7qWEY",
"WyJLZy1VTFN4N2JlOGRlYm5yMngyVjRnIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd": [
"Kg-ULSx7be8debnr2x2V4g",
"family_name",
"Silverstone"
]
}
{
"digest": "uXiO47XWWlGPDa6q-sJNPsDzLwWLli1427cP6A3C3HU",
"WyJZLUVCUkw0OTJyUm54T3BGaWtUOE1BIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ": [
"Y-EBRL492rRnxOpFikT8MA",
"given_name",
"Inga"
]
}
{
"digest": "zOQ6jU7_Ox07qYqIJ7oW216bbQdsmy_AyGq0imnnyAc",
"WyJreUFTdHg1SWNUaXVfQ3RNdTBDck5RIiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd": [
"kyAStx5IcTiu_CtMu0CrNQ",
"birthdate",
"1991-11-06"
]
}
{
"digest": "uHfl4bgM0Wb6a0VHu3YltaMfHduMvHo221UAQBd08XU",
"WyI4WGJCNGRRZENZbkh6a3BuR094YU1RIiwiMTgiLHRydWVd": [
"8XbB4dQdCYnHzkpnGOxaMQ",
"18",
true
]
}
{
"digest": "IwmjQMwtNNOgVByenkaf1zdrbsR-illZm7zG6aozmMU",
"WyJYM0RBUHdFWTRGaEQ2UUNNSU5idXBnIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd": [
"X3DAPwEY4FhD6QCMINbupg",
"locality",
"Shoshone"
]
}
{
"digest": "7wyr7x39o2SzV7trbuICxwLjMPbiwQvu4xuG7OlALiY",
"WyJ3SDhKTE5tc1dkcVdfZ3Q3RmtQbGpRIiwiZm9ybWF0dGVkIiwiMTE0IE9sZCBTdGF0ZSBId3kgMTI3LCBTaG9zaG9uZSwgQ0EgOTIzODQsIFVTQSJd": [
"wH8JLNmsWdqW_gt7FkPljQ",
"formatted",
"114 Old State Hwy 127, Shoshone, CA 92384, USA"
]
}
{
"digest": "Jl8BoyDHMy1yZJAa0DybSBsmBRgXEUOEQ41olpKon8w",
"WyJEVUJaRXNkWVVPREhoZTJ3TUxFMnpBIiwic3RyZWV0X2FkZHJlc3MiLCIxMTQgT2xkIFN0YXRlIEh3eSAxMjciXQ": [
"DUBZEsdYUODHhe2wMLE2zA",
"street_address",
"114 Old State Hwy 127"
]
}
{
"digest": "wWBkWPeMRuGF3b6KGkBxrxgYckjW3qPDDOtr9PNwCbE",
"WyJ6dXQzd0pnbjN1MExCcTJRNTlXZEFRIiwibG9jYWxpdHkiLCJTaG9zaG9uZSJd": [
"zut3wJgn3u0LBq2Q59WdAQ",
"locality",
"Shoshone"
]
}
{
"digest": "n-jVZHE6d3t85ZCPWa1NBnJMSEt80yOL1JatQueXfwo",
"WyJsVzdXQTkxakRON1RGWkpRRzJNUi1BIiwicG9zdGFsX2NvZGUiLCJDQSA5MjM4NCJd": [
"lW7WA91jDN7TFZJQG2MR-A",
"postal_code",
"CA 92384"
]
}
{
"digest": "Yzluf4m4Ltah0Eshk3CF7iS-q2tRm2iW_UuYicmO9Ks",
"WyJlczdiUHpNeXdNaXJON0tzemtpMFdRIiwiY291bnRyeSIsIlVTQSJd": [
"es7bPzMywMirN7Kszki0WQ",
"country",
"USA"
]
}
{
"digest": "IyHzhcH7i3sdK_6KEIIgCiY3EgC9PTiNscWiwPFKqGk",
"WyJtOEVlUUM2akhIeUlSWGVDQXhERi1nIiwiaXNzdWluZ19hdXRob3JpdHkiLCJVUyJd": [
"m8EeQC6jHHyIRXeCAxDF-g",
"issuing_authority",
"US"
]
}
{
"digest": "6J-ld9Ky-CxTIxjnyF6v3_BKj52GMFCxgt_AJvINsQU",
"WyIxMzhhaHkwMVdvNFFxekZyNFlpSld3IiwiaXNzdWluZ19jb3VudHJ5IiwiVVMiXQ": [
"138ahy01Wo4QqzFr4YiJWw",
"issuing_country",
"US"
]
}
```
You can see that the `cnf.jwk` property in the second verifiable credential
matches the public key embedded in the second key proof.
```json theme={null}
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"kid": "B8os8dNBM1viONYOzOHfPKJwp3SGlMYKSNACSWuZRqs",
"x": "JJ-7f0AsQ5fIRdqh2rIz1I-6JJAMJ0B533R2no-Fk_A",
"y": "z-1Efw6fdhgdKTFHIXNH29m_TNZcYJC-LCIU6YdPtR8"
}
}
```
[RFC_6749]: https://www.rfc-editor.org/rfc/rfc6749.html
[RFC_6749_3_1]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1
[RFC_6749_3_1_2]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1.2
[RFC_6749_3_2]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
[RFC_6749_4_1]: https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1
[RFC_7515_4_1_3]: https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.3
[RFC_7515_4_1_4]: https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.4
[RFC_7515_4_1_6]: https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.6
[RFC_7517]: https://www.rfc-editor.org/rfc/rfc7517.html
[RFC_7519]: https://www.rfc-editor.org/rfc/rfc7519.html
[RFC_7519_4_1_6]: https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6
[RFC_7636]: https://www.rfc-editor.org/rfc/rfc7636.html
[RFC_8392]: https://www.rfc-editor.org/rfc/rfc8392.html
[RFC_8610_G]: https://www.rfc-editor.org/rfc/rfc8610#appendix-G
[RFC_8949]: https://www.rfc-editor.org/rfc/rfc8949.html
[RFC_8949_8]: https://www.rfc-editor.org/rfc/rfc8949#section-8
[RFC_9052]: https://www.rfc-editor.org/rfc/rfc9052.html
[RFC_9053]: https://www.rfc-editor.org/rfc/rfc9053.html
[RFC_9126]: https://www.rfc-editor.org/rfc/rfc9126.html
[RFC_9396]: https://www.rfc-editor.org/rfc/rfc9396.html
[RFC_9449]: https://www.rfc-editor.org/rfc/rfc9449.html
[RFC_9901]: https://www.rfc-editor.org/rfc/rfc9901.html
[W3C_DATA_INTEGRITY]: https://w3c.github.io/vc-data-integrity/
[W3C_DID_DID_URL]: https://www.w3.org/TR/did-core/#dfn-did-urls
[W3C_JOSE_COSE]: https://w3c.github.io/vc-jose-cose/
[W3C_JOSE_COSE_REPO]: https://github.com/w3c/vc-jose-cose
[W3C_VCDM]: https://www.w3.org/TR/vc-data-model/
[W3C_VCDM_1_1]: https://www.w3.org/TR/vc-data-model-1.1/
[W3C_VCDM_2_0]: https://www.w3.org/TR/vc-data-model-2.0/
[ISO_IEC_18013_5]: https://www.iso.org/standard/69084.html
[IANA_COSE]: https://www.iana.org/assignments/cose/cose.xhtml
[OID4VCI]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
[OID4VCI_10_ID1]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html
[OID4VCI_10_ID1_7_2_1_1]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html#section-7.2.1.1
[OID4VCI_10_ID1_7_2_1_2]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html#section-7.2.1.2
[OID4VCI_10_ID1_7_2_1_3]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html#section-7.2.1.3
[OID4VCI_10_FINAL]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html
[OID4VCI_10_FINAL_6_2]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html#section-6.2
[OID4VCI_10_FINAL_8_2]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html#section-8.2
[OID4VCI_10_FINAL_F]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html#appendix-F
[OID4VCI_10_FINAL_F1]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html#appendix-F.1
[OID4VCI_10_FINAL_F2]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html#appendix-F.2
[OID4VCI_10_FINAL_F3]: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-final.html#appendix-F.3
[OID4VCI_PR293]: https://github.com/openid/OpenID4VCI/pull/293
[OIDFED]: https://openid.net/specs/openid-federation-1_0.html
[ATTESTATION_BASED_CLIENT_AUTH]: https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/
[AUTHLETE_CBOR]: https://github.com/authlete/cbor
[AUTHLETE_JAVA_COMMON]: https://github.com/authlete/authlete-java-common
[AUTHLETE_JAVA_COMMON_JAVADOC]: https://authlete.github.io/authlete-java-common/
[AUTHLETE_OID4VCI_DEMO]: https://github.com/authlete/oid4vci-demo
[AUTHLETE_SD_JWT]: https://github.com/authlete/sd-jwt
[CBOR_ZONE]: https://cbor.zone/
[CLIENT_AUTHENTICATION]: https://medium.com/@darutk/oauth-2-0-client-authentication-4b5f929305d4
[CONTACT_US]: https://www.authlete.com/contact/
[CREDENTIAL_PROFILE_COMPARISON]: https://github.com/vcstuff/credential-profile-comparison
[HAIP]: https://vcstuff.github.io/oid4vc-haip-sd-jwt-vc/draft-oid4vc-haip-sd-jwt-vc.html
[JAVA_OAUTH_SERVER]: https://github.com/authlete/java-oauth-server
[POTENTIAL]: https://www.digital-identity-wallet.eu/
[SD_JWT]: https://datatracker.ietf.org/doc/draft-ietf-oauth-selective-disclosure-jwt/
[SD_JWT_VC]: https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/
[CredentialRequestInfo]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/CredentialRequestInfo.html
[CredentialSingleIssueRequest]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/CredentialSingleIssueRequest.html
[CredentialSingleParseResponse]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/CredentialSingleParseResponse.html
[IntrospectionResponse]: https://authlete.github.io/authlete-java-common/com/authlete/common/dto/IntrospectionResponse.html
# Device Flow (OAuth 2.0 Device Authorization Grant)
Source: https://developers.authlete.com/v2/configuration-reference/endpoints/enabling-device-flow
Guide on enabling the device flow for API clients on devices without web browsers using Authlete's authorization server component architecture.
This page is for **Authlete 2.x**. For current (3.0) documentation, see [this page](/protocols-and-flows/advanced-flows/oauth-2-0-device-authorization-grant-device-flow).
## Preface
"Device flow," defined in [RFC 8628 (OAuth 2.0 Device Authorization Grant)](https://tools.ietf.org/html/rfc8628)
, is an authorization flow to issue access tokens for API clients running on devices with no Web browsers and/or that have limited capability for inputting text, with end user consent.
This article describes component architecture of an authorization server using Authlete to support the device flow and instructions to configure Authlete.
> This feature is available in Authlete version 2.1 and later.
## How an authorization server supports the device flow using Authlete API
An authorization server must have the following endpoints and "verification URI" to support the device flow. Authlete provides functions as APIs for implementing them.
* Device authorization endpoint
* Processing a [device authorization request](https://tools.ietf.org/html/rfc8628#section-3.1)
sent from a client
* Authlete API: [/device/authorization](https://docs.authlete.com/#device-authorization-api)
* Token endpoint
* Processing a [device access token request](https://tools.ietf.org/html/rfc8628#section-3.4)
sent from a client
* Authlete API: [POST /auth/token](/api-reference/token-endpoint/process-token-request)
* "Verification URI"
* [Processing a "user\_code" submitted by an end user](https://tools.ietf.org/html/rfc8628#section-3.3)
, and authenticate the user and ask consent if needed
* Authlete API: [/device/verification](https://docs.authlete.com/#device-verification-api)
, [POST /device/complete](/api-reference/device-flow/complete-device-authorization)
## Configuration settings
This section explains settings for enabling the device flow. You have to configure both Authlete service and its client that uses the flow.
## Authlete service
Configure the following settings in Service Owner Console.
| Tab | Item | Value |
| - | - | - |
| Authorization | Supported Grant Types | Enable "**DEVICE\_CODE**" |
| Device Flow | Device Authorization Endpoint | The URL of the device authorization endpoint (e.g. [https://as.example.com/device\_authorization](https://as.example.com/device_authorization)) |
| Device Flow | Verification URI | The value of verification\_uri to be presented to end users (e.g. [https://as.example.com/device](https://as.example.com/device)) |
| Device Flow | Verification URI with Placeholder | The value of verification\_uri\_complete (e.g. [https://as.example.com/device?user\_code=USER\_CODE](https://as.example.com/device?user_code=USER_CODE)) |
| Device Flow | Verification Code Duration | The duration of device\_code and user\_code in seconds (e.g. 600) |
| Device Flow | Polling Interval | The minimum interval between polling requests to the token endpoint in seconds (e.g. 5) |
| Device Flow | User Code Character Set | The character set for user\_code (e.g. BASE20) |
| Device Flow | User Code Character Set | The length of user\_code (e.g. 8) |
## Client settings
Configure the following settings in Client Developer Console.
| Tab | Key | Value |
| - | - | - |
| Basic | Client Type | **PUBLIC** |
| Authorization | Grant Types | Enable "**DEVICE\_CODE**" |
| Authorization | Token Endpoint — Client Authentication Method | Choose "**NONE**" |
> In this article, the device is assumed to be [a non-confidential client](https://tools.ietf.org/html/rfc8628#section-5.6)
> . Choose appropriate values for Client Type and Client Authentication Method if devices in your environment are confidential clients.
## Example
This example shows how an authorization server using Authlete responds to a device authorization request from a client, verifies a user\_code from an end user, and responds token requests from the client.\\
## Device authorization request
Assume the client makes the following "device authorization request" to the authorization server (step #2). (all examples below are folded for readability)
```http highlight={5} theme={null}
POST /device_authorization HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
client_id=...&scope=openid+profile+read
```
The authorization server forwards the request content to Authlete's [/device/authorization](https://docs.authlete.com/#device-authorization-api)
API (step #3, #4).
* Request (step #3 using curl command)
```bash highlight={4} theme={null}
curl -s -X POST $apiUrl/device/authorization \
-u $apiKey:$apiSecret \
-H 'Content-type: application/json' \
-d '{"parameters": "client_id=...&scope=openid+profile+read"}'
```
* Response (step #4)
```json highlight={9-18} theme={null}
{
"resultCode": "A220001",
"resultMessage":
"[A220001] The device authorization request was
processed successfully.",
"action": "OK",
"deviceCode":
"-jxwQ_7MEdR3SqS86bEg1ONUYdwGmSYjqH8eIBZ1c3U",
"responseContent":
"{\"user_code\":\"TXBBPHDZ\",
\"device_code\":
\"-jxwQ_7MEdR3SqS86bEg1ONUYdwGmSYjqH8eIBZ1c3U\",
\"interval\":5,
\"verification_uri_complete\":
\"https://as.example.com/device?user_code=TXBBPHDZ\",
\"verification_uri\":
\"https://as.example.com/device\",
\"expires_in\":600}",
"userCode": "TXBBPHDZ",
"verificationUri":
"https://as.example.com/device",
"verificationUriComplete":
"https://as.example.com/device?user_code=TXBBPHDZ"
}
```
The authorization server extracts the value of "responseContent" and sends it back to the client as a device authorization response (step #5. Details omitted).
## "Verification URI"
### Verifying the user\_code
The client will send "device access token requests" to the server. The requests include the value of "device\_code" extracted from the server's device authorization response (described later in this section).
Concurrently with the token requests, the client will ask the end user to present the value of "user\_code" (extracted from the device authorization response) at "Verification URI" of the authorization server (step #6).
How the code is shown to the user is up to the client. The following example is to present the code along with the value of "verification\_uri" to the end user (based on [an example in RFC 8628](https://tools.ietf.org/html/rfc8628#section-3.3)
).
```text theme={null}
+-----------------------------------------------+
Using a browser on another device, visit:
https://as.example.com/device
And enter the code:
TXBBPHDZ
+-----------------------------------------------+
```
Another example below shows that the client encodes the value of "verification\_uri\_complete", which is included in the device authorization response as well, into QR code and asks the user to scan it (based on [an example in RFC 8628](https://tools.ietf.org/html/rfc8628#section-3.3.1)
).
```text theme={null}
+-------------------------------------------------+
Scan the QR code or, using a browser on another
device, visit: https://as.example.com/device
And enter the code: TXBBPHDZ
+-------------------------------------------------+
```
Once receiving the user\_code from the end user in some way (step #7), the "verification URI" of the authorization server forwards the user\_code to Authlete's [/device/verification](https://docs.authlete.com/#device-verification-api)
API (step #8, #9).
* Request (step #8 using curl command)
```text theme={null}
curl -s -X POST $apiUrl/device/verification \
-u $apiKey:$apiSecret \
-H 'Content-type: application/json' \
-d '{"userCode":"TXBBPHDZ"}'
```
* Response (step #9)
```json theme={null}
{
"resultCode": "A224001",
"resultMessage": "[A224001] The user code is valid.",
"action": "VALID",
"claimNames": [],
"clientId": 1720108316616,
"clientName": "Demo Client",
"scopes": [
{
"defaultEntry": false,
"name": "openid"
},
{
"defaultEntry": false,
"name": "profile"
},
{
"defaultEntry": false,
"name": "read"
}
]
}
```
This response states that the verification in terms of the user\_code was successful. It also includes other details about the client, requested scopes and/or claims etc.
The authorization server will employ these information to ask consent to the end user.
### Completing the verification
The authorization server may authenticate the end user, and present the information of the client and details of the requested access based on the result described above (step #10, #11).
Then the server determines the end user's identifier and properties (e.g. scopes, claims) of the token to be issued, and makes a request to [POST /device/complete](/api-reference/device-flow/complete-device-authorization)
API (step #12, #13).
* Request (step #12 using curl command)
```text theme={null}
curl -s -X POST $apiUrl/device/complete
-u $apiKey:$apiSecret
-H 'Content-type: application/json'
-d '{"userCode":"TXBBPHDZ",
"result":"AUTHORIZED",
"subject":"testuser01"}'
```
* Response (step #13)
```json theme={null}
{
"resultCode": "A241001",
"resultMessage": "[A241001] The API call was processed successfully.",
"action": "SUCCESS"
}
```
On receiving the response above, The authorization server notifies the end user that the verification process is completed (step #14).
## Token request and response
As described before, the client makes "device access token requests" using the value of "device\_code" to the authorization server (step #a). It usually sends the requests multiple times (i.e. polling) until the authorization server completes the verification of the user\_code and provides an access token.
* Request (step #b using curl command)
```text theme={null}
curl -s -X POST $apiUrl/auth/token
-u $apiKey:$apiSecret
-H 'Content-type: application/json'
-d '{"parameters":
"client_id=...
&grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=-jxwQ_7MEdR3SqS86bEg1ONUYdwGmSYjqH8eIBZ1c3U"}'
```
* Response (step #c, before the verification of the user\_code is completed)
```json theme={null}
{
"resultCode": "A242307",
"resultMessage":
"[A242307] The device authorization request has not been authorized yet.",
"action": "BAD_REQUEST",
"grantType": "DEVICE_CODE",
"responseContent":
"{\"error_description\":
\"[A242307] The device authorization request has not been authorized yet.\",
\"error\":\"authorization_pending\",
\"error_uri\":\"https://docs.authlete.com/#A242307\"}"
}
```
* Response (step #c, after the verification of the user\_code is completed)
```json theme={null}
{
"resultCode": "A242002",
"resultMessage":
"[A242002] The token request
(grant_type=urn:ietf:params:oauth:grant-type:device_code) was processed
successfully.",
"accessToken": "ZJHO26vXTC1LIQXm9aYUFnMZd4R599aFA4hLBmH-OlM",
"action": "OK",
"clientId": 1720108316616,
"grantType": "DEVICE_CODE",
"idToken":
"eyJhbGciOiJIUzI1NiJ9.
eyJhdF9oYXNoIjoiZkpNOHhuODlTaVNQVnNsMGFLYnBTQSIsInN1YiI6InRlc3R1
c2VyMDEiLCJhdWQiOiIxNzIwMTA4MzE2NjE2MSIsImlzcyI6Imh0dHBzOi8vYXV0
aGxldGUuY29tIiwiZXhwIjoxNTk2NjE5OTk2LCJpYXQiOjE1OTY1MzM1OTZ9.
OYuGqNbombW_DrSHsm9A07LZWa4UWyV_hSiSAQy-CYI",
"refreshToken": "sliwK3Oa6Pag1c2aGenZALcGZXAP9cIiIu_zjGIdBCI",
"responseContent":
"{\"access_token\":\"ZJHO26vXTC1LIQXm9aYUFnMZd4R599aFA4hLBmH-OlM\",
\"refresh_token\":\"sliwK3Oa6Pag1c2aGenZALcGZXAP9cIiIu_zjGIdBCI\",
\"scope\":\"openid profile read\",
\"id_token\":
\"eyJhbGciOiJIUzI1NiJ9.
eyJhdF9oYXNoIjoiZkpNOHhuODlTaVNQVnNsMGFLYnBTQSIsInN1YiI6InRlc3R1
c2VyMDEiLCJhdWQiOiIxNzIwMTA4MzE2NjE2MSIsImlzcyI6Imh0dHBzOi8vYXV0
aGxldGUuY29tIiwiZXhwIjoxNTk2NjE5OTk2LCJpYXQiOjE1OTY1MzM1OTZ9.
OYuGqNbombW_DrSHsm9A07LZWa4UWyV_hSiSAQy-CYI\",
\"token_type\":\"Bearer\",
\"expires_in\":3600}",
"scopes": [
"openid",
"profile",
"read"
],
"subject": "testuser01"
}
```
The authorization server extracts the value of "responseContent" and sends it back to the client (step #d).
## See also
* [**Device Flow (OAuth 2.0 Device Authorization Grant)**](/protocols-and-flows/advanced-flows/oauth-2-0-device-authorization-grant-device-flow)
describes overview of the device flow and how Authlete supports it.
* [**Sequence Diagrams Templates**](https://www.authlete.com/resources/templates/sequence-diagrams/)
allow you to design the device flow using Authlete.
* [**Authlete API Reference**](https://docs.authlete.com/#device-flow)
provides details of APIs for implementing the device flow in your authorization server.
# Client Authorized by User
Source: https://developers.authlete.com/configuration-reference/client-management/authletes-policy-on-managing-clients-which-have-been-authorized-by-user
Technical information on Authlete's policy for managing authorized clients based on valid or expired access and refresh tokens.
## Overview
An Authlete-powered authorization server can leverage [/client/authorization/get/list](/api-reference/client-management/get-authorized-applications) API to retrieve a list of clients which has been authorized by a specific user.
In order to make the list, Authlete checks its token database to see which clients' tokens are stored. Expiration time for each token doesn't matter i.e. existence of tokens is only considered.
## Details
Authlete provides your authorization server with an API response including information on clients correlated with access tokens and/or refresh tokens which have been stored in Authlete's token database. The following conditions affect existence of tokens in the database.
### Either one of an access token or a refresh token is valid (not expired)
The valid token is surely stored in the database. Thus Authlete provides information on the client correlated with the token.
### Both an access token and a refresh token are expired
Even in such case, Authlete provides information on the client correlated with the expired tokens if they have been still stored (i.e. not been swept) in the token database. Authlete removes such expired tokens when:
* [Accepting /auth/introspection request that contains the access token](/configuration-reference/endpoints/introspection-response-for-expired-access-token)
* [Executing batch removal (cleanup) process](/configuration-reference/tokens-and-claims/authletes-policy-on-sweeping-unused-tokens)
* Accepting explicit removal request, e.g., [/client/authorization/delete](/api-reference/client-management/delete-client-tokens) API
# Client Attributes
Source: https://developers.authlete.com/configuration-reference/client-management/client-attributes
Configuring and utilizing client attributes in OAuth 2.0 for defining client affiliations roles and access controls.
For **Authlete 2.x** documentation, see [2.x version](/v2/configuration-reference/client-management/client-attributes).
## Overview
Client attributes are arbitrary attributes in key-value format and associated on a per-client basis. An authorization server would use the attributes, for example, to define affiliations or roles of clients, to control authentication and authorization flows, and to determine scopes of access tokens and claims in ID tokens to be created. Resource servers would also use the client attributes, resulting from introspection, to make API responses that should be returned to the requesting client.
*Client attributes*
This article describes how to configure the client attributes feature and its use cases.
> This feature is available in Authlete version 2.2 or later.
***
## Configuration
To set client attributes, follow the steps below.
1. Log in to the [Authlete Management Console](https://console.authlete.com/)
2. Click on your Organization name and choose your Service.
3. Navigate to **Client Settings > Basic Settings > Advanced**
4. Scroll down to `Client Attributes` and click the `add` button.
5. Enter values of “Key” and “Value” for the new attribute and click the `Save` button. Note that these values of each attribute are treated as a string.
6. Click `Save Changes` to apply updates.
The following is an example of setting an attribute with "affiliation" as a key and "partner" as a value.

*Configuration of client attributes*
***
## Use cases
You can use client attributes in a variety of situations. Here are some typical use cases.
### Control of Configuration Items in Client Management
Client attributes can be obtained as part of the client information. The following is an example response from Authlete's [GET `/client/get/{clientId}`](/api-reference/client-management/get-client) API.
```
[...]
"attributes": [
{
"key": "affiliation",
"value": "partner"
}
],
[...]
```
Based on this response, the authorization server identifies the client as an external partner application and regulates the items that the client administrator is permitted to configure.
### Control of authorization flows
An authorization server can use client attributes obtained as a response from Authlete's [POST /auth/authorization](/api-reference/authorization-endpoint/process-authorization-request) API to control the subsequent authorization flow. The following is an example of an API response.
```
{
"action": "INTERACTION",
"client": {
"attributes": [
{
"key": "affiliation",
"value": "partner"
}
],
[...]
```
In this case, the authorization server identifies the client as a '`partner`' and informs the user during authentication or consent confirmation that the client requesting access is an external partner application.
### Control of API responses
Authlete makes a token introspection response, that may include client attributes. The following is an example response from the [POST /auth/introspection](/api-reference/introspection-endpoint/process-introspection-request) API.
```
{
"action": "OK",
"clientAttributes": [
{
"key": "affiliation",
"value": "partner"
}
],
[...]
}
```
Based on these attributes, a resource server would determine that the client making the API request with the access token is an external partner application and control the API response.
***
## See also
* [Authlete 2.2.4 Release notes](/release-notes/server-v2-2/server-v2-2-march-2021)
* [com.authlete.common.dto.Client](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Client.html)
# Token Revocation upon Client Deletion
Source: https://developers.authlete.com/configuration-reference/client-management/token-management-policy-when-deleting-clients
When deleting a client, Authlete automatically revokes access tokens and refresh tokens issued to the client.
When deleting a client, Authlete automatically revokes access tokens and refresh tokens issued to the client. Therefore, a service owner does not have to call token management APIs to revoke tokens.
# Audit Logs
Source: https://developers.authlete.com/configuration-reference/security/audit-logs
Audit logs in Authlete provide a detailed record of security-related events and system activities performed by users and system actors.
# Audit Logs User Guide
Audit logs in Authlete provide a detailed record of security-related events and system activities.\
They help administrators understand what happened, who performed it, and when and where it occurred.
***
## Overview
Authlete audit logs capture operations such as creating or deleting organizations, services, clients, tokens, and other key events.\
All actions — whether performed directly by a **user** or automatically by a **system actor (service principal)** — are recorded in a single, unified audit trail.
***
## User Types
Each audit log entry identifies the user responsible for the action.
| User Type | Description |
| - | - |
| **User** | A human user performing actions through the Authlete Console or APIs. |
| **Service Principal** | A system-level or non-human actor that performs automated or background operations on behalf of the platform. Service principals represent trusted internal services such as migration or cleanup processes. |
Audit logs clearly distinguish between user and service principal activities, ensuring visibility into both human and automated operations.
***
## Viewing Audit Logs
Audit logs can be viewed in the Authlete Management Console or accessed via the Authlete API.
### How to View Audit Logs in the Management Console
1. Log in to the [Authlete Management Console](https://console.authlete.com/)
2. Choose your Organization name.
3. Navigate to **Organization Settings > Audit Logs**
Once you reach the logs page, you can view the following information:
| Log Information | Description |
| - | - |
| Event Type | The category of the recorded event |
| Status | Indicates whether the event completed successfully or failed |
| Date | Timestamp of the event |
| User | The user or service principal that performed the action |
***
## Log Filtering
Audit logs can be filtered to help you locate specific entries quickly.
* **Event Type Filter:** Show only a specific category of events (e.g., `Security Login`, `User Delete`, `Client Update`).
* **Status Filter:** Display only completed or failed events.
* **Date Range Filter:** View logs within a selected time period.
* **Free-text Search:** Find logs containing specific terms or identifiers.

*Audit Logs Filters*
***
## Understanding Log Entries
When you select an event, the log entry is displayed in JSON format showing key information about the recorded activity.

*Audit Log Entry Example*
Each audit log entry may include the following fields:
| Log Field | Type | Description |
| - | - | - |
| Event | String | The type of event recorded |
| Status | String | Status of completion (`success` or `fail`) |
| Timestamp | String | Time of the event |
| Cluster | String | The environment or server group targeted (e.g., `us.authlete.com`, `jp.authlete.com`, `login.authlete.com`) |
| Path | String | The API endpoint or system path involved |
| Remote Address | String | IP address of the user performing the action |
| User Agent | String | The web browser or system client used to perform the action |
| User Type | String | Indicates whether the actor is a `user` or `service_principal` |
| User Identifier | String | The unique identifier of the actor (e.g., user email or service principal ID) |
| Details | Object | Additional context such as Organization ID, Service ID, or other relevant metadata |
***
## User Event Types
User event types describe activities performed directly by human users through the Authlete Console or APIs.
| Event Name | Description |
| - | - |
| Organization Create | An organization was created |
| Organization Delete | An organization was deleted |
| Organization Token Create | A new organization token was created |
| Organization Token Rotate | An organization token was replaced |
| Organization Token Delete | An organization token was deleted |
| Service Token Create | A service token was successfully created |
| Service Token Rotate | A service token was replaced with a new token |
| Service Token Delete | A service token was deleted |
| Service Create | A new service was created |
| Service Delete | A service was deleted |
| Service Update | Service settings were updated |
| Client Create | A client was created |
| Client Delete | A client was deleted |
| Client Update | Client settings were updated |
| Security Login | A user logged in through OIDC authentication |
| Security Logout | A user logged out |
| Security Register | A user registered a new account |
| Security Verify Email | A user verified their email address |
| Security Login Mfa | A user logged in using Multi-Factor Authentication |
| Security Login Oidc | A user logged in using Single Sign-On (SSO) or external identity |
| Security Mfa Create | A user enabled Multi-Factor Authentication |
| Security Mfa Delete | A user removed Multi-Factor Authentication |
| Security Password Update | A user updated their password |
| User Create | A user account was created |
| User Delete | A user account was deleted |
| User Invite | A user was invited to an organization |
| User Invite Accepted | A user accepted an invitation |
| User Invite Decline | A user declined an invitation |
| User Invite Revoke | A user invitation was revoked |
| User Admin Set | A user was assigned the System Administrator role |
| User Admin Unset | The System Administrator role was removed from a user |
| Permission Change | User access permissions were updated for an Organization, Service, or Client |
***
## Service Principals in Audit Logs
Some audit entries are generated automatically by internal Authlete services or background processes.\
These system-level actors are called **Service Principals**.
Service principals represent trusted services that perform operational or maintenance tasks on behalf of Authlete.\
Their actions appear in audit logs the same way user actions do, but with:
* **User Type:** a Service Principal
* **User Identifier:** The assigned ID or alias of the system actor (for example, `migration-service@system.authlete.com`)
This ensures full visibility of both human and automated activities within a single, consistent audit view.
***
## Service Principal Event Types
Service principal event types record automated actions performed by system-level or background processes.\
These events reflect non-human activity such as maintenance or data migration tasks performed by trusted Authlete services.
| Event Name | Description |
| - | - |
| Migration Service | Automated background service that processes or transfers data between systems as part of migration operations. |
| Batch Cleanup Service | Internal background service that performs scheduled maintenance or cleanup of expired tokens and resources. |
These event types appear alongside user events within the same audit log interface.\
Each entry lists its **User Type** as a Service Principal to indicate that the operation was automated.
***
## Summary
Authlete audit logs provide a unified record of all activity — whether performed by users or by automated system actors.\
By distinguishing between **user** and **service principal** events, Authlete ensures complete transparency, accountability, and traceability across the platform.
***
# Creating an OAuth/OpenID Connect Service
Source: https://developers.authlete.com/configuration-reference/service-settings/creating-an-oauth-openid-connect-service
The basic elements of an OAuth 2.0/OpenID Connect service.
# OAuth 2.0 Endpoints
An **[OAuth 2.0][6749]** **[authorization server][6749_11]** must implement two endpoints in order to issue an **[access token][6749_5]**: an **[authorization endpoint][6749_31]** and a **[token endpoint][6749_32]**. These are called **OAuth 2.0 endpoints**. RFC 6749, the specification of OAuth 2.0, describes how the two endpoints should behave.
An **[OpenID Connect][oidc]** **[OpenID Provider][oidc_terms]** extends the same two OAuth 2.0 endpoints in order to issue an **[ID Token][idtoken]**. [OpenID Connect Core 1.0][core] describes additional requirements for the two endpoints.
There are other endpoints which are defined by the specifications, but they are optional. The minimum implementation of OAuth 2.0 and OpenID Connect generally comprises the authorization endpoint and the token endpoint. If, however, you choose not to support some of the grant types defined in [RFC 6749][6749], you may only need to implement one of the two endpoints. For example, the OAuth 2.0 [Implicit Grant][6749_42] requires only the authorization endpoint.
The primary purpose of Authlete is to provide Web APIs which enable developers to implement OAuth 2.0 endpoints very easily. In fact, it is possible to use Authlete's default OAuth 2.0 endpoint implementations and write no code at all.
# Client Application Management
The specifications define the role of third-party **client applications** that access protected resource endpoints (see below) on behalf of end users. Typically, a service allows third-party developers to register their client applications.
Authlete provides Web APIs to manage (create / read / update / delete) client applications. You may directly call the Web APIs or build tools that call the Web APIs, but the simplest way is to use Authlete's **[Developer Console][cd_console]**. Developers of client applications for your service can also manage their client applications using the Developer Console.
# Protected Resource Endpoints
A Web service provides Web APIs through which its client applications access end-users' data. In the OAuth 2.0 context, these APIs are called **[protected resource endpoints][pre]**.
A client application accesses a protected resource endpoint with an access token which has been issued from either the authorization endpoint or the token endpoint. The protected resource endpoint implementation must verify that the access token has the correct privileges to access the protected resource before acting on the request.
Authlete provides an **introspection API** to retrieve information about an access token. Implementations of protected resource endpoints use this API to determine the end user and permissions associated with the token and allow or deny the request accordingly.
[oidc]: https://openid.net/connect/
[core]: https://openid.net/specs/openid-connect-core-1_0.html
[oidc_terms]: https://openid.net/specs/openid-connect-core-1_0.html#Terminology
[idtoken]: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
[6749]: https://tools.ietf.org/html/rfc6749
[6749_11]: https://tools.ietf.org/html/rfc6749#section-1.1
[6749_31]: https://tools.ietf.org/html/rfc6749#section-3.1
[6749_32]: https://tools.ietf.org/html/rfc6749#section-3.2
[6749_42]: https://tools.ietf.org/html/rfc6749#section-4.2
[6749_5]: http://tools.ietf.org/html/rfc6749#section5
[pre]: /developers/definitive_guide/protected_resource/
[cd_console]: /developers/cd_console
[so_console]: /developers/so_console
# Service Configurations
Source: https://developers.authlete.com/configuration-reference/service-settings/service-management
This page documents all the services settings available to configure Authlete service for your Authorization Server. Easily set and manage these configurations through the Authlete Management Console or directly via the Authlete APIs.
# Service Settings
* [Basic](#basic-settings)
* [Endpoints](#endpoints-global-settings)
* [Global Settings](#endpoints-global-settings)
* [Authorization](#endpoints-authorization)
* [Token](#endpoints-token)
* [General](#endpoints-general)
* [Federation](#endpoints-federation)
* [Advanced](#endpoints-advanced)
* [Token and Claims](#tokens-and-claims-access-token)
* [Access Token](#tokens-and-claims-access-token)
* [Refresh Token](#tokens-and-claims-refresh-token)
* [ID Token](#tokens-and-claims-id-token)
* [Claims](#tokens-and-claims-claims)
* [Advanced](#tokens-and-claims-advanced)
* [Key Management](#key-management-hsm)
* [JWK Set](#key-management-jwk-set)
* [HSM](#key-management-hsm)
* [Metadata](#metadata)
* [Verifiable Credentials](#verifiable-credentials)
This page documents all the services settings available to configure Authlete service for your authorization server. You can manage these configurations through the [Authlete Management Console](https://console.authlete.com) or via the [Authlete APIs](https://docs.authlete.com/).
## Basic Settings
### Service Name
A name of this service. Up to 100 Unicode characters.
### Service Description
A description of this service. Up to 200 Unicode characters.
### Issuer Identifier
An identifier of this service as an OpenID Provider. Authlete uses it as the value of the "iss" claim in [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) issued by this service. Additionally, if the Authorization Server implements a discovery endpoint (OpenID Connect Discovery 1.0, [4. Obtaining OpenID Provider Configuration Information](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig)) using Authlete's [Service Configuration API](/api-reference) (`GET /api/{serviceId}/service/configuration`), Authlete uses it as the value of the "issuer" property included in the response from the endpoint.
The default value is "[https://authlete.com](https://authlete.com)." Change this value in a production environment. This must be an URL starting with "https\://" and excluding the query part and the fragment part.
The URL of a discovery endpoint must be `{Token-Issuer-Identifier}/.well-known/openid-configuration`, so be careful when determining the value of the token issuer identifier if your service exposes a discovery endpoint. See OpenID Connect Discovery 1.0, [4. Obtaining OpenID Provider Configuration Information](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig) for details.
### Supported Custom Client Metadata
Names of custom client metadata that this service supports. Some names, such as `client_name`, are reserved and not supported.
### Service Attributes
Attributes of this service. You can associate arbitrary key-value pairs with the service for any purpose. The associated attributes are embedded in responses of some Authlete APIs (e.g., `/api/auth/introspection` API). In most cases, they are embedded as the value of `serviceAttributes`, but in cases where a response contains `service`, the attributes are embedded as the value of `attributes` in the `service` JSON object.
The attributes are encrypted and stored in Authlete's database.
### Service Access Tokens
Service Access Tokens grant your server access to call Authlete APIs for a specific service. Use this token alongside your Service ID and API server URL in your Authorization Server or Resource Server implementation.
Permission customization allows tokens to be scoped based on their use case.
#### Token Name
A human-readable identifier used to distinguish tokens in the Service Access Tokens list.
#### Expiration Date
Specifies when the token will expire and become invalid.
* If set, the token cannot be used after the specified date.
* If not set, the token does not expire.
#### Token Value
The token value is displayed only once when the token is created or rotated.
* It must be copied and stored securely at creation time.
* The plain token value cannot be retrieved again after this step.
#### Rotation
Tokens can be rotated to generate a new value.
* Rotation invalidates the previous token value.
* The new token value is displayed once and must be copied and saved.
#### Permission Presets
Pre-configured permission sets aligned to standard OAuth roles.
| Preset | Description |
| - | - |
| Authorization Server | Grants access to all Authlete API endpoints required for a standard Authorization Server implementation. |
| Admin Authorization Server | Full administrative control over the service. |
| Resource Server | Minimal access required to validate access tokens. |
#### Custom Permissions
Permissions can be explicitly selected when creating a token. Use custom permissions when the predefined presets do not match your use case.
To learn more about Service Access Tokens and how to use them, see [Service Access Tokens](/deployment-and-operations/access-control/service-access-tokens).
## Endpoints: Global Settings
### Supported Grant Types
Grant types supported by this service. This configuration item indicates which flows defined in RFC 6749 are supported. If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `grant_types_supported` property included in the response from the endpoint.
Client applications can use only the flows enabled in this setting. Using an unsupported flow results in an error.
**AUTHORIZATION\_CODE** is a flow to get issued an authorization code at the authorization endpoint and exchange it for an access token at the token endpoint. Details are described in RFC 6749, [4.1. Authorization Code Grant](https://tools.ietf.org/html/rfc6749?#section-4.1). This is the most common flow.
**IMPLICIT** is a flow to get issued an access token directly at the authorization endpoint. Details are described in RFC 6749, [4.2. Implicit Grant](https://tools.ietf.org/html/rfc6749?#section-4.2). This flow is used, for example, to issue access tokens directly to JavaScript applications that run in web browsers.
**PASSWORD** is a flow to get issued an access token by presenting a user's credentials (ID & password) at the token endpoint. Details are described in RFC 6749, [4.3. Resource Owner Password Credentials Grant](https://tools.ietf.org/html/rfc6749?#section-4.3). As stated in the specification, use this flow only when other flows cannot be used for some reason. If you don't have a clear reason, disable this flow in a commercial environment.
**CLIENT\_CREDENTIALS** is a flow to get issued an access token by presenting a client application's credentials (client ID and client secret) at the token endpoint. Details are described in RFC 6749, [4.4. Client Credentials Grant](https://tools.ietf.org/html/rfc6749?#section-4.4). Access tokens issued by this flow are not associated with any user. If you don't have a clear reason to issue such access tokens, disable this flow in a commercial environment.
**REFRESH\_TOKEN** is a flow to get a new access token by presenting a refresh token at the token endpoint. Details are described in RFC 6749, [6. Refreshing an Access Token](https://tools.ietf.org/html/rfc6749?#section-6). If this flow is disabled, refresh tokens are not issued.
\[cf.] [Diagrams And Videos Of All The OAuth 2.0 Flows](https://medium.com/@darutk/diagrams-and-movies-of-all-the-oauth-2-0-flows-194f3c3ade85)
**CIBA** (TBD)
**DEVICE\_CODE** (TBD)
**TOKEN\_EXCHANGE** (TBD)
**JWT\_BEARER** (TBD)
**PRE\_AUTHORIZED\_CODE** (TBD)
### Supported Response Types
Supported combinations of tokens issued from the authorization endpoint. If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `response_types_supported` property included in the response from the endpoint.
In RFC 6749, what can be issued at a time at the authorization endpoint is either an authorization code or an access token, but the additional specification of [OAuth 2.0 Multiple Response Type Encoding Practices](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html) has enabled issuing any combination of an authorization code, an access token, and an ID token. Also, an option to issue nothing (none) has been added.
**NONE**, **CODE**, **TOKEN**, and **ID\_TOKEN** mean that "none", "code", "token", and "id\_token" can be independently specified as a value of the `response_type` parameter of an authorization request, respectively.
**CODE\_TOKEN** means that the combination of "code" and "token" (in no particular order) can be specified as the value of the `response_type` parameter. Likewise, **CODE\_ID\_TOKEN** represents the combination of "code" and "id\_token", **ID\_TOKEN\_TOKEN** represents the combination of "id\_token" and "token", and **CODE\_ID\_TOKEN\_TOKEN** represents the combination of "code", "id\_token", and "token".
Client applications cannot use combinations disabled here as the value of the `response_type` request parameter.
If you don't have a clear reason, leave all the combinations enabled (it is up to you whether to enable or disable "none").
\[cf.] [Diagrams of All The OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660)
### Error Details
### Include Error Description
If enabled, error responses from your authorization server will include the `error_description` response parameter.
`error_description` is a standard response parameter defined in RFC 6749, but its content depends on each authorization server implementation. On error cases, Authlete sets an error description to the `error_description` response parameter. The content of the description may either be generic or Authlete-specific. In either case, it contains an Authlete-specific result code.
If you want to hide the information in a production environment, disable this setting. However, note that in that case, error information returned to client applications will become very simple (e.g., "error=invalid\_request" only), so it may take longer for client application developers to find error causes.
### Include Error URI
If enabled, error responses from your authorization server will include the `error_uri` response parameter.
`error_uri` is a standard response parameter defined in RFC 6749, but its content depends on each authorization server implementation. On error cases, Authlete sets a URI to the `error_uri` response parameter, which points to the page that contains the list of error descriptions. The page exists on Authlete's website. If you want to hide the information in a production environment, disable this setting.
## Endpoints: Authorization
### Authorization Endpoint URL
An URL of the authorization endpoint. If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `authorization_endpoint` property included in the response from the endpoint.
[OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) states the `authorization_endpoint` property is REQUIRED. Therefore, if you want to comply with the specification completely, set a proper value to this configuration item.
### Proof Key for Code Exchange (PKCE)
### Require PKCE
**\[Enabled recommended]** This configuration item determines whether to require the `code_challenge` request parameter as a mandatory parameter in the authorization code flow.
If enabled, authorization requests using the authorization code flow without the `code_challenge` request parameter are all rejected.
If disabled, the `code_challenge` request parameter becomes optional, but even in this case, if an authorization request includes the request parameter, it is handled as the specification defines. As a result, the token request that corresponds to the authorization request with the `code_challenge` request parameter has to include a `code_verifier` request parameter.
The `code_challenge` is defined in [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth Public Clients). This specification is a countermeasure for the authorization code interception attack. If your system allows smartphone applications to use the authorization code flow, the `code_challenge` request parameter is almost mandatory from a security standpoint.
If enabled, your system will become more secure. However, keep in mind that client application developers are required to do additional work.
If you don't have a reason like "we have to support existing client applications that don't support PKCE," especially if you build a new service and develop a new client application, "Required" is recommended in a production environment.
### Require S256 for Code Challenge Method
(TBD)
\[cf.] [Proof Key for Code Exchange (RFC 7636)](/protocols-and-flows/protocol-extensions/proof-key-for-code-exchange-pkce)
### Authorization Response Duration
The duration of authorization response [JWTs](https://tools.ietf.org/html/rfc7519) in seconds.
[Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)](https://openid.net/specs/openid-financial-api-jarm.html) defines the following new values for the `response_mode` request parameter:
* `query.jwt`
* `fragment.jwt`
* `form_post.jwt`
* `jwt`
If one of these is specified as the response mode, response parameters from the authorization endpoint will be packed into a JWT. This value is used to compute the value of the `exp` claim of the JWT. The maximum value is 999999999999999 seconds (about 31.7 million years).
### Issuer Identification Response Parameter
If **Suppress** is selected, the authorization server does not include the `iss` response parameter in authorization responses.
"OAuth 2.0 Authorization Server Issuer Identifier in Authorization Response" has defined a new authorization response parameter, `iss`, as a countermeasure for a certain type of mix-up attacks.
The specification requires that the `iss` response parameter always be included in authorization responses unless JARM (JWT Secured Authorization Response Mode) is used.
By switching this configuration item, developers of client applications can experiment with the mix-up attack and the effect of the `iss` response parameter.
Note that you should not select **Suppress** in a production environment unless there are special reasons for it.
### Loopback Redirection URI
This is a flag indicating whether the port number component of redirection URIs can be variable when the host component denotes loopback.
When **Variable** is selected, if the host component of a redirection URI specified in an authorization request denotes loopback (to be precise, when the host component is `localhost`, `127.0.0.1`, or `::1`), the port number component is ignored when the specified redirection URI is compared to pre-registered ones. This behavior is described in [7.3. Loopback Interface Redirection](https://www.rfc-editor.org/rfc/rfc8252.html#section-7.3) of [RFC 8252 OAuth 2.0 for Native Apps](https://www.rfc-editor.org/rfc/rfc8252.html).
[3.1.2.3. Dynamic Configuration](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1.2.3) of [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) states:
***"If the client registration included the full redirection URI, the authorization server MUST compare the two URIs using simple string comparison as defined in \[RFC3986] Section 6.2.1."***
Also, the description of `redirect_uri` in [3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) of [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) states:
***"This URI MUST exactly match one of the Redirection URI values for the Client pre-registered at the OpenID Provider, with the matching performed as described in Section 6.2.1 of \[RFC3986] (Simple String Comparison)."***
These "Simple String Comparison" requirements are preceded by this flag. That is, even when the conditions described in RFC 6749 and OpenID Connect Core 1.0 are satisfied, the port number component of loopback redirection URIs can be variable when this flag is true (**Variable**).
[8.3. Loopback Redirect Considerations](https://www.rfc-editor.org/rfc/rfc8252.html#section-8.3) of [RFC 8252](https://www.rfc-editor.org/rfc/rfc8252.html) states:
> *`"While redirect URIs using localhost (i.e., http://localhost:{port}/{path}) function similarly to loopback IP redirects described in Section 7.3, the use of localhost is NOT RECOMMENDED. Specifying a redirect URI with the loopback IP literal rather than localhost avoids inadvertently listening on network interfaces other than the loopback interface. It is also less susceptible to client-side firewalls and misconfigured host name resolution on the user's device."`*
However, Authlete allows the port number component to be variable in the case of `localhost`, too. It is left to client applications whether they use `localhost` or a literal loopback IP address (`127.0.0.1` for IPv4 or `::1` for IPv6).
Section 7.3 and Section 8.3 of [RFC 8252](https://www.rfc-editor.org/rfc/rfc8252.html) state that loopback redirection URIs use the `http` scheme, but Authlete allows the port number component to be variable in other cases (e.g., in the case of the `https` scheme), too.
Because FAPI 1.0 Part 1 [Section 7.5. Native Apps](https://openid.net/specs/openid-financial-api-part-1-1_0-final.html#native-apps) explicitly states:
*"shall not support Loopback Interface Redirection"*
the port number component of loopback redirection URIs cannot be variable in the context of FAPI, even if **Variable** is selected.
### Supported Locales
A list of display languages supported on the authorization page of your authorization server. Technically, this is a list of [language tags](https://tools.ietf.org/html/rfc5646#section-2) ([RFC 5646](https://tools.ietf.org/html/rfc5646)) that the authorization endpoint recognizes as the value of the `ui_locales` parameter ([OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)). If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `ui_locales_supported` property included in the response from the endpoint.
A client application may specify display languages for the authorization page by using the `ui_locales` request parameter. However, it is not assured that the authorization server honors the request parameter and changes the display language accordingly. Even if the authorization server completely ignores the `ui_locales` parameter, it is not a specification violation. Probably, most authorization server implementations recognize the Accept-Language HTTP header but don't recognize the `ui_locales` request parameter.
The value of the `ui_locales` parameter in an authorization request is included in a response from Authlete's `/api/auth/authorization` API as the value of the `uiLocales` property. However, languages that are not listed in this configuration item will never be included in the `uiLocales`. For example, even if an authorization request includes `ui_locales=ja`, if "ja" is not listed in this configuration item, "ja" will not appear in the `uiLocales` property in the response from `/api/auth/authorization` API.
If your authorization endpoint implementation ignores the `ui_locales` request parameter, you can leave this configuration item empty.
### Supported Display Types
A list of display types supported on the authorization page of your authorization server. Technically, this is a list of valid values that the authorization endpoint recognizes as the value of the `display` parameter ([OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)). If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `display_values_supported` property included in the response from the endpoint.
**PAGE** is the default value to request the entire drawing area of the user agent to be used. If this display type is checked, `display=page` can be included in an authorization request. If this display type is disabled, Authlete regards all authorization requests that don't include the `display` request parameter explicitly as invalid requests. Therefore, don't disable this display type.
**POPUP** is the value to request a popup to be used. If this display type is checked, `display=popup` can be included in an authorization request.
**TOUCH** is the value to request a page suitable for touch devices. If this display type is checked, `display=touch` can be included in an authorization request.
**WAP** is the value to request a page suitable for feature phones. If this display type is checked, `display=wap` can be included in an authorization request.
A client application may specify a display type for the authorization page by using the `display` request parameter. However, it is not assured that the authorization server honors the request parameter and changes the display type accordingly. Even if the authorization server completely ignores the `display` parameter, it is not a specification violation.
If a value that is not checked in this configuration item is used as the value of the `display` request parameter, the authorization server returns an error.
The value of the `display` parameter in an authorization request is included in a response from Authlete's `/api/auth/authorization` API as the value of the `display` property. If the authorization request does not include the `display` parameter, the value of the `display` property in the response from `/api/auth/authorization` API becomes the default value, **PAGE**.
If you don't have a clear reason, leave all the display types checked.
### Supported Authentication Context Class References
Authentication context class references supported for user authentication at the authorization endpoint. If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `acr_values_supported` property included in the response from the endpoint.
A client application may specify authentication contexts it wants to be satisfied when the authorization server processes an authorization request. The means to specify authentication contexts are (1) the `acr_values` request parameter ([OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)), (2) the `acr` claim in the `claims` request parameter ([OpenID Connect Core 1.0, 5.5. Requesting Claims using the "claims" Request Parameter](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter)), and (3) the `default_acr_values` metadata ([OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata)) of the client application.
Even if authentication contexts are specified by the means above, values not listed in this configuration item will be dropped on Authlete's side. For example, even if an authorization request includes `acr_values=my_acr`, `my_acr` won't be included in the `acrs` property in the response from Authlete's `/api/auth/authorization` API if `my_acr` is not included in this configuration item.
If you do not plan to implement an authorization server that refers to the value of specified authentication contexts and changes its behavior accordingly, you can leave this configuration item empty.
The [UK Open Banking](https://www.openbanking.org.uk/) defines at least two authentication contexts: "urn:openbanking:psd2:sca" and "urn:openbanking:psd2:ca".
### Request Object
If **Require** is selected, authorization requests to this service must always specify a request object by using the `request` or `request_uri` parameter.
If **Require** is selected here and **JAR compatible** is selected in **Request Object Processing**, this service behaves as if `require_signed_request_object` server metadata is `true`. See the specification of JAR (JWT Secured Authorization Request) for details about the server metadata.
### Request Object Processing
If **Enable JAR Compatibility** is selected, request objects are processed based on the rules defined in JAR (JWT Secured Authorization Request). If not, the rules defined in OpenID Connect Core 1.0 apply for backward compatibility.
Differences between JAR rules and OIDC Core 1.0 rules are as follows:
1. JAR requires that request objects be always signed.
2. JAR prohibits referring to request parameters outside the request object. As a result, when JAR rules apply, parameters outside the request object and parameters inside the request object are not merged.
3. OIDC Core 1.0 requires that the `response_type` query/form parameter be present outside the request object even when the request object contains `response_type`. On the contrary, when JAR rules apply, the `response_type` query/form parameter outside the request object is no longer mandatory. However, instead, it means JAR requires that `response_type` be always included in the request object.
4. OIDC Core 1.0 requires that an OIDC request come with the `scope` query/form parameter, including `openid`, even when the request object contains `scope`. On the contrary, when JAR rules apply, the `scope` query/form parameter outside the request object is no longer mandatory. Note that the request object must contain `scope` including `openid` so that the authorization request can be regarded as an OIDC request.
If **Enable JAR Compatibility** is selected here and **Mandatory** is selected in **Request Object**, this service behaves as if `require_signed_request_object` server metadata is `true`. See the specification of JAR for details about the server metadata.
### nbf Claim
This setting is intended to facilitate smooth migration from FAPI 1.0 ID2 to 1.0 Final without breaking live systems. If **Require** is off, even when an authorization request is regarded as a FAPI-Part2 request, the `nbf` claim in the request object used in the authorization request is treated as optional.
> The final version of the Financial-grade API (FAPI) was approved in January 2021. Part 2 introduec a new requirement for request object lifetimes: they must contain an `nbf` claim, and the duration (`exp - nbf`) must not exceed 60 minutes. If an authorization request is treated as a FAPI-Part2 request, its request object must include an `nbf` claim; otherwise, the authorization server will reject it. This setting allows for backward compatibility when needed.
### Supported Authorization Details Types
Supported values of the `type` field of each element in `authorization_details`. Details are defined in *OAuth 2.0 Rich Authorization Requests*.
### Encryption in Front Channel
If **Require** is selected, encryption of request objects is required when the request object is passed through the front channel.
The configuration of this property does not affect the processing of request objects at the Pushed Authorization Request Endpoint, which is defined in [OAuth 2.0 Pushed Authorization Requests](https://datatracker.ietf.org/doc/draft-ietf-oauth-par/). Unencrypted request objects are accepted at the endpoint even if this property is set to **Require**.
Note that, even if **Require** is not selected, encryption of the request object is required if the client's **Request Object Encryption In Front Channel** is set to **Require**.
### Encryption Algorithm Match
If **Require** is selected, the JWE `alg` of an encrypted request object must match the `request_object_encryption_alg` client metadata of the client that sent the request object.
Note that, even if **Require** is not selected, the match is required if the client's **Request Object Encryption Algorithm Match** is set to **Require**.
### Encryption Encoding Algorithm Match
If **Require** is selected, the JWE `enc` of an encrypted request object must match the `request_object_encryption_enc` client metadata of the client that sent the request object.
Note that, even if **Require** is not selected, the match is required if the client's **Request Object Encryption Encoding Algorithm Match** is set to **Require**.
### Audience Validation
If **Require** is selected, Authlete checks whether the `aud` claim of a request object matches the issuer identifier of this service and raises an error when they are different. On the other hand, if **Require** is not selected, Authlete does not check the value of the `aud` claim.
Both [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) and [RFC 9101](https://www.rfc-editor.org/rfc/rfc9101.html) state that the `aud` claim should match or include the issuer identifier of the server. If you want to treat the statement as a mandatory requirement, please select **Require**.
Depending on the category of the official conformance suite, **Require** has to be selected here.
## Endpoints: Token
### Token Endpoint URL
An URL of the token endpoint. If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "token\_endpoint" property included in the response from the endpoint.
If a JWT-based client authentication method is used at the token endpoint (= if a token request includes "client\_assertion=JWT"), the value of the "aud" claim of the JWT is required to be the URL of the token endpoint. Therefore, set the correct value if the token endpoints supports CLIENT\_SECRET\_JWT and PRIVATE\_KEY\_JWT.
### Supported Client Authentication Methods
Client authentication methods supported at the token endpoint. If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `token_endpoint_auth_methods_supported` property included in the response from the endpoint.
If there are client applications whose client type (RFC 6749, [2.1. Client Types](https://tools.ietf.org/html/rfc6749#section-2.1)) is "confidential," at least one client authentication method except NONE must be supported.
**NONE** allows token requests without client authentication. If there are client applications whose client type is "public," NONE has to be supported.
**CLIENT\_SECRET\_BASIC** is a client authentication method where a client ID and a client secret are embedded in the Authorization header in the format of Basic Authentication. This client authentication method is mentioned in RFC 6749. The specification says authorization servers must support this client authentication method.
**CLIENT\_SECRET\_POST** is a client authentication method where a client ID and a client secret are embedded in the request body. This client authentication method is also mentioned in RFC 6749 in addition to CLIENT\_SECRET\_BASIC. As CLIENT\_SECRET\_BASIC is regarded as more secure than CLIENT\_SECRET\_POST, authorization servers may adopt a security policy that allows CLIENT\_SECRET\_BASIC but does not allow CLIENT\_SECRET\_POST.
**CLIENT\_SECRET\_JWT** is a client authentication method defined in [RFC 7523](https://tools.ietf.org/html/rfc7523) that uses a JWT with a symmetric signature algorithm. See OpenID Connect Core 1.0, [9. Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) for details.
**PRIVATE\_KEY\_JWT** is a client authentication method defined in [RFC 7523](https://tools.ietf.org/html/rfc7523) that uses a JWT with an asymmetric signature algorithm. See OpenID Connect Core 1.0, [9. Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) for details.
**TLS\_CLIENT\_AUTH** is a client authentication method that uses a client certificate. Details are described in [OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens](https://datatracker.ietf.org/doc/draft-ietf-oauth-mtls/), 2.1. PKI Mutual TLS OAuth Client Authentication Method.
**SELF\_SIGNED\_TLS\_CLIENT\_AUTH** is a client authentication method that uses a self-signed client certificate. Details are described in [OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens](https://datatracker.ietf.org/doc/draft-ietf-oauth-mtls/), 2.2. Self-Signed Certificate Mutual TLS OAuth Client Authentication Method.
To support the TLS-based client authentication methods, the implementation of the token endpoint of the front-end authorization server must extract a client certificate from a token request and pass it to Authlete's `/api/auth/token` API as the value of the `clientCertificate` request parameter.
**ATTEST\_JWT\_CLIENT\_AUTH**
(TBD)
### OpenID on Refresh
If **Require offline\_access** is selected, the `openid` scope is dropped from a new access token issued by the refresh token flow unless the presented refresh token contains the `offline_access` scope.
### Client ID Omission
Do not allow Client ID Omission in Authlete 3.0. This feature is available only in some scenarios in Authlete 2.3 and does not work correctly in Authlete 3.0.
### Client ID
If **Require** is selected, the service rejects token requests that use the grant type `urn:ietf:params:oauth:grant-type:jwt-bearer` but contain no client identifier.
[RFC 7523 JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://www.rfc-editor.org/rfc/rfc7523.html) states: ***"JWT authorization grants may be used with or without client authentication or identification."*** Technically, **"identification"** in this context means that a token request contains no identifier of the client that made the request.
Although RFC 7523 allows for this, it is risky to permit unidentifiable clients (API callers) to make token requests. However, there may be cases where token requests by unidentified clients are necessary, and the safety of the operation can be ensured in some way. In such cases, this switch can be turned **off**.
### Encrypted JWT
If **Allow** is selected, Authlete skips the remaining validation steps on an input assertion when it detects that the JWT is encrypted. This means that your authorization server implementation must verify an encrypted JWT independently when one is used as an authorization grant.
If **Allow** is not selected, the service rejects token requests that use an encrypted JWT as an authorization grant with the grant type `urn:ietf:params:oauth:grant-type:jwt-bearer` ([RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html)).
See [JWT Authorization Grant](/protocols-and-flows/advanced-flows/jwt-authorization-grant-rfc-7523-2-1) on the Authlete website for details about the validation process performed by Authlete.
### Unsigned JWT
If **Allow** is selected, Authlete skips the remaining validation steps on an input assertion when it detects that the JWT is unsigned.
If **Allow** is not selected, the service rejects token requests that use an unsigned JWT as an authorization grant with the grant type `urn:ietf:params:oauth:grant-type:jwt-bearer` ([RFC 7523](https://www.rfc-editor.org/rfc/rfc7523.html)).
See [JWT Authorization Grant](/protocols-and-flows/advanced-flows/jwt-authorization-grant-rfc-7523-2-1) on the Authlete website for details about the validation process performed by Authlete.
## Endpoints: General
### Revocation Endpoint URL
An URL of the revocation endpoint ([RFC 7009](https://tools.ietf.org/html/rfc7009) ). If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "revocation\_endpoint" property included in the response from the endpoint.
An implementation of a revocation endpoint is not mandatory.
Authlete's `/api/auth/revocation` API is an API which helps customers implement an RFC 7009 compliant revocation endpoint. The API itself is not an implementation of RFC 7009.
### Introspection Endpoint URL
An URL of the introspection endpoint ([RFC 7662](https://tools.ietf.org/html/rfc7662) ). If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "introspection\_endpoint" property included in the response from the endpoint.
An implementation of an introspection endpoint is not mandatory.
Authlete's `/api/auth/introspection/standard` API is an API which helps customers implement an RFC 7662 compliant introspection endpoint. The API itself is not an implementation of RFC 7662.
Authlete's `/api/auth/introspection` API is an Authlete-specific custom introspection API. Unlike the standard specification (RFC 7662), `/api/auth/introspection` API not only returns information about an access token but also validates the access token and builds an RFC 6750 compliant error message as necessary. Also, the API returns information about arbitrary key-value pairs associated with the access token (Authlete's custom feature). Therefore, in normal cases, Web API implementations on resource servers use the Authlete's custom introspection API.
### Pushed Authorization Requests (PAR)
### Require PAR
If **Require PAR** is selected, Authlete rejects an authorization request that doesn't employ PAR.
### PAR Endpoint URL
An URL of the pushed authorization request endpoint. Client applications can register authorization requests at this endpoint in advance before sending the authorization requests to the authorization endpoint. The pushed authorization request endpoint issues a `request_uri` that represents the registered authorization request. The issued `request_uri` can be included in an authorization request. Technical details are defined in *OAuth 2.0 Pushed Authorization Requests*.
The value of this property is used as the value of the `pushed_authorization_request_endpoint` metadata in the response from the discovery endpoint.
### PAR Endpoint Duration
The default value of the duration of `request_uri`s issued from the pushed authorization request endpoint. The unit of time is seconds. If the value is 0, the default value determined by the Authlete server is used.
If an authorization request being registered at the pushed authorization request endpoint includes information about its expiration (e.g., as the value of the `exp` claim), that information is considered. The smaller value between the specified expiration and the default value is chosen as the duration of the registered authorization request. Note that the specification, *OAuth 2.0 Pushed Authorization Requests*, states: *"The request URI lifetime is at the discretion of the AS."* Therefore, the behavior described here is specific to the Authlete implementation.
### UserInfo Endpoint URL
An URL of the userinfo endpoint (OpenID Connect Core 1.0, [5.3. UserInfo Endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) ). If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "userinfo\_endpoint" property included in the response from the endpoint.
An implementation of a userinfo endpoint is not mandatory.
Authlete's /api/auth/userinfo API and /api/auth/userinfo/issue API are APIs that help customers implement their own userinfo endpoint. The Authlete APIs themselves are not a userinfo endpoint.
### End Session Endpoint URL
An URL of the End Session Endpoint, also known as the RP-Initiated Logout endpoint ([OpenID Connect RP-Initiated Logout 1.0](https://openid.net/specs/openid-connect-rpinitiated-1_0.html)). If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "end\_session\_endpoint" property included in the response from the endpoint ([OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html)).
RP-Initiated Logout 1.0 defines this endpoint as the one to which a client (Relying Party) redirects the user agent in order to initiate logout at the Authorization Server. When invoked, the Authorization Server is expected to terminate the user's session and, if applicable, perform OP-wide single logout processing.
Typical logout requests may include parameters such as `id_token_hint`, `client_id`, `post_logout_redirect_uri` and `state`. If a post-logout redirect is supported, the Authorization Server must ensure that only pre-registered post-logout redirect URIs are accepted, in order to prevent open-redirect vulnerabilities.
The "end\_session\_endpoint" property is OPTIONAL in OpenID Connect Discovery 1.0. It is strongly recommended to configure this endpoint if the Authorization Server supports RP-Initiated Logout or provides session-based authentication.
Example values:
* `https://as.example.com/logout`
* `https://login.example.com/oidc/end-session`
## Endpoints: Federation
### Federation Support
A flag that indicates whether this service supports OpenID Federation 1.0.
However, if the feature of OpenID Federation 1.0 is not enabled on the Authlete server hosting this service, functions related to OpenID Federation 1.0 are not usable regardless of the setting of this property.
### Client Registration Types
client registration types supported by this service. This property corresponds to the `client_registration_types_supported` server metadata defined in OpenID Federation 1.0.
If the `EXPLICIT` checkbox is checked, the **Registration Endpoint** must be set up properly.
### Registration Endpoint
An URI of the federation registration endpoint. This property corresponds to the `federation_registration_endpoint` server metadata defined in OpenID Connect Federation 1.0.
If the Authorization Server declares that it supports the client registration type `explicit`, this property must not be empty.
### Organization Name
A human-readable name representing the organization that operates this service. This property corresponds to the `organization_name` server metadata defined in OpenID Connect Federation 1.0.
If this property is not empty, the `organization_name` property appears in self-signed entity statements of this service.
### Authority Identifier
Identifiers of authorities that can issue entity statements for this service. The format of the identifiers is a URL with either the `https` or `http` scheme. This property corresponds to the `authority_hints` property that appears in self-signed entity statements.
OpenID providers participating in one or more federations are expected to have superior authorities. Only trust anchors with no superiors do not have superior authorities.
Because the `authority_hints` property in self-signed entity statements is mandatory, if this setting is left empty, the configuration endpoint (`/.well-known/openid-federation`) cannot generate a valid entity statement. Therefore, to make OpenID Connect Federation 1.0 work, this property must be set up properly.
### Entity Configuration Duration
Duration of the entity configuration in seconds.
An OpenID provider that participates in an OpenID Connect federation must publish its entity configuration at `/.well-known/openid-federation` or at a variant location such as `/.well-known/openid-federation{path_part_of_issuer}`. An entity configuration is a kind of JWT. This property specifies the duration of the JWT in seconds.
When the value of this property is 0, the default value determined by your Authlete server is used as the duration of the entity configuration.
### Trust Anchors
Trust anchors referenced when this service resolves trust chains of relying parties. If this property is left empty, attempts of client registration will fail regardless of whether its type is `automatic` or `explicit`. Therefore, to make OpenID Connect Federation 1.0 work, this property must be set up properly.
Each trust anchor has an entity ID and a JWK Set. The format of entity IDs is a URL with either the `https` or `http` scheme. JWK Sets are expected to contain public keys that this service can use to verify signatures of entity statements issued by trust anchors.
The OpenID Connect Federation 1.0 specification does not define how to obtain JWK Sets of trust anchors. This is considered out of scope of the specification.
## Endpoints: Advanced
### Dynamic Client Registration (DCR)
If **Supported** is selected, the service will allow access to the dynamic client registration protocol API.
You probably want to supply both a **Registration Endpoint URI** and **Registration Management Endpoint base URI** value to support the protocol.
If **Supported** is not selected, the registration API is not available for this service, though the usual client management APIs are still available.
See [OpenID Connect Dynamic Registration 1.0](http://openid.net/specs/openid-connect-registration-1_0.html) for details about client registration.
### DCR's Scope Parameter
This switch indicates whether the dynamic client registration/configuration endpoints of your authorization server recognize the `scope` parameter in client metadata.
When **Enable** is selected, Authlete's `/api/client/registration` API and `/api/client/registration/update` API use the value of the `scope` parameter to limit the range of scopes that the client application can request. The limitation is achieved by registering the specified scopes as **requestable scopes** (cf. `ClientExtension.isRequestableScopesEnabled()`). The Authlete APIs will embed the `scope` parameter in the `responseContent` response parameter, representing a response from the dynamic client registration/configuration endpoints.
If the switch is off, the Authlete APIs ignore the `scope` parameter in client metadata and do not embed the `scope` parameter in `responseContent`. This behavior is backward-compatible with previous Authlete versions. No limitation is imposed on the range of requestable scopes, so the client application can request any scopes that your authorization server supports.
See [RFC 7591 OAuth 2.0 Dynamic Client Registration Protocol](https://www.rfc-editor.org/rfc/rfc7591.html) for technical details about the `scope` parameter.
### DCR with Duplicate Software ID
A DCR (Dynamic Client Registration) request may contain the `software_id` client metadata, defined in [RFC 7591](https://www.rfc-editor.org/rfc/rfc7591.html). The client metadata is saved in Authlete's database together with other client metadata.
If **Accept** is selected, Authlete does not check for duplication of `software_id`.
If **Accept** is not selected, Authlete checks whether the value of the `software_id` client metadata included in a DCR request already exists in the database and rejects the DCR request if it does.
### Registration Endpoint URL
An URL of an endpoint to register a client application dynamically ([OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html)). If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `registration_endpoint` property included in the response from the endpoint. Note that a `registration_access_token` is returned from the dynamic client registration API response **only when this URI is configured**. If this URI is not set, the `registration_access_token` will not be included in the response.
An implementation of a registration endpoint is not mandatory.
### Registration Management Endpoint Base URL
An URL of the client registration endpoint of this service. ASCII characters only. 200 characters at most. The URL must start with `https://`.
The value is used as the value of the `registration_endpoint` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) of this service.
See [OpenID Connect Dynamic Registration 1.0](http://openid.net/specs/openid-connect-registration-1_0.html) for details about client registration.
### Client Configuration Error Behavior
A flag to toggle the behavior of the `/api/client/registration` API in the error case of *401 Unauthorized*. If **Return Unauthorized** is selected, the API uses `UNAUTHORIZED` as a value of the `action` response parameter when appropriate. If **Return Unauthorized** is not selected, the API never uses `UNAUTHORIZED`.
The `UNAUTHORIZED` enum value did not exist in the initial implementation of `ClientRegistrationResponse.Action` enum ([JavaDoc](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/ClientRegistrationResponse.Action.html)). Therefore, implementations of the client configuration endpoint were not able to conform to [RFC 7592](https://www.rfc-editor.org/rfc/rfc7592.html) strictly.
For backward compatibility (to avoid breaking running systems), the API does not use the `UNAUTHORIZED` action if this flag is **OFF**.
The steps an existing implementation of the client configuration endpoint has to take in order to conform to the requirements related to *401 Unauthorized* are as follows:
1. Update the Authlete library (e.g., [authlete-java-common](https://github.com/authlete/authlete-java-common)) your system may be using.
2. Update your implementation of the client configuration endpoint so that it can handle the `UNAUTHORIZED` action.
3. Set **ON** to this flag.
RFC 7592 requires that the client configuration endpoint return *401 Unauthorized* when the registration access token is invalid or the client does not exist (or is invalid).
### Grant Management
### Require Action
This property corresponds to the `grant_management_action_required` server metadata defined in [Grant Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html). If **Require Action** is selected, the authorization server requires that every authorization request and any request serving as an authorization request (e.g., CIBA backchannel authentication request) include the `grant_management_action` request parameter.
Note that selecting **Require Action** will result in rejecting all authorization requests from public clients because the specification does not allow public clients to utilize grant management.
### Endpoint URL
An URL of the grant management endpoint. This property corresponds to the `grant_management_endpoint` server metadata defined in [Grant Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html). If this property is set, `query` and `revoke` are added to the `grant_management_actions_supported` server metadata advertised in the discovery document ([OIDC Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html)) in addition to the default set (`create`, `replace`, and `merge`).
Grant Management for OAuth 2.0 is supported from Authlete 2.3. The grant management endpoint can be implemented by using Authlete's `/api/gm` API.
### Device Authorization Endpoint URL
An URL of the device authorization endpoint. This property corresponds to the `device_authorization_endpoint` metadata defined in OAuth 2.0 Device Authorization Grant (Device Flow). The value must consist of only ASCII characters and its length must not exceed 200. The scheme part must be `https`.
This property is used as the value of `device_authorization_endpoint` in the response from the discovery endpoint. Also, if a JWT-based client authentication method (`client_secret_jwt` or `private_key_jwt`) is used, the `aud` claim in the client assertion (`client_assertion`) is compared to the value of this property.
### Verification URI
A verification URI. This property is used as the value of the `verification_uri` parameter in responses from the device authorization endpoint. The value must consist of only ASCII characters and its length must not exceed 200. The scheme part must be `https`.
This property must be set in advance to use Device Flow. Otherwise, an error is returned when `/api/device/authorization` API is called.
### Verification URI with Placeholder
A verification URI with a placeholder for a user code. The value must consist of only ASCII characters and its length must not exceed 200. The scheme part must be `https`.
If this property is set and its value includes a fixed string `USER_CODE` (e.g., `https://example.com/verification?user_code=USER_CODE`), it is used to generate the value of the `verification_uri_complete` parameter in responses from the device authorization endpoint. Authlete replaces the fixed string with an actual user code. If the value of this property is not set or does not include the fixed string, the `verification_uri_complete` parameter will not be embedded in responses from the device authorization endpoint.
### Verification Code Duration
Duration of device verification codes (`device_code`) and end-user verification codes (`user_code`) in seconds. The value is used as the value of the `expires_in` parameter in responses from the device authorization endpoint.
A positive number must be set to this property in advance to use Device Flow. Otherwise, an error is returned when `/api/device/authorization` API is called.
### Polling Interval
A minimum interval between polling requests to the token endpoint in seconds. The value must be between 0 and 65,535. The value is used as the value of the `interval` parameter in responses from the device authorization endpoint unless it is zero. If the value is set to zero, the `interval` parameter will not be included in the response from the device authorization endpoint.
### User Code Character Set
A character set for user codes (`user_code`).
If `BASE20` is selected, user codes will consist of BCDFGHJKLMNPQRSTVWXZ (20 upper-case non-vowel characters) (e.g., `WDJBMJHT`). If `NUMERIC` is selected, 0123456789 are used (e.g., `019450730`). If this property is not set, `BASE20` is used as the default value.
Note that user codes generated by Authlete do not include dashes and other punctuation.
### User Code Length
The length of user codes (`user_code`). The value must be between 0 and 255.
If the value is 0, the length of user codes will be 8 when using the character set `BASE20` (the entropy is 20^8) and 9 when using the character set `NUMERIC` (the entropy is 10^9). The value of this property directly affects the entropy of user codes. Setting the value too low may cause frequent database errors due to the UNIQUE constraint. Conversely, setting the value too high may demotivate end-users from entering the user codes. Therefore, it is advised not to set an extreme value.
### Mutual TLS Authentication
### Validate PKI Certificate Chain
If **Validate PKI Certificate Chain** is selected, Authlete server checks whether client certificates used for TLS\_CLIENT\_AUTH client authentication can be reached from root certificates which have been registered in advance.
If **Validate PKI Certificate Chain** is not selected, Authlete server does not perform the validation. Therefore, the validation needs to be done in the implementation of your front-end authorization server.
You can register trusted root certificates at the configuration item, "Trusted Root Certificates for Mutual TLS Authentication".
#### Trusted Root Certificate
List of trusted root certificates used when validating client certificate chains in Mutual TLS PKI authentication. If the configuration item above, "Validate PKI Certificate Chain " is enabled, Authlete server checks whether client certificates used for TLS\_CLIENT\_AUTH can be reached from root certificates registered here.
### MTLS Endpoint Aliases
This property corresponds to the `mtls_endpoint_aliases` metadata defined in "5. Metadata for Mutual TLS Endpoint Aliases" of [OAuth 2.0 Mutual TLS Client Authentication and Certificate-Bound Access Tokens](https://datatracker.ietf.org/doc/rfc8705/).
### FAPI
### Supported Service Profiles
**\[IMPORTANT]** Supported additional features on top of the base OAuth 2.0 and OpenID Connect. Note that if these features are not enabled in your Authlete server environment, you can't use them even if they are marked as enabled in the Console. Please contact [sales@authlete.com](mailto:sales@authlete.com) for details.
**FAPI** is a set of security profiles defined by the [FAPI Working Group](https://openid.net/wg/fapi/) under the [OpenID Foundation](https://openid.net/).
**OPEN\_BANKING** represents the Open Banking Security Profile defined by Open Banking Implementation Entity in the UK. This feature works on top of FAPI.
### FAPI 1.0 Modes
(TBD)
### FAPI 2.0 Modes
(TBD)
### Backchannel Authentication Endpoint URL
An URL of the backchannel authentication endpoint. This property corresponds to the `backchannel_authentication_endpoint` metadata.
### Supported Backchannel Token Delivery Modes
Select token delivery modes to support.
`POLL` means a backchannel token delivery mode where a client polls the token endpoint until it gets tokens.
`PING` means a backchannel token delivery mode where a client is notified via its client notification endpoint and then retrieves tokens from the token endpoint.
`PUSH` means a backchannel token delivery mode where a client receives tokens directly at its client notification endpoint.
### Backchannel Auth Req ID Duration
Specify the duration of `auth_req_id` issued from the backchannel authentication endpoint in seconds. This is used as the value of the `expires_in` property in responses from the backchannel authentication endpoint.
### Backchannel Polling Interval
Specify the minimum interval between polling requests to the token endpoint from client applications in seconds. This is used as the value of the `interval` property in responses from the backchannel authentication endpoint. The maximum value is 65535 seconds.
### Backchannel User Code Parameter
If **Supported** is selected, the `user_code` request parameter is supported at the backchannel authentication endpoint. This property corresponds to the `backchannel_user_code_parameter_supported` metadata.
### Binding Message in FAPI Context
If **Require** is selected, the `binding_message` request parameter is always required when a backchannel authentication request is judged as a request for Financial-grade API (FAPI).
The FAPI-CIBA profile requires that the authorization server *`shall ensure a unique authorization context exists in the authorization request or require a binding_message in the authentication request`* (FAPI-CIBA, 5.2.2, 2). The simplest way to fulfill this requirement is to select **Require** here.
If you don't select **Require**, the `binding_message` request parameter remains optional even in the FAPI context, but in exchange, your authorization server must implement a custom mechanism that ensures each backchannel authentication request has a unique context.
## Tokens and Claims: Access Token
### Access Token
### Type
The type of access tokens issued from the service. The value of this configuration item is used as the value of the `token_type` response parameter which is included together with an access token in a response from your authorization server.
If Web APIs on resource servers receive access tokens by the means defined in [RFC 6750](https://tools.ietf.org/html/rfc6750) , set `Bearer` (case-insensitive).
In general, Web API implementations comply with RFC 6750 unless they have a clear reason to avoid it.
### Duration
Duration of access tokens in seconds. This value is used to calculate the value of `expires_in` response parameter which is returned together with an access token.
The appropriate value for access token duration varies depending on characteristics of respective services. It may be 1 hour, 1 day, 1 year and even 100 years.
The value set to this configuration item is applied to all access tokens issued by this service. However, it is possible to change the expiration date of an access token independently by calling `/api/auth/token/update` API with the `accessTokenExpiresAt` request parameter. Also, `/api/auth/token/create` API (which is used to create an access token manually) provides an optional request parameter, `accessTokenDuration`, to specify the duration for a newly created access token.
In addition, there is a mechanism to control access token duration using "scope attributes". See the description about scopes for details.
### Enable TLS Client Certificate Bound
Configuration item to control whether or not this service supports certificate-bound access tokens defined in [OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens](https://www.rfc-editor.org/rfc/rfc8705.html#name-mutual-tls-client-certifica). If **Enable** is selected, a discovery endpoint implemented using Authlete's `/api/service/configuration` API will include `"tls_client_certificate_bound_access_tokens":true`.
If this feature is enabled, the client certificate used in a token request is associated with the access token issued by the token request. When a client application accesses Web APIs with the access token, the client application has to use the same client certificate that was used in the token request. Otherwise, accesses would be rejected.
If an access token is associated with a client certificate, it becomes difficult to abuse the access token even when the access token is leaked. It is because one won't succeed in using Web APIs with the access token unless he has the same client certificate at hand which was used when the access token was issued.
To use this feature, implementations of a token endpoint and Web APIs must extract a client certificate from a request and pass it to an Authlete API as the value of the `clientCertificate` request parameter. `/api/auth/token` API (which a token endpoint implementation calls) and `/api/auth/introspection` API (which Web API implementations call) have the `clientCertificate` request parameter.
Associating client certificates with access tokens will make your system securer. However, note that additional development work will be required for an authorization server, resource servers and client applications.
Even if **Enable** is selected here at the service level, if "TLS Client Certificate Bound Access Tokens" is not enabled at the client level, access tokens issued to the client application won't be associated with client certificates.
#### Single Token per Subject
Configuration item to control whether to allow at most one access token only per combination of a subject (= end-user identifier) and a client application, or to allow multiple access tokens to be issued for each combination.
If this switch is turned **on**, an attempt to issue a new access token invalidates existing access tokens that are associated with the combination of the same subject and the same client.
Note that, however, attempts by Client Credentials flow do not invalidate existing access tokens because access tokens issued by the flow are not associated with any end-user's subject.
Also note that an attempt by Refresh Token flow invalidates the coupled access token only and this invalidation is always performed regardless of whether the value of this configuration item is Enabled or Disabled.
### Signature Algorithm
The algorithm used for signing access tokens. If this property is set, access tokens issued by this service are [JWTs](https://tools.ietf.org/html/rfc7519). If not, access tokens issued by this service are random strings.
### Signature Key ID
A key ID to identify the [JWK](https://tools.ietf.org/html/rfc7517) (JSON Web Key) used for signing access tokens (This property is only used when JWT-based access tokens are generated by this service).
If there is only one applicable JWK, then there is no problem. However, if there are multiple candidates, a [key ID](https://tools.ietf.org/html/rfc7517#section-4.5) needs to be specified so that the Authlete Server can select the correct one.
If this setting has a value, it is interpreted as a key ID. Authlete Server uses this key ID to select a JWK when signing access tokens.
Even if there are multiple candidate JWKs and this setting is left empty, Authlete Server will try its best to choose one. However, there are cases where Authlete Server cannot choose one (e.g., there are multiple candidate JWKs but none of them has a key ID). In such cases, Authlete Server fails to generate an access token.
## Tokens and Claims: Refresh Token
### Refresh Token Duration
### Duration
Duration of refresh tokens in seconds.
There is a mechanism to control refresh token duration using "scope attributes". See the description about scopes for details.
### Enable Duration Reset
This flag indicates whether duration of refresh tokens are reset when they are used even if **Token Rotation** is enabled.
### Refresh Token Rotation
### Enable Token Rotation
Configuration item to control whether to keep a refresh token valid after its use or invalidate the used refresh token and issue a new one.
If a refresh token remain valid after its use and a new one is not issued, the refresh token will eventually expire. At the timing, authorization process is required again.
On the other hand, if a refresh token is invalidated when it is used and if a new one is issued, authorization process is not required again while you continue to use a refresh token to get a new access token before the refresh token expires.
### Enable Idempotency
If the switch is turned **on**, refresh token requests using the same refresh token can be made multiple times in quick succession, and they will obtain the same renewed refresh token within a short period.
If the switch is turned **off**, each refresh token request will receive a different renewed refresh token, even if the requests are made in a short period.
### Enable Duration Takeover
If the switch is turned **on**, the remaining duration of the used refresh token is carried over to the newly issued one. This setting applies only when token rotation is enabled.
### Enable Token Expiration
If the switch is turned **on**, the expiration date of the access token will never exceed the expiration date of the corresponding refresh token, regardless of the calculated duration based on other settings.
## Tokens and Claims: ID Token
### ID Token Duration
### Duration
Duration of ID tokens in seconds. The value set to this configuration item is used to compute the value of the "exp" claim of ID tokens.
### Allowable Clock Skew
Specify the allowable clock skew between this service and clients in seconds. This value is taken into consideration when time-related claims in a JWT (e.g., `exp`, `iat`, `nbf`) are verified.
### Enable Reissuable
This switch controls whether to enable the ID token reissuance feature in the refresh token flow.
If the switch is turned **on**, the `action` parameter in a response from the `/auth/token` API becomes `ID_TOKEN_REISSUABLE` when the following conditions are all met:
1. The flow of the token request is the refresh token flow.
2. The scope set after processing the token request still contains the `openid` scope.
3. The access token is associated with the subject of a user.
4. The access token is associated with a client application.
When receiving `ID_TOKEN_REISSUABLE`, the implementation of the token endpoint can take either of the following actions:
1. Execute the same steps as for the `OK` action. This will result in the token endpoint behaving as before, and no ID token is reissued.
2. Call the `/idtoken/reissue` API to reissue an ID token and let the API prepare a token response including a new ID token, along with a new access token and a refresh token.
See the [`JavaDoc of the TokenResponse class`](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/TokenResponse.html) for details.
### Audience Claim Format
This setting allows you to select the type of the `aud` claim in ID tokens. The options are:
* **Unspecified**: When this option is chosen, the type of the `aud` claim remains the same as before. If backward compatibility is important, this option should be selected. Because examples in standard specifications are inconsistent in terms of the type of the `aud` claim, Authlete's implementation is also inconsistent. For example, the `aud` claim of ID tokens issued by the authorization code flow is an *array*, whereas the `aud` claim of ID tokens issued by CIBA flows is a *string*.
* **Array**: When this option is selected, the type of the `aud` claim in ID tokens always becomes an *array*. From the viewpoint of the JWT specification ([RFC 7519](https://www.rfc-editor.org/rfc/rfc7519.html)), this behavior is the general case. [Section 4.1.3](https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3) of the specification states as follows:
> *In the general case, the "aud" value is an array of case-sensitive strings, each containing a StringOrURI value. In the special case when the JWT has one audience, the "aud" value MAY be a single case-sensitive string containing a StringOrURI value.*
* **String**: When this option is selected, the type of the `aud` claim in ID tokens always becomes a *string*.
Some Authlete APIs, such as `/auth/authorization/issue`, have the `idTokenAudType` request parameter. If the request parameter is given and its value is either `array` or `string`, the request parameter takes precedence over this service property (`Service.idTokenAudType`).
## Tokens and Claims: Claims
### Claims
### Supported Claims
A list of claims supported by this service. If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `claims_supported` property included in the response from the endpoint.
"Supported" here means that this service can provide values of the claims directly or indirectly.
On service creation, 20 standard claims defined in [OpenID Connect Core 1.0, 5.1. Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) are set as the initial value.
The names of claims requested by an authorization request are included in a response from Authlete's `/api/auth/authorization` API as the value of the `claims` property. However, claims that are not listed in this configuration item will never be included in the `claims`. For example, even if the `scope` parameter of an authorization request includes `email` and `phone` (which are expanded to 4 claims: `email`, `email_verified`, `phone_number`, and `phone_number_verified`), if `email_verified` and `phone_number_verified` are not listed in this configuration item, `email_verified` and `phone_number_verified` will not appear in the `claims` property in the response from `/api/auth/authorization` API.
### Supported Claim Locales
A list of claim languages supported by this service. Technically, this is a list of [language tags](https://tools.ietf.org/html/rfc5646#section-2) ([RFC 5646](https://tools.ietf.org/html/rfc5646)) which the authorization endpoint recognizes as the value of the `claims_locales` parameter ([OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)). If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `claims_locales_supported` property included in the response from the endpoint.
A client application may specify languages of claim values that are embedded in an ID token by using the `claims_locales` request parameter. For example, by including `claims_locales=ja` in an authorization request, a client application can tell the authorization server that it wishes the value of the `family_name` claim to be written in Japanese. See [OpenID Connect Core 1.0, 5.2. Claims Languages and Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts) for details.
However, it is not assured that the authorization server honors the request parameter and changes the claim language accordingly. Even if the authorization server completely ignores the `claims_locales` parameter, it is not a spec violation.
The value of the `claims_locales` parameter in an authorization request is included in a response from Authlete's `/api/auth/authorization` API as the value of the `claimsLocales` property. However, languages that are not listed in this configuration item will never be included in the `claimsLocales`. For example, even if an authorization request includes `claims_locales=ja`, if `ja` is not listed in this configuration item, `ja` will not appear in the `claimsLocales` property in the response from `/api/auth/authorization` API.
If your authorization endpoint implementation ignores the `claims_locales` request parameter, you can leave this configuration item empty.
### Supported Types
Claim types supported by this service. If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `claim_types_supported` property included in the response from the endpoint.
**NORMAL** means that this service (OpenID Provider) itself provides claim values directly. On service creation, this claim type is enabled by default.
**AGGREGATED** means that providers of claim values exist separately, and this service collects claim values from such providers and returns aggregated information to client applications.
**DISTRIBUTED** means that providers of claim values exist separately, and this service just returns pointers to the providers to client applications.
See [OpenID Connect Core 1.0, 5.6. Claim Types](https://openid.net/specs/openid-connect-core-1_0.html#ClaimTypes) for details about claim types.
*Note*: If you want to support AGGREGATED and DISTRIBUTED in your service, you have to format information as described in [5.6.2. Aggregated and Distributed Claims](https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims) and pass it to Authlete's `/api/auth/authorization/issue` API. Authlete itself does not support AGGREGATED and DISTRIBUTED directly.
#### Restrict Shortcut
If the switch is on, claims specified by shortcut scopes (e.g. `profile`) are included in an ID token only when no access token is issued (only when `response_type` is `id_token`). On the other hand, if **Restrict Shortcut** is not selected, the claims are always included in an ID token regardless of whether an access token is issued or not.
To strictly conform to the description in OpenID Connect Core 1.0 Section 5.4, choose **Restrict Shortcut**. If backward compatibility with Authlete 2.1 and older versions is important, don't choose **Restrict Shortcut**.
From a programmer's point of view, the choice here affects the following response parameters only:
* `claims` in `/api/auth/authorization` API responses
* `claimNames` in `/api/backchannel/authentication` API responses
* `claimNames` in `/api/device/authorization` API responses
If the implementation of your authorization server ignores the response parameters above, in other words, if the implementation does not change its behavior according to the values of the response parameters, the choice here has no meaning.
### Trust Frameworks
Trust frameworks supported by this service. Pre-defined values listed in [11.1. Trust Frameworks](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.11.1) of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) are as follows:
* `de_aml`
* `eidas_ial_substantial`
* `eidas_ial_high`
* `nist_800_63A_ial_2`
* `nist_800_63A_ial_3`
* `jp_aml`
* `jp_mpiupa`
Trust frameworks listed here are used as values of the `trust_frameworks_supported` metadata in the discovery document. See [7. OP Metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7) for details.
### Identity Evidence
Types of identity evidence supported by this service. Pre-defined values listed in [4.1.1. Evidence](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.4.1.1) of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) are as follows:
* `id_document`
* `utility_bill`
* `qes`
Identity evidence listed here are used as values of the `evidence_supported` metadata in the discovery document. See [7. OP Metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7) for details.
### Supported Identity Documents
Types of identity documents supported by this service. Pre-defined values listed in [11.2. Identity Documents](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.11.2) of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) are as follows:
* `idcard`
* `passport`
* `driving_permit`
* `de_idcard_foreigners`
* `de_emergency_idcard`
* `de_erp`
* `de_erp_replacement_idcard`
* `de_idcard_refugees`
* `de_idcard_apatrids`
* `de_certificate_of_suspension_of_deportation`
* `de_permission_to_reside`
* `de_replacement_idcard`
* `jp_drivers_license`
* `jp_residency_card_for_foreigner`
* `jp_individual_number_card`
* `jp_permanent_residency_card_for_foreigner`
* `jp_health_insurance_card`
* `jp_residency_card`
Identity document types listed here are used as values of the `id_documents_supported` metadata in the discovery document. See [7. OP Metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7) for details.
### Supported Verification Methods
Types of identity document verification methods supported by this service. Pre-defined values listed in [11.3. Verification Methods](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.11.3) of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) are as follows:
* `pipp`
* `sripp`
* `eid`
* `uripp`
Verification methods listed here are used as values of the `id_documents_verification_methods_supported` metadata in the discovery document. See [7. OP Metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7) for details.
### Supported Verified Claims
Names of verified claims supported by this service. For example, `given_name` and `family_name`. See [3.1. Additional Claims about End-Users](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.3.1) and [4.2. claims Element](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.4.2) in [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) for more examples.
Claim names listed here are used as values of the `claims_in_verified_claims_supported` metadata in the discovery document. See [7. OP Metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7) for details.
### Predefined Transformed Claims
Predefined **transformed claims**. The format is JSON. This property corresponds to the `transformed_claims_predefined` server metadata, which is defined in "OpenID Connect Advanced Syntax for Claims (ASC) 1.0".
### Document Types
Document types supported by this service. This property corresponds to the `documents_supported` server metadata, which was added by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html). Examples of predefined values are `idcard` and `passport`. See the [identifiers](https://bitbucket.org/openid/ekyc-ida/wiki/identifiers) page maintained by the [eKYC-IDA WG](https://openid.net/wg/ekyc-ida/) for the complete list of predefined values.
### Document Check Methods
(TBD)
### Electronic Record Types
(TBD)
### Attachment Types
Attachment types supported by this service. The check boxes correspond to `embedded` and `external`, which are predefined values for the `attachments_supported` server metadata added by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html).
### Supported Digest Algorithms
Supported algorithms that can be used to compute digest values of external attachments. This property corresponds to the `digest_algorithms_supported` server metadata, which was added by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html).
Possible values are listed in the [Hash Algorithm Registry](https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg) of IANA (Internet Assigned Numbers Authority). If the Authorization Server supports *external attachments*, this property must include at least `sha-256`.
Client applications can specify a digest algorithm by setting the `digestAlgorithm` property (which corresponds to the `digest_algorithm` client metadata). When the client property is not set, the service must use `sha-256` as the default algorithm.
### Access Token for External Attachments
If **Embed Automatically** is selected, Authlete generates access tokens for external attachments and embeds them in ID tokens and userinfo responses.
The `claims` request parameter of `/auth/authorization/issue` and `/auth/userinfo/issue` APIs may contain `verified_claims` (as defined in [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html)). The `verified_claims` may contain `attachments`, which is a JSON array of JSON objects. Each element in the `attachments` array represents either an embedded attachment or an external attachment. A JSON object that represents an external attachment contains a `url` property pointing to the location of the content of the attachment and may optionally contain an `access_token` property, which is an access token the client application should use when accessing the URL.
If **Embed Automatically** is selected, when Authlete finds a JSON object representing an external attachment that does not contain the `access_token` property, Authlete generates an access token and embeds it in the JSON object as the value of the `access_token` property. In this case, Authlete also embeds an `expires_in` property.
See [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) for details about external attachments.
### Validation Schema Set
Choice of the validation schema set that is used to validate the content of `'verified_claims'`.
Since version 2.3, Authlete validates the content of `'verified_claims'` based on the JSON schema files that accompany the specification ([OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html)). These files are found in the [/schema/](https://bitbucket.org/openid/ekyc-ida/src/master/schema/) folder of the Git repository of the specification.
Usually, Authlete uses the legitimate JSON schema files that conform to the specification. However, it is possible to make Authlete use a different set of JSON schema files by specifying a name of validation schema set through this property (`Service.verifiedClaimsValidationSchemaSet`).
Authlete recognizes the following names of validation schema sets:
* **null**: Same as `'standard'`.
* `'standard'`: The set of the legitimate JSON schema files.
* `'standard+id_document'`: A set of customized JSON schema files that mostly conform to the standard but additionally accept `'id_document'` as a valid name of evidence. This is for backward compatibility. Note that `'id_document'` was deprecated by Implementer's Draft 4 (cf. [eKYC-IDA PR 152](https://bitbucket.org/openid/ekyc-ida/pull-requests/152)).
## Tokens and Claims: Advanced
### Requests Without Scope Parameter
When a request has no `scope` parameter (or none of the given scopes is valid) and the service's pre-defined default scope set is empty, the authorization server considers that the request is made with no scope. If the **Reject** switch is turned **on**, such requests are rejected.
The last paragraph in RFC 6749 Section 3.3 does not explicitly mention the case where the default scope set is empty. However, if you interpret the state *"the default scope set exists but is empty"* as *"the default scope set does not exist"* and want to strictly conform to the requirement in RFC 6749 Section 3.3, turn the **Reject** switch **on**.
When the **Reject** switch is **on**, it ensures that every access token issued by the authorization server has at least one scope, unless you create access tokens without a scope using the `/api/auth/token/create` API.
If backward compatibility with Authlete 2.1 and older versions is important, leave the **Reject** switch **off**.
### Supported Scopes
A list the scopes that this service recognizes. Each scope name must consist of only printable ASCII characters, excluding spaces, double quotation marks, and backslashes (RFC 6749, [3.3. Access Token Scope](https://tools.ietf.org/html/rfc6749#section-3.3)). Additionally, the length of each scope name must not exceed 200 characters.
OpenID Connect has defined some standard scope names. `openid` is the most important scope. If the `openid` scope is not included in a request to the authorization endpoint, the request is not considered an OpenID Connect request ([3.1.2.1. Authentication Request](http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)). `profile`, `email`, `address`, and `phone` are scopes that can be used to request specific claims to be included in an ID token ([5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)). `offline_scope` is a scope to request that an OAuth 2.0 Refresh Token be issued, which can be used to obtain an Access Token that grants access to the End-User's UserInfo Endpoint even when the End-User is not present (not logged in) ([11. Offline Access](http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess)).
If the Authorization Server implements a discovery endpoint with Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `"scopes_supported"` property included in the response from the endpoint.
Values listed here are recognized as valid values for the `"scope"` parameter in authorization requests. Authlete silently ignores unknown scopes that are not listed here, even if they are included in the `"scope"` parameter. The reason Authlete does not raise an error for unknown scopes is that OpenID Connect Core 1.0, [3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), states: *"Scope values used that are not understood by an implementation SHOULD be ignored."*
**Note:** if you do not list `openid`, this service will stop providing OpenID Connect functionalities.
Each scope has configuration items such as name, default entry (boolean), description, and attributes.
* **Scope Name**: A string used as the value of the `"scope"` parameter. Usable characters are printable ASCII characters except space, double quote, and backslash.
* **Default Entry**: A boolean flag which is referenced when an authorization request does not include the `"scope"` parameter. If the `"scope"` parameter is missing, scopes whose default entry is `true` are used as if they were specified in the `"scope"` parameter.
* **Description**: Although not required by the specification, Authlete manages this data. Note that descriptions of the standard scopes defined in OpenID Connect Core 1.0 (`openid`, `profile`, `email`, `address`, `phone`, and `offline_access`) cannot be changed.
* **Attributes**: Arbitrary key-value pairs. The functionality to attach arbitrary key-value pairs to scopes is a custom mechanism provided by Authlete.
Note that some scope attributes listed below are interpreted in a special way by Authlete.
| AttributeName | AttributeValue | Description |
| - | - | - |
| fapi | r | This means "Financial-grade API Read-Only". An authorization request that includes at least one scope with this attribute is regarded as a request for an access token for Read-Only API. As a result, Authlete checks whether the authorization request satisfies the constraints imposed by [Financial-grade API - Part 1: Read-Only API Security Profile](https://bitbucket.org/openid/fapi/src/master/Financial_API_WD_001.md). For example, when the flow of the authorization request is "authorization code flow", Authlete checks whether the request includes `"code_challenge"` and `"code_challenge_method=S256"`. |
| fapi | rw | This means "Financial-grade API Read-Write". An authorization request that includes at least one scope with this attribute is regarded as a request for an access token for Read-Write API. As a result, Authlete checks whether the authorization request satisfies the constraints imposed by [Financial-grade API - Part 2: Read and Write API Security Profile](https://bitbucket.org/openid/fapi/src/master/Financial_API_WD_002.md). For example, Authlete checks whether the request includes either `"request"` or `"request_uri"`. If both `fapi=r` and `fapi=rw` exist in a request, `fapi=rw` takes precedence. |
On service creation, standard scopes defined in OpenID Connect Core 1.0 are set as the initial values.
| Scope Name | Description |
| - | - |
| openid | The most important scope. If an authorization request does not include this scope, it is not regarded as an OpenID Connect request. Do not remove this scope unless you have a clear reason, as OpenID Connect won't work without it. See [3.1.2.1. Authentication Request](http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) for details. |
| profile | The scope to request claims related to the user's profile ("name", etc.) be included in an ID token. See [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) for details. |
| email | The scope to request claims related to email ("email" and "email\_verified") be included in an ID token. See [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) for details. |
| address | The scope to request claims related to the address ("address") be included in an ID token. See [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) for details. |
| phone | The scope to request claims related to the phone ("phone\_number" and "phone\_number\_verified") be included in an ID token. See [5.4. Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) for details. |
| offline\_access | The scope to request a refresh token that can be used to obtain an access token that grants permission to access the [userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) even if the user is not logged in. See [11. Offline Access](http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess) for details. |
There is no fixed rule for the granularity of scopes; it depends on your service's needs.
If managing many fine-grained scopes is challenging, consider attaching additional metadata to access tokens instead. Authlete allows you to attach arbitrary key-value pairs to access tokens, simplifying scope management. For more information, see the [Extra Properties](/configuration-reference/tokens-and-claims/how-to-add-extra-properties-to-an-access-token) documentation.
### DPoP Token
### Require Nonce
When this switch is turned **on**, Authlete will require a `nonce` in DPoP Proof tokens. If the `nonce` is missing, the Authlete server will return a `use_dpop_nonce` error, and the client must repeat the previous request with a `nonce` claim included. The `nonce` may be supplied by the Authlete server in the HTTP `DPoP-Nonce` response header. If not, the client must generate the `nonce` itself.
### Nonce Duration
This setting controls how long a `dpop-nonce` value can be reused in proof tokens. To ensure proper validation, save the `dpop-nonce` value from the token response header and refresh it at the specified duration interval.
### Token Exchange
### Enable Identifiable Clients Only
If this switch is on, this service rejects **token exchange requests** that contain no client identifier.
Section 2.1 of [RFC 8693 OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html) states: ***"The supported methods of client authentication and whether or not to allow unauthenticated or unidentified clients are deployment decisions that are at the discretion of the authorization server."*** Technically, "**unidentified**" in this context means that a token exchange request contains no identifier of the client that made the request.
Although [A.1.1 of RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html#section-a.1.1) shows an example of a token exchange request that contains no client identifier, it is risky to allow unidentifiable clients (API callers) to make token exchange requests. However, there may be cases where token exchanges by unidentified clients are necessary, and the safety of the operation can be ensured in some way. In such cases, this option can be unchecked.
### Enable Confidential Clients Only
If this switch is on, this service rejects **token exchange requests** from **public** clients.
Section 2.1 of [RFC 8693 OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html) states: ***"The supported methods of client authentication and whether or not to allow unauthenticated or unidentified clients are deployment decisions that are at the discretion of the authorization server."*** Technically, "**unauthenticated**" in this context means that the client making a token exchange request is a public client and therefore [client authentication](https://darutk.medium.com/oauth-2-0-client-authentication-4b5f929305d4) for the client is not required at the token endpoint.
To accept token exchange requests from public clients as well as those from confidential clients, uncheck this option.
### Enable Permitted Clients Only
If this switch is on, this service rejects **token exchange requests** from clients that have no explicit permission for token exchange.
An administrator can grant a client explicit permission to make token exchange requests by checking the checkbox for "Explicit Permission for Token Exchange" in the "Extension" tab of the Web console for client application management.
See [RFC 8693 OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html) for details about token exchange.
### Reject Encrypted JWT
If this switch is on, this service rejects **token exchange requests** that use encrypted JWTs as input tokens with the token type `urn:ietf:params:oauth:token-type:jwt` or the token type `urn:ietf:params:oauth:token-type:id_token`.
If this checkbox is not checked, Authlete skips the remaining validation steps on an input token when it detects that it is an encrypted JWT. This means that your authorization server implementation must verify an encrypted JWT by itself when one is used as an input token.
See [RFC 8693 OAuth 2.0 Token Exchange](/protocols-and-flows/advanced-flows/oauth-2-0-token-exchange-rfc-8693) on the Authlete website for details about the validation process performed by Authlete.
### Reject Unsigned JWT
If this switch is on, this service rejects **token exchange requests** that use unsigned JWTs as input tokens with the token type `urn:ietf:params:oauth:token-type:jwt` or the token type `urn:ietf:params:oauth:token-type:id_token`.
If this checkbox is not checked, Authlete skips the remaining validation steps on an input token when it detects that it is an unsigned JWT.
See [RFC 8693 OAuth 2.0 Token Exchange](/protocols-and-flows/advanced-flows/oauth-2-0-token-exchange-rfc-8693) on the Authlete website for details about the validation process performed by Authlete.
## Key Management: JWK Set
### JWK Set Content
Content of the JWK Set document ([RFC 7517](https://tools.ietf.org/html/rfc7517)) of this service.
If the Authorization Server supports asymmetric algorithms for signing [ID tokens](https://openid.net/specs/openid-connect-core-1_0.html), [UserInfo Endpoint](https://openid.net/specs/openid-connect-core-1_0.html) responses, and authorization responses ([JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)](https://openid.net/specs/openid-financial-api-jarm.html)) and for encrypting [request objects](https://openid.net/specs/openid-connect-core-1_0.html), a JWK Set must be registered. The JWK Set must contain pairs of public and private keys for the algorithms that the Authorization Server wants to support.
Algorithms that can be used as JWS ([RFC 7515](https://tools.ietf.org/html/rfc7515)) "alg" are listed in [3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1) of JWA ([RFC 7518](https://tools.ietf.org/html/rfc7518)). Among them, algorithms whose names start with "RS", "PS", or "ES" are asymmetric algorithms.
Algorithms that can be used as JWE ([RFC 7516](https://tools.ietf.org/html/rfc7516)) "alg" are listed in [4.1. "alg" (Algorithm) Header Parameter Values for JWE](https://tools.ietf.org/html/rfc7518#section-4.1) of JWA ([RFC 7518](https://tools.ietf.org/html/rfc7518)). Among them, algorithms whose names start with "RSA" or "ECDH" are asymmetric algorithms.
FYI: There exists an online service that generates JWK and JWK Set at [mkjwk.org](https://mkjwk.org/). Try it if you are not familiar with JWK.
### JWK Set Endpoint URI
An URL of the JWK Set document ([RFC 7517](https://tools.ietf.org/html/rfc7517)) for this service. If the Authorization Server implements a discovery endpoint using Authlete's `/api/service/configuration` API, the value of this configuration item is used as the value of the `"jwks_uri"` property included in the response from the endpoint.
If the configuration item "JWK Set Content" is empty, Authlete attempts to fetch a JWK Set from the URI specified here as needed. However, Authlete will never attempt to fetch an external JWK Set for secret keys of asymmetric algorithms. Therefore, if there are processes that use secret keys of asymmetric algorithms—such as signing ID tokens with asymmetric algorithms—the secret keys for those algorithms need to be registered as part of the "JWK Set Content".
If the Authorization Server supports asymmetric algorithms for signing [ID tokens](https://openid.net/specs/openid-connect-core-1_0.html#IDToken), [UserInfo Endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) responses, and authorization responses ([JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)](https://openid.net/specs/openid-financial-api-jarm.html)), as well as for encrypting [request objects](https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests), the Authorization Server must implement a JWK Set endpoint (unless the JWK Set is distributed to client application developers offline).
Authlete's `/api/service/jwks/get` API is available to help customers implement their own JWK Set endpoint.
### ID Token Signature Key ID
A key ID to identify the JWK ([RFC 7517](https://tools.ietf.org/html/rfc7517)) used for signing ID tokens.
When the algorithm for ID token signature is asymmetric (e.g., RS256), a JWK representing a private key of the algorithm needs to be selected from the registered JWK Set. If there is only one applicable JWK, then there is no issue. However, if there are multiple candidates, a [key ID](https://tools.ietf.org/html/rfc7517#section-4.5) needs to be specified so that the Authlete server can select the correct one.
Even if there are multiple candidate JWKs but this configuration item is left empty, the Authlete server will attempt to choose one. However, there are situations where the Authlete server may fail (e.g., there are multiple candidate JWKs but none of them has a key ID). In such cases, the Authlete server may fail to generate an ID token.
### UserInfo Signature Key ID
A key ID to identify the JWK ([RFC 7517](https://tools.ietf.org/html/rfc7517)) used for signing responses from the [UserInfo Endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo).
When the algorithm for signing responses from the UserInfo Endpoint is asymmetric (e.g., RS256), a JWK representing a private key of the algorithm needs to be selected from the registered JWK Set. If there is only one applicable JWK, then there is no issue. However, if there are multiple candidates, a [key ID](https://tools.ietf.org/html/rfc7517#section-4.5) needs to be specified so that the Authlete server can select the correct one.
Even if there are multiple candidate JWKs, but this configuration item is left empty, the Authlete server will attempt to choose one. However, there are situations where the Authlete server may fail (e.g., there are multiple candidate JWKs but none of them has a key ID). In such cases, the Authlete server may fail to generate a response from the UserInfo Endpoint.
### Authorization Signature Key ID
A key ID to identify the JWK ([RFC 7517](https://tools.ietf.org/html/rfc7517)) used for signing authorization responses ([JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)](https://openid.net/specs/openid-financial-api-jarm.html)).
When the algorithm for signing authorization responses is asymmetric (e.g., RS256), a JWK representing a private key of the algorithm needs to be selected from the registered JWK Set. If there is only one applicable JWK, then there is no issue. However, if there are multiple candidates, a [key ID](https://tools.ietf.org/html/rfc7517#section-4.5) needs to be specified so that the Authlete server can select the correct one.
Even if there are multiple candidate JWKs, but this configuration item is left empty, the Authlete server will attempt to choose one. However, there are situations where the Authlete server may fail (e.g., there are multiple candidate JWKs but none of them has a key ID). In such cases, the Authlete server will fail to generate an authorization response JWT, resulting in an error response being returned in the format defined in RFC 6749.
## Key Management: HSM
### Hardware Security Modules (HSM)
This option enables support for HSM (Hardware Security Modules). When disabled, keys managed in HSMs are not used even if they exist. Additionally, `/api/hsk/*` APIs reject all requests. Even if enabled, HSM-related features will not function if the configuration of the Authlete server you are using does not support HSM.
*Note: Only supported in Dedicated Cloud.*
### HSK (Hardware Security Keys)
This setting displays details for Hardware-Secured Keys when the HSM feature is enabled and active.
*Note: Only supported in Dedicated Cloud.*
### Federation JWK Set Content
A JWK Set containing private keys that are used when this service signs (1) self-signed entity statements and (2) responses from the endpoint that returns this service's JWK Set in the JWT format; the URI of the endpoint is specified by the `signed_jwks_uri` server metadata.
If this property is left empty, this service cannot generate a valid self-signed entity statement. Therefore, to make OpenID Connect Federation 1.0 work, this property must be set up properly.
Note that this JWK Set is different from the one used for normal OpenID Connect operations, such as signing ID tokens.
### Federation Signed JWK Set URI
An URI of the endpoint that returns this service's JWK Set in the JWT format. This property corresponds to the `signed_jwks_uri` server metadata defined in OpenID Connect Federation 1.0.
The JWT returned from the endpoint is signed with a private key in the JWK Set specified in the **JWK Set** field. Therefore, if the **JWK Set** field is not set up properly, the endpoint will not be able to return a valid response.
### Federation Signature Key ID
A key ID to identify a JWK that should be used to sign the entity configuration and the signed JWK Set.
The entity configuration is a kind of JWT and is published at `/.well-known/openid-federation` or at a variant location such as `/.well-known/openid-federation{path_part_of_issuer}`.
The signed JWK Set is also a kind of JWT and is published at the URL designated by the `signed_jwks_uri` server metadata.
When this property is specified, Authlete will use the JWK with the specified key ID when signing the entity configuration and the signed JWK Set. If this property is omitted, there is no guarantee as to which JWK Authlete will choose.
### VCI JWK Set
A JWK Set document containing private keys used to sign verifiable credentials.
Some Authlete APIs, such as the `/vci/single/issue` and `/vci/batch/issue` APIs, may issue one or more verifiable credentials. The content of this property is utilized by these APIs.
Authlete APIs that issue verifiable credentials recognize a request parameter that can specify the key ID of a private key for signing. For instance, a request to the `/vci/single/issue` API includes an `order` object with a `signingKeyId` parameter that can specify the key ID of a private key to be used for signing. If no key ID is specified, Authlete will automatically select a private key.
If the JWKs in the JWK Set do not contain the `kid` property ([RFC 7517, Section 4.5](https://datatracker.ietf.org/doc/html/rfc7517#section-4.5)) when this `credentialJwks` property is updated, Authlete will automatically insert the `kid` property into such JWKs. The JWK thumbprint ([RFC 7638](https://datatracker.ietf.org/doc/html/rfc7638)) computed with the SHA-256 hash algorithm is used as the value of the `kid` property.
### VCI JWK Set URI
An URL where the JWK Set document of the credential issuer is exposed.
This URL is used as the value of the `jwks_uri` property in the JWT issuer metadata. The metadata is published at `/.well-known/jwt-issuer`. For more details about the JWT issuer metadata, refer to the SD-JWT-based Verifiable Credentials (SD-JWT VC) specification.
## Metadata
### Service Data Policy
An URL of the document describing how the data provided by this service can be used. If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "op\_policy\_uri" property included in the response from the endpoint.
### Terms of Service
An URL of this service's Terms Of Service for client application developers. If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "op\_tos\_uri" property included in the response from the endpoint.
### Service Documentation for Developers
An URL of the documentation about this service for developers. If the Authorization Server implements a discovery endpoint with Authlete's /api/service/configuration API, the value of this configuration item is used as the value of the "service\_documentation" property included in the response from the endpoint.
## Verifiable Credentials
### Verifiable Credentials Feature
This flag controls availability of features related to Verifiable Credentials such as support of the OID4VCI specification.
### Credential Offer Duration
Duration for credential offers issued by the Credential Issuer. If the Wallet does not specify a duration or specifies a duration of `0` or less (`<= 0`) in the credential offer request, this default value will be applied.
### Transaction Duration
Duration, in seconds, for which transaction IDs remain valid when issued as a result of a credential request or a batch credential request. If this property is set to 0 or a negative value, the default duration configured in this setting will be applied.
### Credential Duration
Duration, in seconds, for which verifiable credentials remain valid. This duration applies to credentials issued by Authlete APIs such as the `/vci/single/issue` and `/vci/batch/issue` APIs.
A value of 0 indicates that verifiable credentials will not expire, meaning they won't include an expiration property. For example, JWT-based verifiable credentials will not contain the `exp` claim ([RFC 7519, Section 4.1.4](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4)).
Authlete APIs that issue verifiable credentials also support request parameters that can override this default duration. For instance, a request to the `/vci/single/issue` API includes an `order` object with a `credentialDuration` parameter that can specify a different duration.
### Anonymous Access
Indicates whether token requests using the pre-authorized code flow by unidentifiable client applications are allowed.
This property corresponds to the `pre-authorized_grant_anonymous_access_supported` metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
### Credential Nonce Duration (c\_nonce)
Duration, in seconds, for which a `c_nonce` remains valid.
When the token endpoint of the authorization server issues an access token for verifiable credential issuance, it also issues a `c_nonce` alongside the access token. Additionally, the credential endpoint and batch credential endpoint of the credential issuer will issue a new `c_nonce` if the presented one has already expired. This property determines the lifetime of these `c_nonces`.
If this property is set to 0 or a negative value, the default duration configured on the Authlete server will be applied.
### Authorization Servers
An identifiers of the authorization servers that the credential issuer relies on for authorization when this service acts as a credential issuer. This property corresponds to the `authorization_servers` metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The values must be HTTP-accessible URLs.
### JWE alg Algorithms
A list of supported JWE encryption algorithms ([RFC7516](https://datatracker.ietf.org/doc/html/rfc7516)), `alg` values defined in [RFC7518](https://datatracker.ietf.org/doc/html/rfc7518), supported by the Credential and Batch Credential Endpoint to encode the Credential or Batch Credential Response in a JWT. These algorithms are typically used to encrypt the content encryption key (CEK).
### JWE enc Algorithms
A list of supported JWE encryption algorithms ([RFC7516](https://datatracker.ietf.org/doc/html/rfc7516)), `enc` values defined in [RFC7518](https://datatracker.ietf.org/doc/html/rfc7518), used by the Credential Endpoint to encode the Credential Response in a JWT ([RFC7519](https://datatracker.ietf.org/doc/html/rfc7519)). These algorithms are typically used to encrypt the content.
### Credential Response Encryption
If **Require** is selected, the Credential Issuer mandates additional encryption on top of TLS for every Credential Response. In this case, the Wallet **MUST** provide encryption keys in the Credential Request. If **Require** is off, the Wallet may choose whether it provides encryption keys or not.
### Credential Issuer Identifier
An identifier of the credential issuer when this service acts as a credential issuer. This property corresponds to the `credential_issuer` metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The value must be a valid URL with the `https` scheme, and it must not include a query part or fragment. Additionally, Authlete restricts this value to ASCII characters only and enforces a maximum length of 200 characters.
This property must be set for the Authorization Server to function as a credential issuer.
### Credential Endpoint
An URL of the credential endpoint when this service acts as a credential issuer. This property corresponds to the `credential_endpoint` metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The value must be a valid URL with the `https` scheme and must not include a fragment part. Additionally, Authlete restricts this value to ASCII characters only and enforces a maximum length of 200 characters.
This property must be set for the service to function as a credential issuer.
### Batch Credential Endpoint
An URL of the batch credential endpoint when this service acts as a credential issuer. This property corresponds to the `batch_credential_endpoint` metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The value must be a valid URL with the `https` scheme and must not include a fragment part. Additionally, Authlete restricts this value to ASCII characters only and enforces a maximum length of 200 characters.
Implementing the batch credential endpoint is optional.
### Deferred Credential Endpoint
An URL of the deferred credential endpoint when this service acts as a credential issuer. This property corresponds to the `deferred_credential_endpoint` metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The value must be a valid URL with the `https` scheme and must not include a fragment part. Additionally, Authlete restricts this value to ASCII characters only and enforces a maximum length of 200 characters.
If the credential endpoint and/or the batch credential endpoint of your credential issuer may issue transaction IDs, you must implement the deferred credential endpoint.
### Supported Credentials
Credentials supported by the credential issuer when this service acts as a credential issuer. This property corresponds to the `credential_configurations_supported` metadata defined in the [OID4VCI specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html).
The value must be a JSON object. While non-ASCII characters are allowed, Authlete enforces a maximum length of 16,383 characters.
This property must be set for the service to function as a credential issuer.
For backward compatibility, the name of this property remains `credentialsSupported` and will not be renamed to `credentialConfigurationsSupported`.
# Understanding Service Access Tokens
Source: https://developers.authlete.com/deployment-and-operations/access-control/service-access-tokens
Learn how Authlete Service Access Tokens work, including permissions, presets, inheritance, API usage, and security best practices.
## Introduction
This article describes Authlete's **Service Access Tokens** (SATs).
A SAT is a bearer credential issued for a single Authlete service. It is the
credential your Authorization Server, Resource Server, CLI scripts, or CI/CD
pipelines present when calling Authlete's runtime and service-administration
APIs.
Permission customization, either via a preset or by selecting individual permissions,
lets you scope a token to exactly what its caller needs, so a Resource Server only gets
introspection rights, an administrative tool only gets the client-management
rights it requires, and so on.
SATs are the right credential for service-level runtime workloads. For
management-plane operations that span every service in an organization such
as creating services, reading the organization audit log, or driving Terraform
/ CI-CD provisioning, use an Organization Access Token instead.
## Token Properties
#### Token Name
A human-readable identifier used to distinguish tokens in the Service Access
Tokens list. The name has no effect on token behavior or permissions.
#### Expiration Date
Specifies when the token will expire and become invalid.
* If set, the token cannot be used after the specified date.
* If not set, the token does not expire.
For tokens used by automation, an explicit expiration date is strongly
recommended. The console renders any token expiring within one month in red.
#### Token Value
The token value is displayed only once, when the token is created or rotated.
* It must be copied and stored securely at creation time.
* The plain token value cannot be retrieved again after this step.
#### Rotation
Tokens can be rotated to generate a new value.
* Rotation invalidates the previous token value immediately.
* The new token value is displayed once and must be copied and saved.
* Permissions and metadata (name, expiration) are preserved across rotation
unless explicitly updated.
#### Token List
All Service Access Tokens are listed in a table with the following columns:
* **Token Name**: User-defined identifier.
* **Permissions**: Assigned permissions for the token.
* **Created**: Date the token was created.
* **Expires**: Expiration date, or no expiry if not set.
* **Actions**: Available operations such as rotation or deletion.
## Permission Presets
Pre-configured permission sets aligned to [standard OAuth 2.0 roles][RFC6749-1.1]. Selecting
a preset applies a fixed combination of permissions; if you then add or remove
permissions manually, the preset is cleared.
| Preset | Description | Permissions |
| :- | :- | :- |
| Authorization Server | Grants access to all runtime OAuth/OIDC endpoints required by a standard Authorization Server. | `use_service` |
| Admin Authorization Server | Default selection. Full administrative control over the service. Includes runtime endpoints, client management, and service configuration. | `modify_service` |
| Resource Server | Minimal access required to validate access tokens at a Resource Server. | `use_introspection` |
## Custom Permissions
Permissions can be selected individually when a preset does not match your use
case. Permissions follow the inheritance rules described in
[Permission Inheritance](#permission-inheritance) and selecting a higher-level
permission implicitly grants the lower-level ones.
If both `preset` and `permissions` are omitted, the **Create** button is
disabled. You must select a preset or at least one permission before the
token can be issued. If a preset is selected and the caller then adds or
removes individual permissions, the preset is cleared.
#### `use_service`
Runtime OAuth/OIDC operations. The standard permission for an Authorization
Server deployment. Allows calling:
* `/auth/authorization`
* `/auth/token`
* `/auth/userinfo`
* `/auth/introspection`
* `/auth/revocation`
* `/pushed_auth_req`
* `/service/configuration`
* `/service/jwks/get`
* `/backchannel/authentication`
* `/device/authorization`
* `/device/verification`
* `/device/complete`
* `/federation/*`
* `/vci/*` (Verifiable Credentials)
* `/jose/verify`
* `/idtoken/reissue`
* `/token/create`
* `/token/update`
* `/token/delete`
* `/token/revoke`
* `/token/get/list`
* `/rs/sign`
* `/hsk/get`
* `/client/authorization/get/list`
* `/client/registration` (Dynamic Client Registration)
Also implicitly grants `use_introspection`,
`view_service`, and `view_client`.
#### `use_introspection`
Token introspection only. The minimum permission a Resource Server needs to
validate access tokens. Allows calling:
* `/auth/introspection`
* `/auth/introspection/standard`
Does not grant access to authorization, token, or other runtime endpoints.
#### `view_service`
Read service configuration. Allows calling:
* `/service/get`
* `/service/get/list`
Also implicitly grants `view_client`. Use this for monitoring
dashboards or read-only integrations.
#### `view_client`
Read client details. Allows calling:
* `/client/get`
* `/client/get/list`
* `/client/granted_scopes/get`
* `/client/extension/requestable_scopes/get`
Does not allow creating, modifying, or deleting clients. Use this for audit
tools or dashboards that inspect client registrations.
#### `modify_client`
Update and delete clients, including the endpoints required to perform user
logout via authorization deletion. Allows calling:
* `/client/update`
* `/client/delete`
* `/client/secret/update`
* `/client/secret/refresh`
* `/client/lock_flag/update`
* `/client/extension/requestable_scopes/update`
* `/client/granted_scopes/delete`
* `/client/authorization/delete` (user session revocation / logout)
* `/client/authorization/update`
Also implicitly grants `view_client`.
#### `create_client`
Register new OAuth clients programmatically. Allows calling:
* `/client/create`
* `/client/get/default`
Also implicitly grants `use_service`,
`modify_client`, `view_service`, and
`view_client`. Use this for platforms that dynamically
provision OAuth clients.
#### `modify_service`
Full service administration. Allows calling:
* `/service/update`
* `/hsk/create`
* `/hsk/delete`
* `/client/extension/requestable_scopes/delete`
Subsumes all other SAT permissions. Use this for admin tools or CI/CD
pipelines that need complete control over the service.
Known issue: `modify_service`
does not currently grant `use_introspection` implicitly.
See Troubleshooting 403 Errors for the
workaround.
## Permission Inheritance
Higher-level permissions implicitly grant the lower-level permissions they
depend on. The inheritance chain is:
* `modify_service` → all permissions
* `create_client` → `use_service`, `modify_client`, `view_service`, `view_client`
* `use_service` → `use_introspection`, `view_service`, `view_client`
* `modify_client` → `view_client`
* `view_service` → `view_client`
When building a custom permission set, select only the highest-level
permission your caller requires; lower-level permissions in its inheritance
chain are granted implicitly.
## Using a Service Access Token
Send the token as a bearer credential on the `Authorization` header of any
Authlete service-level API call. Pair it with your Service ID and the
appropriate API server URL.
The following is an example token-endpoint call from an Authorization Server
using a SAT that holds the `use_service` permission.
```shell theme={null}
curl --oauth2-bearer ${SERVICE_ACCESS_TOKEN} \
https://${AUTHLETE_SERVER}/api/${SERVICE_ID}/auth/token \
--data-urlencode "parameters=grant_type=authorization_code&code=…&redirect_uri=…" \
--data-urlencode "clientId=client001" \
--data-urlencode "clientSecret=${CLIENT_SECRET}"
```
The following is an example introspection call from a Resource Server using a
SAT that holds only `use_introspection`.
```shell theme={null}
curl --oauth2-bearer ${SERVICE_ACCESS_TOKEN} \
https://${AUTHLETE_SERVER}/api/${SERVICE_ID}/auth/introspection \
--json '{
"token": "${ACCESS_TOKEN_TO_VALIDATE}"
}'
```
The API server checks the token's permissions on every call. If the token does
not hold the permission required by the endpoint, the response is
`403 Forbidden`. For example, a token holding only
`use_introspection` cannot call `/auth/token`.
## How the Console Issues a Token
Service Access Tokens are issued by the Authlete IdP's
`/api/servicetoken/create` endpoint. The Management
Console calls this endpoint on your behalf when an administrator clicks
**Create Token**; the endpoint authenticates the caller as an IdP user
(via session or user access token) and requires the
MODIFY\_SERVICE privilege on the target service. Service
Access Tokens and Organization Access Tokens are not accepted as the bearer
credential for this endpoint.
The request body the console submits has the following shape.
| Request Parameter | Necessity | Description |
| :- | :-: | :- |
| `serviceId` | REQUIRED | The ID of the service the token is scoped to. |
| `organizationId` | REQUIRED | The ID of the organization that owns the service. |
| `apiServerId` | REQUIRED | The ID of the API server (cluster) hosting the service. |
| `description` | REQUIRED | Human-readable name shown in the console and used to distinguish tokens. |
| `preset` | REQUIRED | One of `standard_as`, `admin_as`, `resource_server`, or `custom`. |
| `permissions` | CONDITIONAL | Explicit permission list. Required when `preset` is `custom`; rejected for any other preset. Permissions must be drawn from `use_service`, `use_introspection`, `view_service`, `view_client`, `create_client`, `modify_client`, `modify_service`. |
| `durationSeconds` | OPTIONAL | Lifetime of the token, in **seconds** from creation. Omit (or set to `null`) for a token that never expires. |
The response body shape is as follows.
```json theme={null}
{
"serviceId": 12345,
"organizationId": 67890,
"apiServerId": 111,
"accessToken": "sat_3Yq9bP0u1NiqU6Pz_uIH7n3Z2bP7Sg5K…",
"tokenId": "01HW…",
"description": "prod-as",
"permissions": ["use_service"],
"createdAt": "2026-05-12T07:34:22Z",
"expiresAt": "2026-06-11T07:34:22Z"
}
```
The `accessToken` field is returned only once. It cannot be
retrieved again afterwards; copy it to a secrets manager immediately. The
same single-display rule applies to the value returned by token rotation.
## Troubleshooting 403 Errors
If an Authlete API call fails with HTTP `403 Forbidden`, the response body
includes a `resultMessage` that names the exact permission the endpoint
requires.
```
[A457101] (/client/create) Function requires access rights ([CREATE_CLIENT])
for service (233044842478), access token does not have sufficient access.
```
```
[A456101] (/client/delete/{clientIdentifier}) Function requires access rights
([MODIFY_CLIENT]) for service (1495709902) and client (4140464843).
```
Map the bracketed access right (for example `[CREATE_CLIENT]`,
`[MODIFY_CLIENT]`) to a permission in [Custom Permissions](#custom-permissions)
and reissue the token with that permission. Two options:
1. **Recreate the token with a preset that covers the operation.** For
client-management calls, the **Admin Authorization Server** preset
(`modify_service`) covers every client-management and
service-management endpoint.
2. **Recreate the token with custom permissions.** Select only the access
right the error message named (or its lowest-level ancestor in
[Permission Inheritance](#permission-inheritance)). For example, a workload
that only needs to provision and update clients should hold
`create_client`; that single permission implicitly grants
`use_service`, `modify_client`,
`view_service`, and `view_client`.
The `use_service` permission (applied by the
Authorization Server preset) grants the runtime OAuth/OIDC
endpoints required by a standard Authorization Server, but it does
not grant `/client/create` or
`/client/delete`. Workloads that need to register or remove
clients (for example a CI pipeline that provisions test clients, or a
self-service developer portal) must be issued a token with
`create_client` or `modify_client`, or use
the Admin Authorization Server preset
(`modify_service`, which is the default selection).
Known issue: The introspection endpoints
(`/auth/introspection` and
`/auth/introspection/standard`) currently require an
explicit `use_introspection` permission on the token. The
`modify_service` and `create_client`
permissions do not implicitly grant
`use_introspection` (inheritance in the underlying
`AccessRight` enum is direct, not transitive), so a token
issued with the Admin Authorization Server preset, or a
custom token holding only `modify_service` or
`create_client`, will fail
`/auth/introspection` with HTTP `403` /
`A457101`.
Workaround: Issue introspection-calling tokens with the
Custom preset and include
`use_introspection` explicitly alongside the higher-level
permission — for example
`["modify_service", "use_introspection"]` or
`["create_client", "use_introspection"]`. Tokens
issued with this combination will continue to function unchanged once the
underlying bug is fixed, so the workaround is forward-compatible. The
Authorization Server preset (`use_service`)
and Resource Server preset
(`use_introspection`) are not affected.
#### Common 403s and what to switch to
| `resultMessage` access right | Endpoint(s) | Minimum permission to fix |
| :- | :- | :- |
| `[CREATE_CLIENT]` | `/client/create`, `/client/get/default` | `create_client` (or `modify_service`) |
| `[MODIFY_CLIENT]` | `/client/update`, `/client/delete`, `/client/secret/*`, `/client/authorization/delete`, … | `modify_client` (or `create_client` / `modify_service`) |
| `[MODIFY_SERVICE]` | `/service/update`, `/hsk/create`, `/hsk/delete` | `modify_service` |
| `[USE_INTROSPECTION]` | `/auth/introspection`, `/auth/introspection/standard` | `use_introspection` (or `use_service`) |
| `[VIEW_CLIENT]` | `/client/get`, `/client/get/list`, `/client/granted_scopes/get` | `view_client` (or any higher-level permission) |
| `[VIEW_SERVICE]` | `/service/get`, `/service/get/list` | `view_service` (or any higher-level permission) |
## Common Use Cases
| Use case | Recommended preset | Key permission |
| :- | :- | :- |
| Standard Authorization Server | Authorization Server | `use_service` |
| Resource Server (introspection only) | Resource Server | `use_introspection` |
| User logout / session management | Custom | `modify_client` |
| Dynamic client provisioning | Custom | `create_client` |
| Monitoring / inventory dashboards | Custom | `view_service` |
| Client audit / configuration review | Custom | `view_client` |
## Security Best Practices
* **Use least privilege.** A Resource Server should hold only
`use_introspection`; a read-only dashboard should hold only
`view_service`. Reserve `modify_service` for
trusted admin tooling.
* **Use a separate token per workload.** Do not share a single SAT across the
Authorization Server, the Resource Server, and your CI pipeline; each should
have its own token with its own permission set and its own rotation cadence.
* **Set an expiration date** on every SAT used by automation, and rotate them
as part of your secret-rotation policy.
* **Never commit SATs to source control.** Store them in a secrets manager
(AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, 1Password, etc.) and
inject them at runtime.
* **Rotate immediately** on suspected compromise, on personnel change, or on
any change to the workload that consumes the token.
## Conclusion
Service Access Tokens give Authlete deployments a single, scoped credential
for each runtime workload that calls Authlete APIs. The preset model covers
the standard Authorization Server, Resource Server, and admin use cases out
of the box, while custom permissions and the inheritance rules make it
straightforward to mint a least-privilege token for any other integration.
For more information, please contact us via the [contact form][CONTACT].
[CONTACT]: https://www.authlete.com/contact
[RFC6749-1.1]: https://datatracker.ietf.org/doc/html/rfc6749#section-1.1
# User Permissions
Source: https://developers.authlete.com/deployment-and-operations/access-control/user-permissions
This page documents the access control and permission model available in Authlete, organized across three independent scopes, Organization, Service, and Client. Manage these permissions through the Authlete Management Console or directly via the Authlete APIs.
In Authlete, access control is implemented across three independent scopes:
* Organization
* Service
* Client
Each scope supports two access levels:
* Admin
* Viewer
Privileges are explicitly assigned per scope and do not automatically inherit, allowing enforcement of least-privilege access.
## Organization Privileges
### Scope
Organization-level privileges govern tenant wide administration and governance.
### Organization Admin
An Organization Admin can:
* Manage organization settings and metadata
* Invite and manage users and assign permissions
* Generate organization-level administrative access tokens
* Perform service import / migration operations
* View organization-wide audit logs
* Perform irreversible administrative actions (e.g., organization deletion)
### Organization Viewer
An Organization Viewer can:
* View organization metadata (name, ID, plan)
* View configuration screens in a read-only capacity
An Organization Viewer **cannot**:
* Modify organization settings
* Manage users or permissions
* Generate administrative tokens
* Perform imports, migrations, or destructive actions
* Access audit logs
## Service Privileges
### Scope
Service-level privileges control the configuration and behavior of an individual OAuth / OpenID Connect authorization service.
### Service Admin
A Service Admin can:
* Configure OAuth/OIDC endpoints and flows
* Manage grant types, response types, and token behavior
* Configure cryptographic and security features (PKCE, mTLS, FAPI, CIBA, etc.)
* Manage federation, dynamic client registration, and advanced protocol features
* Generate service-level access tokens
* Delete the service
These permissions directly affect runtime authentication and authorization behavior.
### Service Viewer
A Service Viewer can:
* View service configuration and settings in a read-only capacity
A Service Viewer **cannot**:
* Modify endpoints or protocol behavior
* Generate service tokens
* Enable or disable security features
* Delete the service
## Client Privileges
### Scope
Client-level privileges apply to OAuth / OpenID Connect client applications.
### Client Admin
A Client Admin can:
* Create, modify, rotate, and delete clients
* Manage client credentials, redirect URIs, and client-specific settings
### Client Viewer
A Client Viewer can:
* View client configuration in a read-only capacity
A Client Viewer **cannot**:
* Modify client configuration
* Rotate secrets or delete clients
## Permission Summary (Audit View)
| Permission Scope | Admin Capabilities | Viewer Capabilities |
| - | - | - |
| Organization | Tenant administration, users, tokens, audit logs | Read-only organization visibility |
| Service | Full authorization server configuration | Read-only service visibility |
| Client | Full client lifecycle management | Read-only client visibility |
# Amazon API Gateway
Source: https://developers.authlete.com/deployment-and-operations/integration-with-api-gateways/amazon-api-gateway
Integration information on using Authlete with Amazon API Gateway and its Lambda Authorizers to handle access tokens issued by Authlete.
## Overview
You can integrate Authlete with [Amazon API Gateway](https://aws.amazon.com/api-gateway/?nc1=h_ls)
with its [Lambda Authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html)
so that the gateway can handle access tokens which Authlete has issued.
## Links
* [Authlete API Tutorial: Financial-grade Amazon API Gateway](/deployment-and-operations/integration-with-api-gateways/financial-grade-amazon-api-gateway)
* [Amazon API Gateway + AWS Lambda + OAuth](/deployment-and-operations/integration-with-api-gateways/amazon-api-gateway-lambda-oauth)
* [Amazon API Gateway の Custom Authorizer を使い、OAuth アクセストークンで API を保護する](https://qiita.com/TakahikoKawasaki/items/b372ab49da0a9aedb76a) (in Japanese)
# Amazon API Gateway + Custom Authorizer + OAuth
Source: https://developers.authlete.com/deployment-and-operations/integration-with-api-gateways/amazon-api-gateway-custom-authorizer-oauth
Technical Information about how to protect APIs built on Amazon API Gateway by OAuth access tokens by utilizing Custom Authorizer.
# What is Custom Authorizer?
On Feb 11, 2016, a blog entry of AWS Compute Blog, "[Introducing custom authorizers in Amazon API Gateway][blog]", announced that **Custom Authorizer** had been introduced into Amazon API Gateway.
Thanks to this mechanism, an API built on Amazon API Gateway can delegate validation of a Bearer token (such as an OAuth or SAML token) presented by a client application to an external authorizer. The figure below is an excerpt from the online document "[Enable Amazon API Gateway Custom Authorization][use]" and "Lambda Auth function" at the top position in the figure is an authorizer. API Gateway delegates validation of a token to the authorizer if it is configured so.
As the same as before, Amazon API Gateway itself does not provide OAuth server functionalities, but you can protect APIs built on Amazon API Gateway by OAuth access tokens by utilizing Custom Authorizer.
In this document, we use the term "Custom Authorizer", which has been renamed as "Lambda Authorizer".
Before Custom Authorizer was introduced, introspection and validation of an access token had to be executed in an implementation of a lambda function in order to protect APIs by OAuth access tokens. Our document "Amazon API Gateway + AWS Lambda + OAuth" shows how to do it using the old way.
# No example
The [online document][use] and the [blog][blog] show implementation examples of an authorizer. However, the example in the online document uses `allow`, `deny` and `unauthorized` as token values in order to simplify the code example, so it is not a practical example. On the other hand, the example in the blog uses JWT ([RFC 7519][7519]) as a token value, so it is a practical example. But, it does not include any code to make a query to an external server to get information about a token. It is because JWT is a form where information is embedded in a token itself, and so information can be extracted only by decoding the token value.
That is, there is no example to communicate with an authorization server to get information about an access token. The lack of a code example for the use case is unfriendly to developers considering the following.
* An authorizer has to be implemented as an AWS Lambda function.
* As a language for AWS Lambda implementation, node.js is recommended more than others.
* There is no standardized way to process network communication synchronously in node.js (AFAIK).
So, in the next section, we'll show you an authorizer example written in node.js which communicates with an authorization server to get information about an access token.
# Authorizer example
We show an authorizer example written in node.js which communicates with an external authorization server.
The introspection API used here is not the one defined in [RFC 7662][7662] (OAuth 2.0 Token Introspection) but [Authlete's introspection API][intro]. However, you can still get generic knowledge as to the following points.
1. How to extract the HTTP method and the resource path of the request from the value of `event.methodArn`. (in `extract_method_and_path` function)
2. How to extract an access token which is embedded in the form defined in [RFC 6750, 2.1][6750_21]. from the value of event.authorizationToken. (in `extract_access_token` function)
3. How to complete network communication with an authorization server synchronously in `exports.handler` using waterfall function of *async* module.
4. How to communicate with an introspection API of an authorization server using *request* module. (in `introspect` function)
5. How an authorizer generates a response to API Gateway. (in `generate_policy` function)
6. How to set an HTTP status code to reject an request. (in `exports.handler` function)
```javascript theme={null}
// The service ID and a service access token issued by Authlete.
// These are needed to call Authlete's introspection API.
var SERVICE_ID = '{Your-Service-ID}';
var SERVICE_ACCESS_TOKEN = '{Your-Service-Access-Token}';
// Regular expression to extract an access token from
// Authorization header.
var BEARER_TOKEN_PATTERN = /^Bearer[ ]+([^ ]+)[ ]*$/i;
// Modules.
var async = require('async');
var request = require('request');
// A function to extract the HTTP method and the resource path
// from event.methodArn.
function extract_method_and_path(arn)
{
// The value of 'arn' follows the format shown below.
//
// arn:aws:execute-api:::///"
//
// See 'Enable Amazon API Gateway Custom Authorization' for details.
//
// http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html
//
// Check if the value of 'arn' is available just in case.
if (!arn)
{
// HTTP method and a resource path are not available.
return [ null, null ];
}
var arn_elements = arn.split(':', 6);
var resource_elements = arn_elements[5].split('/', 4);
var http_method = resource_elements[2];
var resource_path = resource_elements[3];
// Return the HTTP method and the resource path as a string array.
return [ http_method, resource_path ];
}
// A function to extract an access token from Authorization header.
//
// This function assumes the value complies with the format described
// in "RFC 6750, 2.1. Authorization Request Header Field". For example,
// if "Bearer 123" is given to this function, "123" is returned.
function extract_access_token(authorization)
{
// If the value of Authorization header is not available.
if (!authorization)
{
// No access token.
return null;
}
// Check if it matches the pattern "Bearer {access-token}".
var result = BEARER_TOKEN_PATTERN.exec(authorization);
// If the Authorization header does not match the pattern.
if (!result)
{
// No access token.
return null;
}
// Return the access token.
return result[1];
}
// A function to get a list of required scopes as a string array
// from a combination of an HTTP method and a resource path.
// For example, ["profile", "email"]. When a non-empty array is
// returned, the Authlete server (= the implementation of Authlete's
// introspection API) checks if all the scopes are covered by the
// access token. When this method returns null, such a check on
// scopes is not performed.
function get_required_scopes(http_method, resource_path)
{
// Customize as necessary.
return null;
}
// A function to call Authlete's introspection API.
//
// This function is used as a task for 'waterfall' method of 'async' module.
// See https://github.com/caolan/async#user-content-waterfalltasks-callback
// for details about 'waterfall' method.
//
// * access_token (string) [REQUIRED]
// An access token whose information you want to get.
//
// * scopes (string array) [OPTIONAL]
// Scopes that should be covered by the access token. If the scopes
// are not covered by the access token, the value of 'action' in the
// response from Authlete's introspection API is 'FORBIDDEN'.
//
// * callback
// A callback function that 'waterfall' of 'async' module passes to
// a task function.
//
function introspect(access_token, scopes, callback)
{
request({
// The URL of Authlete's introspection API.
url: 'https://us.authlete.com/api/' + SERVICE_ID + '/auth/introspection',
// HTTP method.
method: 'POST',
// The service access token for Bearer Authentication.
auth: {
bearer: SERVICE_ACCESS_TOKEN
},
// Request parameters passed to Authlete's introspection API.
json: true,
body: {
token: access_token,
scopes: scopes
},
// Interpret the response from Authlete's introspection API as a UTF-8 string.
encoding: 'utf8'
}, function(error, response, body) {
if (error) {
// Failed to call Authlete's introspection API.
callback(error);
}
else if (response.statusCode != 200) {
// The response from Authlete's introspection API indicates something wrong
// has happened.
callback(response);
}
else {
// Call the next task of 'waterfall'.
//
// 'body' is already a JSON object. This has been done by 'request' module.
// As for properties that the JSON object has, see the JavaDoc of
// com.authlete.common.dto.IntrospectionResponse class in authlete-java-common.
//
// http://authlete.github.io/authlete-java-common/com/authlete/common/dto/IntrospectionResponse.html
//
callback(null, body);
}
});
}
// A function to generate a response from Authorizer to API Gateway.
function generate_policy(principal_id, effect, resource)
{
return {
principalId: principal_id,
policyDocument: {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: effect,
Resource: resource
}]
}
};
}
// An authorizer implementation
exports.handler = function(event, context)
{
// Get information about the function that is requested to be invoked.
// Extract the HTTP method and the resource path from event.methodArn.
var elements = extract_method_and_path(event.methodArn);
var http_method = elements[0];
var resource_path = elements[1];
// The access token presented by the client application.
var access_token = extract_access_token(event.authorizationToken);
// If the request from the client does not contain an access token.
if (!access_token) {
// Write a log message and tell API Gateway to return "401 Unauthorized".
console.log("[" + http_method + "] " + resource_path + " -> No access token.");
context.fail("Unauthorized");
return;
}
// Get the list of required scopes for the combination of the HTTP method
// and the resource path.
var required_scopes = get_required_scopes(http_method, resource_path);
async.waterfall([
function(callback) {
// Validate the access token by calling Authlete's introspection API.
introspect(access_token, required_scopes, callback);
},
function(response, callback) {
// Write a log message about the result of the access token validation.
console.log("[" + http_method + "] " + resource_path + " -> " +
response.action + ":" + response.resultMessage);
// The 'action' property contained in a response from Authlete's
// introspection API indicates the HTTP status that the caller
// (= an implementation of protected resource endpoint) should
// return to the client application. Therefore, dispatch based
// on the value of 'action'.
switch (response.action) {
case 'OK':
// The access token is valid. Tell API Gateway that the access
// to the resource is allowed. The value of 'subject' property
// contained in a response from Authlete's introspection API is
// the subject (= unique identifier) of the user who is associated
// with the access token.
context.succeed(generate_policy(response.subject, 'Allow', event.methodArn));
break;
case 'BAD_REQUEST':
case 'FORBIDDEN':
// Tell API Gateway that the access to the resource should be denined.
context.succeed(generate_policy(response.subject, 'Deny', event.methodArn));
break;
case 'UNAUTHORIZED':
// Tell API Gateway to return "401 Unauthorized" to the client application.
context.fail("Unauthorized");
break;
case 'INTERNAL_SERVER_ERROR':
default:
// Return "Internal Server Error". When the value passed to
// context.fail() is other value than "unauthorized", it is
// treated as "500 Internal Server Error".
context.fail("Internal Server Error");
break;
}
callback(null);
}
], function (error) {
if (error) {
// Something wrong has happened.
context.fail(error);
}
});
};
```
## Create a lambda function deployment package
Here we show how to create a lambda function deployment package including the custom authorizer code above.
First, save the authorizer code shown above as `index.js`.
Then, open the file with a text editor and replace `SERVICE_ID` and `SERVICE_ACCESS_TOKEN` with actual values. Both come from the Authlete Management Console: the service ID is shown on the service's page, and a service access token is issued under Service Access Tokens on that page. See "[Getting Started][gs]" for details.
Next, modify the implementation of `get_required_scopes` function as necessary. The role of the method is to return a list of necessary scopes based on the HTTP method and the resource path of a request. See the comment in `index.js` for details.
Then, move to the directory where `index.js` is placed and execute the following commands to install async module and requet module.
```
$ npm install async request
```
Operations so far have created `index.js` file and `node_modules` directory.
```
$ ls -1F
index.js
node_modules/
```
Finally, create a ZIP file containing these. The ZIP file is a lambda function deployment package. Upload it to AWS Lambda.
Note that it is recommended to set the timeout value of the lambda function longer than the default value because the Custom Authorizer implementation communicates with an external authorization server.
## Configure Custom Authorizer
See [the online document][use] and [the blog][blog] about how to use the uploaded lambda function as an implementation of Custom Authoriser.
# Test
Here we assume that GET mydemoresource (which is created by going through the steps described in the Amazon API Gateway online document, "Walkthrough: Create API Gateway API for Lambda Functions") is protected by the Custom Authorizer.
First, access mydemoresource without an access token. Don't forget to replace `{your-api-id}` and `{region-id}` with your own.
```
curl -v -s -k https://{your-api-id}.execute-api.{region-id}.amazonaws.com/test/mydemoresource
```
You will receive "*401 Unauthorized*" when you execute the above command.
Next, access the API with an access token. Here we assume that `MaSp49VXQnAmg_Si2i02PsvKlTSHMuxp3zJH00OA5mc` is a valid access token. (See the next section as to how to issue an access token.)
```
curl -v -s -k https://{your-api-id}.execute-api.{region-id}.amazonaws.com/test/mydemoresource \
-H 'Authorization: Bearer MaSp49VXQnAmg_Si2i02PsvKlTSHMuxp3zJH00OA5mc'
```
A successful response returns an HTTP status code "200 OK" and a JSON `{"Hello":"World"}`.
## How to issue an access token
Authlete does not host an authorization server, so issue an access token for your service to test the integration. The quickest way is Authlete's Create Access Token API, which mints an access token without running a full OAuth flow. Don't forget to replace `{service-id}`, `{service-access-token}` and `{client-id}` with your own, and set `subject` and `scopes` to values supported by your service.
```
curl -v -X POST https://us.authlete.com/api/{service-id}/auth/token/create \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {service-access-token}' \
-d '{ "grantType": "AUTHORIZATION_CODE", "clientId": {client-id}, "subject": "john", "scopes": [ "read" ] }'
```
The issued access token is returned in the `accessToken` field of the response.
```
{
"action": "OK",
"accessToken": "Z5a40U6dWvw2gMoCOAFbZcM85q4HC0Z--0YKD9-Nf6Q",
"clientId": 26888344961664,
"subject": "john",
"tokenType": "Bearer",
"expiresIn": 3600
}
```
To run a full OAuth 2.0 flow instead, use a reference implementation such as [java-oauth-server][jos].
**Congratulations!** You have succeeded in protecting APIs built on Amazon API Gateway by OAuth access tokens using Amazon API Gateway Custom Authorizer! Congratulations!
[gs]: /get-started/quickstarts/getting-started
[intro]: /developers/definitive_guide/protected_resource/#authlete-introspection-api
[blog]: https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway
[use]: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
[jos]: https://github.com/authlete/java-oauth-server
[7519]: https://tools.ietf.org/html/rfc7519
[7662]: https://tools.ietf.org/html/rfc7662
[6750_21]: https://tools.ietf.org/html/rfc6750#section-2.1
# Amazon API Gateway + AWS Lambda + OAuth
Source: https://developers.authlete.com/deployment-and-operations/integration-with-api-gateways/amazon-api-gateway-lambda-oauth
Technical Information about how to protect a Web API implemented using Amazon API Gateway + AWS Lambda with an OAuth 2.0 access token.
# Overview
This document describes how to protect a Web API implemented using Amazon API Gateway + AWS Lambda with an OAuth 2.0 access token.
2016-Apr-6: Amazon API Gateway introduced Custom Authorizer on Feb 11, 2016. It should be utilized. See our new document [Amazon API Gateway Custom Authorizer + OAuth](/deployment-and-operations/integration-with-api-gateways/amazon-api-gateway-lambda-oauth).
# Prerequisites
The following sections assume:
* You have a lambda function `GetHelloWorld` that returns `{"Hello":"World"}`.
* You have `/mydemoresource` resource that supports `GET` method on Amazon API Gateway.
* You have a service in Authlete. A service is created automatically on sign-up and you can use it.
* You have a client application in Authlete. A client application is created automatically on sign-up and you can use it.
# Quick Guide
## Setup
1. \[API Gateway] Complete steps in [Getting Started with Amazon API Gateway][apig]
2. \[Authlete] Complete steps in [Getting Started][gs]
3. \[Lambda] Renew `GetHelloWorld` lambda function
Create a [Deployment Package](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html) and upload it as the implementation of `GetHelloWorld` lambda function.
```
$ mkdir gethelloworld
$ cd gethelloworld
$ npm install async request
```
Download [index.js](https://www.authlete.com/files/apigateway_lambda_oauth/index.js) and put it
in this directory.
```
$ vi index.js # Replace api_key and api_secret.
```
Create a ZIP file containing index.js and node\_modules directory.
4\. \[Lambda] Edit the timeout value
Increase the timeout value of the lambda function (e.g. to 30 sec).
5\. \[API Gateway] \[Method Request] Add `access_token` as a query parameter to `GET /mydemoresource`
6\. \[API Gateway] \[Integration Request] Set a template as is shown below
```
{ "access_token": "$input.params('access_token')" }
```
7. \[Amazon API Gateway] \[Method Response] Add 400, 401, 403 and 500 as HTTP status codes
8. \[Amazon API Gateway] \[Integration Response] Set mappings as shown below
| Lambda Error Regex | Method response status |
| - | - |
| BAD\_REQUEST:\* | 400 |
| UNAUTHORIZED:.\* | 401 |
| FORBIDDEN:.\* | 403 |
| INTERNAL\_SERVER\_ERROR:.\* | 500 |
## Try OAuth 2.0 Flow
1. Issue an access token
Authlete does not host an authorization server, so issue an access token for your service to test the integration. The quickest way is Authlete's Create Access Token API (`/api/{Service ID}/auth/token/create`), which mints an access token without running a full OAuth flow. Replace ``, `` and `` with your own values, and set `subject` and `scopes` to values supported by your service.
```bash theme={null}
curl -v -X POST https://us.authlete.com/api//auth/token/create \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ' \
-d '{ "grantType": "AUTHORIZATION_CODE", "clientId": , "subject": "john", "scopes": [ "read" ] }'
```
The issued access token is returned in the `accessToken` field of the response.
```json theme={null}
{
"action": "OK",
"accessToken": "Z5a40U6dWvw2gMoCOAFbZcM85q4HC0Z--0YKD9-Nf6Q",
"clientId": 26888344961664,
"subject": "john",
"tokenType": "Bearer",
"expiresIn": 3600
}
```
2. Access `/mydemoresource` endpoint with an access token
Access the URL below with your browser.
Don't forget to replace your-api-id, region-id and your-access-token with your own.
```
https://your-api-id.execute-api.region-id.amazonaws.com/test/mydemoresource?access_token=your-access-token
```
You will receive a JSON like below with HTTP status code 200 (OK).
```
{
"Hello": "World",
"clientId": 4326385670,
"subject": "authlete_5526908833"
}
```
[gs]: /developers/getting_started
[apig]: https://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started.html
# Amazon Cognito and Latest OAuth/OIDC Specifications
Source: https://developers.authlete.com/deployment-and-operations/integration-with-api-gateways/amazon-cognito-and-latest-oauth-oidc-specifications
A tutorial that explains how to use Amazon Cognito just as a user database and delegate OAuth/OIDC-related tasks to Authlete so that your system can continue to use Cognito and at the same time support the latest OAuth/OIDC specifications such as Financial-grade API.
# Introduction
*"**[Amazon Cognito][COGNITO]** user pools implements ID, access, and refresh tokens as defined by the OpenID Connect (OIDC) open standard"* --- excerpted from "[Using Tokens with UserPools][COGNITO_USING_TOKENS]"
However, because the OIDC implementation of Cognito is very limited and inflexible, it is common that Cognito's OIDC implementation cannot satisfy requirements of your system. For example, the signature algorithm of ID tokens issued by Cognito is `RS256` and there is no way to change it although the algorithm is prohibited by **[Financial-grade API][FAPI]** (**FAPI**) for security reasons.
This tutorial explains how to use Cognito just as a user database and delegate OAuth/OIDC-related tasks to **Authlete** so that your system can continue to use Cognito and at the same time support the latest OAuth/OIDC specifications such as Financial-grade API (cf. [Authlete Spec Sheet][SPEC_SHEET]).
# Architecture
In the OAuth 2.0 context, a server that issues **access token**s (and optionally **refresh token**s) is called **authorization server**. On the other hand, in the OpenID Connect context, a server that issues **ID token**s is called **OpenID Provider** (**IdP**). Because OIDC has been defined intentionally on top of OAuth 2.0, it is common that one server has both the roles. Therefore, the same server may be differently called "authorization server" or "IdP" depending on contexts. This tutorial refers to the server as "authorization server" uniformly.
To support the **authorization code flow** ([RFC 6749 Section 4.1][6749_41]), which is the most common flow in OAuth/OIDC, an authorization server has to implement two endpoints. They are called **authorization endpoint** ([RFC 6749 Section 3.1][6749_31]) and **token endpoint** ([RFC 6749 Section 3.2][6749_32]). Cognito User Pool provides implementations of the two endpoints, but you need to implement your own custom endpoints when Cognito's OIDC implementation is not satisfactory.
The diagram below illustrates the relationship among components in the authorization code flow when Cognito and Authlete are used combinedly.
The point in the diagram is that **user authentication is performed by Cognito but OAuth/OIDC-related tasks are delegated to Authlete**. Considering the fact that the core specification of OAuth 2.0 ([RFC 6749][6749]) states *"The way in which the authorization server authenticates the resource owner (e.g., username and password login, session cookies) is **beyond the scope of this specification**."*, this clear separation brings about much more benefits than the approach where a user management solution directly supports OAuth/OIDC.
The following sections describe in detail what the authorization server prepared for this tutorial does in its implementations of the authorization endpoint and the token endpoint.
## Authorization Endpoint
The authorization endpoint in the sample authorization server:
1. accepts an **authorization request** ([RFC 6749 Section 4.1.1][6749_411]) from a client application via a web browser,
2. extracts the query parameters of the authorization request,
3. passes the extracted query parameters to Authlete's [/api/auth/authorization][AUTH_AUTHORIZATION] API,
4. builds an **authorization page** based on the information returned from the Authlete API,
5. sends the authorization page back to the web browser,
6. gets the user's username and password from the user via the login form embedded in the authorization page,
7. passes the username and password to Cognito's [AdminInitiateAuth][ADMIN_INITIATE_AUTH] API to authenticate the user,
8. passes the username to Cognito's [AdminGetUser][ADMIN_GET_USER] API to get user attributes,
9. passes the user's subject (unique identifier) and user attributes to Authlete's [/api/auth/authorization/issue][AUTH_AUTHORIZATION_ISSUE] API,
10. builds an **authorization response** ([RFC 6749 Section 4.1.2][6749_412]) based on the information returned from the Authlete API, and
11. sends the authorization response to the web browser.
The authorization response will be delivered to the **redirection endpoint** (and in turn to the client application) because the HTTP status code of the authorization response is "`302 Found`" (unless `response_mode=form_post` is used).
Cognito's AdminInitiateAuth API issues an access token, an ID token and a refresh token. However, they are not used. Instead, the tokens are issued by Authlete.
## Token Endpoint
The token endpoint in the sample authorization server:
1. accepts a **token request** ([RFC 6749 Section 4.1.3][6749_413]) from a client application,
2. extracts the form parameters of the token request,
3. passes the extracted form parameters to Authlete's [/api/auth/token][AUTH_TOKEN] API,
4. builds a **token response** ([RFC 6749 Section 4.1.4][6749_414]) based on the information returned from the Authlete API, and
5. sends the token response to the client application.
# Implementation
The architecture explained above is implemented in **[django-oauth-server][DJANGO_OAUTH_SERVER]**, which is an open-source authorization server written in Python with the **[Django][DJANGO]** web framework. To run the server, follow the steps below.
## Setup Cognito
1. Create a Cognito user pool. Make sure that the `email` attribute is included because we will use the attribute later for testing.
2. Add a client to the Cognito user pool and enable `ALLOW_ADMIN_USER_PASSWORD_AUTH` in the "Auth Flows Configuration" section of the client configuration so that the client can use the "[Server-Side Authentication Flow][SERVER_SIDE_AUTHENTICATION_FLOW]".
3. Add a user to the Cognito user pool.
4. Make sure that your AWS account has permissions neccesary to call Cognito's [AdminInitiateAuth][ADMIN_INITIATE_AUTH] API and [AdminGetUser][ADMIN_GET_USER] API.
## Setup Authorization Server
Install necessary Python libraries.
```
$ pip install authlete # Authlete Library for Python
$ pip install authlete-django # Authlete Library for Django
$ pip install boto3 # AWS SDK for Python
```
Download the source code of the authorization server implementation.
```
$ git clone https://github.com/authlete/django-oauth-server.git
$ cd django-oauth-server
```
Edit the Authlete configuration file (`authlete.ini`) to access Authlete APIs.
```
$ vi authlete.ini
```
Open the Django configuration file (`django_oauth_server/settings.py`),
```
$ vi django_oauth_server/settings.py
```
and add `backends.CognitoBackend` to `AUTHENTICATION_BACKENDS`. See "[Specifying authentication backends][DJANGO_AUTH_BACKENDS]" in "[Customizing authentication in Django][DJANGO_AUTH_CUSTOMIZATION]" for details about Django authentication backends.
```python theme={null}
AUTHENTICATION_BACKENDS = ('backends.CognitoBackend',)
```
Also, edit `COGNITO_USER_POOL_ID` and `COGNITO_CLIENT_ID` in the same file properly.
```python theme={null}
COGNITO_USER_POOL_ID = 'YOUR_COGNITO_USER_POOL_ID'
COGNITO_CLIENT_ID = 'YOUR_COGNITO_CLIENT_ID'
```
If you are interested in how to call Cognito's [AdminInitiateAuth][ADMIN_INITIATE_AUTH] API and [AdminGetUesr][ADMIN_GET_USER] API, look into the source code `[cognito_backend.py][COGNITO_BACKEND_PY]`.
## Start Authorization Server
To start the authorization server, type the command below.
```
$ python manage.py runserver
```
"`make run`" does the same thing.
```
$ make run
```
The authorization server exposes some endpoints as listed in the table below. An easy way to confirm that Authlete setup (`authlete.ini`) is correct is to access the **discovery endpoint** ([http://localhost:8000/.well-known/openid-configuration](http://localhost:8000/.well-known/openid-configuration)) and see if it returns JSON conforming to [OpenID Connect Discovery 1.0][OIDC_DISCOVERY].
| Endpoint | URL |
| :- | :- |
| Authorization Endpoint | `http://localhost:8000/api/authorization` |
| Token Endpoint | `http://localhost:8000/api/token` |
| Discovery Endpoint | `http://localhost:8000/.well-known/openid-configuration` |
# Test
We have done all preparation. Let's get an access token and an ID token by the authorization code flow.
## Authorization Request
In the authorization code flow, the first step is to send an **authorization request** to the **authorization endpoint** of the authorization server via a web browser. In this tutorial, the authorization endpoint is `http://localhost:8000/api/authorization` hosted on django-oauth-server. Replace `CLIENT_ID` and `REDIRECT_URI` in the URL below (which represents an authorization request) properly and access the URL with your web browser.
```
http://localhost:8000/api/authorization?response_type=code&client_id=CLIENT_ID&scope=openid+email&state=123&nonce=abc&redirect_uri=REDIRECT_URI
```
Note that `CLIENT_ID` in the authorization request is NOT the client ID of the Cognito client. Instead, it must be a client ID issued by Authlete.
If you have not changed the redirection endpoint of the client, the default value "`https://api.authlete.com/api/mock/redirection/SERVICE_API_KEY`" can be used as the value of `REDIRECT_URI`. Don't forget to replace `SERVICE_API_KEY` properly in the default redirection endpoint when you use it as the value of `REDIRECT_URI`.
Your web browser will display an **authorization page** generated by the authorization server. It will look like below.
The page has input fields for Login ID and Password. Input the username and the password of the user that you have added to the Cognito user pool there and press "Authorize" button, and your web browser will be redirected to the redirection endpoint of your client application.
The URL of the redirection endpoint which you can see in the address bar of your browser contains `code` response parameter like below.
```
REDIRECT_URI?state=123&code=RwRq2Lp0bJVMiLPKAFz4qB1hxieBD1X5HKuv8EPkJeM
```
The value of `code` response parameter is the **authorization code** which has been issued from the authorization server to your client application. The authorization code is needed when your client application makes a token request.
## Token Request
After getting an authorization code, the client application sends a **token request** to the **token endpoint** of the authorization server. In this tutorial, the token endpoint is `http://localhost:8000/api/token` hosted on django-oauth-server.
A token request can be made by `curl` command in a shell terminal. Below is an example of token request. Don't forget to replace `CLIENT_ID`, `REDIRECT_URI` and `CODE` with the actual values before typing.
```
$ curl http://localhost:8000/api/token -d grant_type=authorization_code -d client_id=CLIENT_ID -d redirect_uri=REDIRECT_URI -d code=CODE
```
| Argument | Description |
| :- | :- |
| `http://localhost:8000/api/token` | The URL of the token endpoint. |
| `-d grant_type=authorization_code` | indicates that the flow is the authorization code flow. |
| `-d client_id=CLIENT_ID` | specifies the client ID. Replace `CLIENT_ID` with the actual client ID of your client application. |
| `-d redirect_uri=REDIRECT_URI` | specifies the redirect URI. Replace `REDIRECT_URI` with the same value given in the authorization request. |
| `-d code=CODE` | specifies the authorization code. Replace `CODE` with the actual authorization code. |
The lifetime of an authorization code is short. The default value is 10 minutes. A token request must be made before the authorization code expires.
When the token request succeeds, the token endpoint returns JSON that includes `access_token` and `id_token` like below.
```json theme={null}
{
"access_token": "FrGIJQpW51-l5mYJHcqGUNIKGJ1W23fFlW6c9AQEZEc",
"refresh_token": "jhvKm9-haLQwnIR4CfkL6bfPIBBlqluFeqZKAgdPNjM",
"scope": "email openid",
"id_token": "eyJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InRha2FAYXV0aGxldGUuY29tIiwiaXNzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInN1YiI6IjIiLCJhdWQiOlsiNDMyNjM4NTY3MCJdLCJleHAiOjE2MTY0MTI3NDIsImlhdCI6MTYxNjMyNjM0MiwiYXV0aF90aW1lIjoxNjE2MzI2MTAwLCJub25jZSI6ImFiYyIsInNfaGFzaCI6InBtV2tXU0JDTDUxQmZraG43OXhQdUEifQ.7sXy2FcELxHo3LCQkb9teLaUE9jtRxXsa8diJKnkwAo",
"token_type": "Bearer",
"expires_in": 86400
}
```
The value of `access_token` is the issued access token. Likewise, the value of `id_token` is the issued ID token.
The payload part of the issued ID token in this tutorial is decoded as follows. We can confirm that the authorization server has communicated with the Congnito user pool successfully by checking whether the value of `email` in the payload matches the `email` attribute of the user in the Cognito user pool.
```json theme={null}
{
"email": "taka@authlete.com",
"iss": "https://example.com",
"sub": "2",
"aud": [
"4326385670"
],
"exp": 1616412742,
"iat": 1616326342,
"auth_time": 1616326100,
"nonce": "abc",
"s_hash": "pmWkWSBCL51Bfkhn79xPuA"
}
```
# Congratulations!
You've completed this tutorial and learned how to enable your authorization server to use Amazon Cognito as a user database and at the same time support the latest OAuth/OIDC specifications by using Authlete.
**[Contact us][CONTACT]** if you need support. You are always welcome!
[6749]: https://tools.ietf.org/html/rfc6749
[6749_31]: https://tools.ietf.org/html/rfc6749#section-3.1
[6749_32]: https://tools.ietf.org/html/rfc6749#section-3.2
[6749_41]: https://tools.ietf.org/html/rfc6749#section-4.1
[6749_411]: https://tools.ietf.org/html/rfc6749#section-4.1.1
[6749_412]: https://tools.ietf.org/html/rfc6749#section-4.1.2
[6749_413]: https://tools.ietf.org/html/rfc6749#section-4.1.3
[6749_414]: https://tools.ietf.org/html/rfc6749#section-4.1.4
[ADMIN_GET_USER]: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminGetUser.html
[ADMIN_INITIATE_AUTH]: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminInitiateAuth.html
[AUTH_AUTHORIZATION]: /api-reference/authorization-endpoint/process-authorization-request
[AUTH_AUTHORIZATION_ISSUE]: /api-reference/authorization-endpoint/issue-authorization-response
[AUTH_TOKEN]: /api-reference/token-endpoint/process-token-request
[COGNITO]: https://aws.amazon.com/cognito/
[COGNITO_BACKEND_PY]: https://github.com/authlete/django-oauth-server/blob/master/backends/cognito_backend.py
[COGNITO_USING_TOKENS]: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html
[CONTACT]: https://www.authlete.com/contact/
[DJANGO]: https://www.djangoproject.com/
[DJANGO_AUTH_BACKENDS]: https://docs.djangoproject.com/en/3.1/topics/auth/customizing/#specifying-authentication-backends
[DJANGO_AUTH_CUSTOMIZATION]: https://docs.djangoproject.com/en/3.1/topics/auth/customizing/
[DJANGO_OAUTH_SERVER]: https://github.com/authlete/django-oauth-server
[FAPI]: https://fapi.openid.net/
[OIDC_DISCOVERY]: https://openid.net/specs/openid-connect-discovery-1_0.html
[SERVER_SIDE_AUTHENTICATION_FLOW]: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#amazon-cognito-user-pools-server-side-authentication-flow
[SPEC_SHEET]: https://www.authlete.com/legal/spec_sheet/
# API Gateway Products that have been Integrated with Authlete
Source: https://developers.authlete.com/deployment-and-operations/integration-with-api-gateways/api-gateway-products-that-have-been-integrated-with-authlete
Technical information about API gateway products integrated with Authlete such as Amazon API Gateway Microsoft Azure API Management Google Apigee and others.
*API gateway and Authlete*
Deploying API gateways is not necessary for Authlete itself to work. But in some cases integration between API gateways and Authlete would be valuable.
API gateways can leverage Authlete to enhance/replace their OAuth authorization server function. We have recognized the following products that have been integrated with Authlete. (no particular order. other products to be added as soon as confirmed)
* [Amazon API Gateway](/deployment-and-operations/integration-with-api-gateways/amazon-api-gateway)
* Microsoft Azure API Management
* Google Apigee
* IBM API Connect
* Kong
* MuleSoft
# Financial-grade Amazon API Gateway
Source: https://developers.authlete.com/deployment-and-operations/integration-with-api-gateways/financial-grade-amazon-api-gateway
A tutorial to explain how to protect APIs built on Amazon API Gateway more securely than ever before by utilizing certificate-bound access tokens (RFC 8705).
# Introduction
This tutorial shows how to protect APIs built on **[Amazon API Gateway][APIGW]** more securely than ever before by utilizing "**certificate-bound access tokens**".
Once a traditional OAuth access token is leaked, an attacker can access APIs with the access token. Traditional access tokens are like a train ticket which anyone can use once it is stolen.
The vulnerability can be mitigated by requiring the API caller to present not only an access token but also evidence that proves the API caller is the legitimate holder of the access token. The evidence is called "**proof of possession**", which is often shortened to **PoP**. Access tokens that need PoP on their use are like a plane ticket for international flight whose boarding procedure requires the passenger to present not only the ticket but also her passport.
**[RFC 8705][8705]**, **OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens**, has standardized a PoP mechanism. The mechanism is called **MTLS** in the OAuth community, but I personally call it "**certificate binding**" to avoid confusion. Anyway, in short, the mechanism requires the API caller to present not only an access token but also the same **[X.509 certificate][X509]** as was used when the access token was issued from the token endpoint. The diagram below illustrates the concept of certificate binding.
You may know **[Financial-grade API][FAPI]** (FAPI). It is a specification built on top of **OAuth 2.0** and **[OpenID Connect][OIDC]** for enhanced API security. [UK Open Banking][UKOB] has adopted FAPI as the base of Open Banking Profile and now other countries are following. The point I want you to pay attention to here is that FAPI requires certificate binding as a must technical component.
The major premise for cetificate binding is that connections between APIs and client applications are established by using **mutual TLS** where client applications are required to present their X.509 **client certificate** during TLS handshake. On September 17, 2020, AWS finally announced in *"[Introducing mutual TLS authentication for Amazon API Gateway][MTLSIntro]"* that Amazon API Gateway has become ready for mutual TLS. Amazon API Gateway has opened a door to Financial-grade API security.
# Authorizer's Role
Amazon API Gateway provides a mechanism called **[Lambda Authorizer][LAUTH]** whereby you can implement custom logic for API protection. A Lambda authorizer that offers OAuth-based API protection extracts an access token and a client certificate from an API call and performs the following validation.
1. the access token exists,
2. the access token has not expired,
3. the access token covers scopes required to access the resource,
4. the access token has not been revoked, and
5. the access token is bound to the client certificate. (**certificate binding**)
Then, the authorizer takes one of the following actions based on the result of the validation.
* return an IAM policy that allows the resource access.
* return an IAM policy that denies the resource access.
* throw an exception with an error message `'Unauthorized'` to tell Amazon API Gateway to reject the resource access with HTTP status code "401 Unauthorized".
* throw an exception with a different error message to tell Amazon API Gateway to reject the resource access with HTTP status code "500 Internal Server Error".
You may choose "format 2.0" for Lambda authorizer response where you may return a simpler response than IAM policy. See [Lambda authorizer response format](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.payload-format-response) for details.
Is there any other way to tell Amazon API Gateway to respond with "401 Unauthorized" instead of throwing an exception? Let us know if you know. A Lambda authorizer should be able to specify a value for `WWW-Authenticate` HTTP header and make Amazon API Gateway use it in order to conform to
[RFC 6750](https://tools.ietf.org/html/rfc6750), The OAuth 2.0 Authorization Framework: Bearer Token Usage.
# Authorizer Implementation
The lambda authorizer implementation I'm going to show you soon delegates validation of access token to **Authlete**'s introspection API (`/api/auth/introspection`), so the authorizer implementation won't contain complex logic. The diagram below depicts the relationship among Amazon API Gateway, Lambda Authorizer and Authlete.
In addition, because [Authorizer][Authorizer] class in [Authlete's Python library][AuthletePython] does almost all the necessary stuff, implementations can be very small. Actually, the code below is a complete example of Lambda authorizer implementation that supports certificate binding.
```python theme={null}
from authlete.aws.apigateway.authorizer import Authorizer
authorizer = Authorizer()
def lambda_handler(event, context):
return authorizer.handle(event, context)
```
## Scopes Required For Resource Access
In real cases, however, you will need to configure which resource requires what scopes. This can be achieved by either (1) giving a function to `Authorizer`'s `handle()` method or (2) making a subclass of `Authorizer` and overriding `determine_scopes()` method in the subclass.
Both the function and the method take 4 arguments as listed in the table below and are required to return a list of scope names that are necessary to access the resource.
| Argument | Description |
| :- | :- |
| `event` | The event given to the authorizer. |
| `context` | The context given to the authorizer. |
| `method` | The HTTP method of the resource access. |
| `path` | The path of the resource. |
Two examples below have the same effect stating that `time:query` scope is required to access `time` resource by HTTP `GET` method.
```python theme={null}
from authlete.aws.apigateway.authorizer import Authorizer
authorizer = Authorizer()
def determine_scopes(event, context, method, path):
if method == 'GET' and path == 'time':
return ['time:query']
return None
def lambda_handler(event, context):
return authorizer.handle(event, context, determine_scopes)
```
```python theme={null}
from authlete.aws.apigateway.authorizer import Authorizer
class CustomAuthorizer(Authorizer):
def determine_scopes(self, event, context, method, path):
if method == 'GET' and path == 'time':
return ['time:query']
return None
authorizer = CustomAuthorizer()
def lambda_handler(event, context):
return authorizer.handle(event, context)
```
## Policy Or Exception
To conform to specifications related to OAuth 2.0, a Lambda authorizer has to tell Amazon API Gateway to return "401 Unauthorized" to the API caller in some cases (e.g. when the presented access token has expired). According to AWS's technical documents and sample programs, the authorizer has to throw an exception with a message `'Unauthorized'` to achieve it. However, such simple exception drops all valuable information about the "Unauthorized" response and makes debugging very hard.
Therefore, by default, in other words, when `policy` property is `True` (default), `Authorizer`'s `handle()` method always returns an IAM policy which represents either "Allow" or "Deny" even in error cases where an exception with a message `'Unauthorized'` should be thrown.
If you want to make `Authorizer` throw an exception in "Unauthorized" and "Internal Server Error" cases, set `False` to `policy` property. You can achieve it by giving `policy=False` to `Authorizer`'s constructor.
```python theme={null}
authorizer = Authorizer(policy=False)
```
## Context In Policy
`Authorizer`'s `handle()` method returns a `dict` instance which represents an IAM policy. In the dictionary, there is `context` key whose value is a dictionary. `Authorizer` embeds some pieces of information there. The table below shows keys that `context` dictionary may contain.
| Property | Description |
| :- | :- |
| `introspection_request` | JSON string that represents the request to Authlete's introspection API. |
| `introspection_response` | JSON string that represents the response from Authlete's introspection API. |
| `introspection_exception` | String that represents an exception raised during the call to Authlete's introspection API. |
| `scope` | String of a space-delimited list of scopes covered by the presented access token. |
| `client_id` | The client ID of the client application to which the access token was issued. |
| `sub` | String that represents the subject of the resource owner who permitted issuance of the access token to the client application. |
| `exp` | The expiration datetime of the access token in seconds since the Unix epoch (January 1, 1970). |
| `challenge` | The value for `WWW-Authenticate` HTTP header in error cases. |
| `action` | The value of `action` in the response from the Authlete's introspection API. |
| `resultMessage` | The value of `resultMessage` in the response from the Authlete's introspection API. |
You can add entries to `context` by overriding `update_policy_context()` method in a subclass of `Authorizer`. Be careful not to use JSON object and array as values in `context`. This is a technical restriction imposed by AWS.
```python theme={null}
class CustomAuthorizer(Authorizer):
def update_policy_context(self, event, context, request, response, exception, ctx):
ctx.update({
'my_key': 'my_value'
})
```
Entries in `context` in the policy returned from a Lambda authorizer can be used at other places later. See *"[Output from an Amazon API Gateway Lambda authorizer][LAUTHOUT]"* for details.
## Hooks
`Authorizer` class offers following hook methods for subclass implementations. Override them as necessary.
| Method | Description |
| :- | :- |
| `determine_scopes()` | determines scopes required for the resource access. |
| `update_policy_context()` | updates `context` that is to be embedded in the policy. |
| `on_enter()` | is called when `handle()` method starts. |
| `on_introspection_error()` | is called when the call to Authlete introspection API failed. |
| `on_introspection()` | is called after Authlete introspection API succeeded. |
| `on_allow()` | is called when an Allow policy is generated. |
| `on_deny()` | is called when a Deny policy is generated. |
| `on_unauthorized()` | is called when an exception for "Unauthorized" is thrown. |
| `on_internal_server_error()` | is called when an exception for "Internal Server Error" is thrown. |
# Authorizer Configuration
## Lambda Event Payload
The most important thing in creating a Lambda authorizer is to **choose "Request" for Lambda Event Payload**. Otherwise, the authorizer cannot access information about the client certificate. It means that the authorizer cannot check whether the access token is bound to the client certificate.
See *"[Input to an Amazon API Gateway Lambda authorizer][LAUTHIN]"* for details about how the choice of Lambda Event Payload type changes the input to the authorizer.
## Environment Variables
If an instance of [AuthleteApi][AuthleteApi] is not given, `Authorizer`'s constructor internally creates one by calling `AuthleteApiImpl(AuthleteEnvConfiguration())` and uses the instance to access Authlete APIs. Because [AuthleteEnvConfiguration][AuthleteEnvConfiguration] used there assumes that Authlete configuration is available via environment variables, the following environment variables need to be set to the Lambda function that is used as the implementation of your Lambda authorizer.
| Environment variable | Description |
| :- | :- |
| `AUTHLETE_BASE_URL` | The base URL of Authlete server. |
| `AUTHLETE_SERVICE_APIKEY` | The API key assigned to your service. |
| `AUTHLETE_SERVICE_APISECRET` | The API secret assigned to your service. |
## Timeout
It is recommended to increase the timeout value of the Lambda function from the default value because the call to Authlete's introspection API may happen to take more time depending on various conditions.
# Authorizer Packaging
How to create and upload a ZIP package of Lambda function is explained at *"[Updating a function with additional dependencies][LPKG_DEP]"* in *"[AWS Lambda deployment package in Python][LPKG]"*.
Below is an example that creates and uploads a ZIP file of Lambda authorizer with the [authlete][AuthletePython] package.
```
~$ mkdir authorizer
~$ cd authorizer
~/authorizer$ vi lambda_function.py
~/authorizer$ pip install --target ./package authlete
~/authorizer$ (cd package; zip -r9 ../function.zip .)
~/authorizer$ zip -g function.zip lambda_function.py
~/authorizer$ aws lambda update-function-code --function-name authorizer --zip-file fileb://function.zip
```
# Testing
Testing may be the hardest part in this tutorial because there are many steps to set up the testing environment illustrated below.
Let's take the following steps one by one together.
1. Prepare an **authorization server** that supports certificate binding.
2. Prepare a **server certificate** for the authorization server.
3. Set up a **reverse proxy** for mutual TLS at the token endpoint of the authorization server.
4. Prepare a **client application** that is configured for certificate binding.
5. Prepare a **client certificate** for the client application.
6. Set up a **custom domain** for Amazon API Gateway.
7. Get a **certificate-bound access token**.
8. **Access a resource** (make an API call).
## Authorization Server
Let's use [java-oauth-server][java-oauth-server] as an authorization server. It's a sample implementation of authorization server written in Java that uses Authlete as a backend service.
```
$ git clone https://github.com/authlete/java-oauth-server
$ cd java-oauth-server
$ vi authlete.properties
$ docker-compose up
```
Command lines above will start an authorization server (java-oauth-server) on your local machine at `http://localhost:8080`.
Then, login [Authlete Management Console][SO] and configure the service corresponding to the authorization server so that it can support certificate binding.
## Server Certificate
Create a private key and then a self-signed certificate for the authorization server.
```
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 > server_private_key.pem
$ openssl req -x509 -key server_private_key.pem -subj /CN=localhost > server_certificate.pem
```
Note that `openssl` command used in this tutorial is OpenSSL's. Because `openssl` command installed on macOS is LibraSSL's since High Sierra, you have to install OpenSSL's `openssl` to try command lines in this tutorial as they are.
```
$ /usr/bin/openssl version -a
LibreSSL 2.6.5
......
$ brew install openssl
$ /usr/local/opt/openssl/bin/openssl version -a
OpenSSL 1.1.1g 21 Apr 2020
......
```
## Reverse Proxy
Because java-oauth-server itself does not protect its endpoints by TLS, a reverse proxy needs to be placed in front of java-oauth-server to accept TLS connections. Below is an example of configuration file that sets up [Nginx][NGINX] as a reverse proxy.
```nginx theme={null}
events {}
http {
server {
# Accept TLS connections at port 8443.
listen 8443 ssl;
# Server certificate in PEM format
ssl_certificate /path/to/server_certificate.pem;
# Servicer private key in PEM format
ssl_certificate_key /path/to/server_private_key.pem;
# Enable mutual TLS. 'optional_no_ca' requests a client certificate but
# does not require it to be signed by a trusted CA certificate. This is
# enough for this tutorial. See the document of ngx_http_ssl_module for
# details: http://nginx.org/en/docs/http/ngx_http_ssl_module.html
ssl_verify_client optional_no_ca;
# Pass the client certificate in the mutual TLS connection to the proxied
# server (java-oauth-server) as the value of 'X-Ssl-Cert' HTTP header.
# The proxied server has to be able to recognize the HTTP header, and
# java-oauth-server does recognize it. To be exact, authlete-java-jaxrs
# library used by java-oauth-server recognizes it.
proxy_set_header X-Ssl-Cert $ssl_client_escaped_cert;
# Pass requests that Nginx receives at 'https://localhost:8443/token' to
# 'http://localhost:8080/api/token' ('/api/token' of java-oauth-server).
# Note that TLS is terminated here.
location = /token {
proxy_pass http://localhost:8080/api/token;
}
}
}
```
This configuration makes Nginx run at `https://localhost:8443` and forward requests to `/token` to `http://localhost:8080/api/token`.
If the name of the configuration file is `nginx.conf`, Nginx can be started by typing the command below.
```
$ nginx -c $PWD/nginx.conf
```
When you want to stop Nginx, type this:
```
$ nginx -s stop
```
## Client Application
Login [Authlete Management Console][DC] and change the configuration of a client application you are going to use for testing to enable certificate binding.
## Client Certificate
Create a private key and then a self-signed certificate for the client application.
```
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 > client_private_key.pem
$ openssl req -x509 -key client_private_key.pem -subj /CN=client.example.com > client_certificate.pem
```
Create one more pair of private key and certificate to use for testing later. Make sure to specify a different value for the common name (for `/CN=`) because the custom domain configuration of Amazon API Gateway, which we'll cover in the next section, rejects a truststore file that contains certificates having the same subject.
```
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 > client_private_key_2.pem
$ openssl req -x509 -key client_private_key_2.pem -subj /CN=client2.example.com > client_certificate_2.pem
```
## Custom Domain
To enable mutual TLS on Amazon API Gateway, at the time of this writing, you have to assign a **custom domain** (e.g. `api.example.com`) to your API.
The Web console of Amazon API Gateway provides "Custom domain names" menu. To set up a custom domain there smoothly, it is better to prepare the following items beforehand.
1. **Server certificate** for the custom domain
2. **Truststore**, a file containing trusted client certificates in PEM format
### Server Certificate For Custom Domain
A server certificate for a custom domain for Amazon API Gateway must be under management of **[AWS Certificate Manager][ACM]** (ACM). You can import existing certificates or create new ones at [ACM console][ACMConsole]. However, imported certificates cannot be used for custom domains for Amazon API Gateway when mutual TLS is enabled. So, create a new one there.
### Truststore
When you setup mutual TLS, you will be asked to input the location of a file that contains trusted client certificates. The file is called "**truststore**". The implementation of Amazon API Gateway mutual TLS checks whether the client certificate presented during the TLS handshake is included in the truststore. If it is not included, Amazon API Gateway rejects the connection without invoking a Lambda authorizer.
Truststore is a text file listing client certificates in PEM format like below.
```PEM theme={null}
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
...
```
See *"[Illustrated X.509 Certificate](https://medium.com/@darutk/illustrated-x-509-certificate-84aece2c5c2e)"* for details about the PEM format.
A truststore for this tutorial can be created by typing the commands below.
```
$ cat client_certificate.pem > truststore.pem
$ cat client_certificate_2.pem >> truststore.pem
```
The truststore needs to be uploaded to **[S3][S3]** so that Amazon API Gateway can refer to it.
```
$ aws s3 cp truststore.pem s3://{your-s3-bucket}
```
Steps above are described in *"[Configuring mutual TLS authentication for a REST API][MTLSConfig]"*. See the document for details.
### Custom Domain Setup
Now you are ready to register a custom domain to Amazon API Gateway.
Enable **Mutual TLS authentication** in "Domain details" box, and a field for Truststore URI will appear. Input the S3 URI of your truststore into the field.
Then, select the server certificate for the custom domain in "Endpoint configuration" box.
After registering a custom domain, you can configure how to map "API" & "Stage" to "Path" under the custom domain. In the screenshot below, "Example" API's `dev` stage is mapped to the custom domain's path `dev`.
### Routing
The final step for custom domain configuration is to add a CNAME record to your DNS server so that the custom domain can be routed to "API Gateway domain name".
## Certificate-Bound Access Token
We have done all preparation. Let's get a **certificate-bound access token** by the **[authorization code flow][6749_41]**.
### Authorization Request
In the authorization code flow, the first step is to send an **authorization request** to the **[authorization endpoint][6749_31]** of the authorization server via a web browser. In this tutorial, the authorization endpoint is `http://localhost:8080/api/authorization` hosted on java-oauth-server. Replace `${CLIENT_ID}` in the URL below that represents an authorization request with the actual client ID of your client application and then access the URL with your web browser.
```
http://localhost:8080/api/authorization?response_type=code&client_id=${CLIENT_ID}&scope=profile+email&state=123
```
Your web browser will display an **authorization page** generated by the authorization server. It will look like below.
The page has input fields for Login ID and Password. Input `john` and `john` there and press "Authorize" button, and your web browser will be redirected to the redirection endpoint of your client application.
`john` and `john` are values hard-coded in the dummy database in java-oauth-server.
Append `&prompt=login` at the end of the authorization request to show the input fields again when you revisit the authorization endpoint. Regarding the `prompt` parameter, see "[3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)" in "[OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)".
If you have not changed **redirect URI** of your client application from the default value, the URL of the redirection endpoint is `https://{authlete-server}/api/mock/redirection/{service-api-key}`.
The URL of the redirection endpoint which you can see in the address bar of your browser contains `code` response parameter like below.
```
https://{authlete-server}/api/mock/redirection/{service-api-key}?state=123&code=RwRq2Lp0bJVMiLPKAFz4qB1hxieBD1X5HKuv8EPkJeM
```
The value of `code` response parameter is the **authorization code** which has been issued from the authorization server to your client application. The authorization code is needed when your client application makes a token request.
### Token Request
After getting an authorization code, the client application sends a **token request** to the **[token endpoint][6749_32]** of the authorization server. In this tutorial, the token endpoint is `https://localhost:8443/token` hosted on Nginx.
A token request can be made by `curl` command in a shell terminal. Below is an example of token request. Don't forget to replace `$CLIENT_ID` and `$CODE` with the actual client ID and the actual authorization code before typing.
```
$ curl -k --key client_private_key.pem --cert client_certificate.pem https://localhost:8443/token -d grant_type=authorization_code -d client_id=$CLIENT_ID -d code=$CODE
```
| Argument | Description |
| :- | :- |
| `-k` | not verify the server certificate. Because this tutorial uses a self-signed server certificate, this option is necessary. |
| `--key client_private_key.pem` | specifies the private key of the client. |
| `--cert client_certificate.pem` | specifies the certificate of the client. |
| `https://localhost:8443/token` | The URL of the token endpoint. |
| `-d grant_type=authorization_code` | indicates that the flow is the authorization code flow. |
| `-d client_id=$CLIENT_ID` | specifies the client ID. Replace `$CLIENT_ID` with the actual client ID of your client application. |
| `-d code=$CODE` | specifies the authorization code. Replace `$CODE` with the actual authorization code. |
The point here is that it is necessary to pass the client's private key and certificate to `curl` command by using `--key` option and `--cert` option because the token endpoint requires mutual TLS (i.e. requires a client certificate during TLS handshake). **The access token being issued from the token endpoint will be bound to the client certificate used in the token request.**
When a token request succeeds, the token endpoint returns JSON that includes `access_token` property like below.
```json theme={null}
{
"access_token": "b5qgqkXpzObRyceBqKeGPDCT9NX9GGXSt_oSYBSj7GQ",
"refresh_token": "1iSpvpeznTzwdUJzwRbt-abqE4znWn_yhN5PbBKV9zw",
"scope": "email profile",
"token_type": "Bearer",
"expires_in": 86400
}
```
The value of the `access_token` property is the issued access token. The client application uses the access token when it makes API calls.
## Resource Access (API Call)
At last, we have become ready to access resources (APIs) on Amazon API Gateway which are protected by certificate-bound access tokens.
First, access a resource with the access token and the right client certificate. Replace `${ACCESS_TOKEN}`, `${CUSTOM_DOMAIN}` and `${RESOURCE}` in the example below with actual values of yours. If all things have been set up correctly, you can get the resource successfully without being blocked.
```
$ curl --key client_private_key.pem --cert client_certificate.pem -H "Authorization: Bearer ${ACCESS_TOKEN}" https://${CUSTOM_DOMAIN}/${RESOURCE}
```
Next, access the resource with the same access token and a wrong client certificate (`client_certificate_2.pem` in this tutorial).
```
$ curl --key client_private_key_2.pem --cert client_certificate_2.pem -H "Authorization: Bearer ${ACCESS_TOKEN}" https://${CUSTOM_DOMAIN}/${RESOURCE}
```
You will receive the following error response.
```json theme={null}
{"Message":"User is not authorized to access this resource with an explicit deny"}
```
This inidicates that Amazon API Gateway rejected the resource access. The most important point here is that **the resource access was rejected although the access token was a legitimate one and it was because the client certificate presented together was not the legitimate one bound to the access token**. This is certificate binding.
# Congratulations!
You've completed this tutorial and now can protect your APIs on Amazon API Gateway more securely than ever before utilizing certificate binding ([RFC 8705][8705])!
**[Contact us][CONTACT]** if you need support. You are always welcome!
[6749_31]: https://tools.ietf.org/html/rfc6749#section-3.1
[6749_32]: https://tools.ietf.org/html/rfc6749#section-3.2
[6749_41]: https://tools.ietf.org/html/rfc6749#section-4.1
[8705]: https://www.rfc-editor.org/rfc/rfc8705.html
[ACM]: https://aws.amazon.com/certificate-manager/
[ACMConsole]: https://console.aws.amazon.com/acm/home
[APIGW]: https://aws.amazon.com/api-gateway/
[AuthleteApi]: https://github.com/authlete/authlete-python/blob/master/authlete/api/authlete_api.py
[AuthleteEnvConfiguration]: https://github.com/authlete/authlete-python/blob/master/authlete/conf/authlete_env_configuration.py
[AuthletePython]: https://github.com/authlete/authlete-python
[Authorizer]: https://github.com/authlete/authlete-python/blob/master/authlete/aws/apigateway/authorizer.py
[CONTACT]: https://www.authlete.com/contact/
[DC]: https://console.authlete.com/
[FAPI]: https://openid.net/wg/fapi/
[java-oauth-server]: https://github.com/authlete/java-oauth-server
[LAUTH]: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
[LAUTHIN]: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-input.html
[LAUTHOUT]: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
[LPKG]: https://docs.aws.amazon.com/lambda/latest/dg/python-package.html
[LPKG_DEP]: https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-dependencies
[MTLSConfig]: https://docs.aws.amazon.com/apigateway/latest/developerguide/rest-api-mutual-tls.html
[MTLSIntro]: https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/
[NGINX]: https://nginx.org/
[OIDC]: https://openid.net/connect/
[S3]: https://aws.amazon.com/s3/
[SO]: /v2/deployment-and-operations/service-owner-console/overview
[UKOB]: https://www.openbanking.org.uk/
[X509]: https://medium.com/@darutk/illustrated-x-509-certificate-84aece2c5c2e
# Integration with External IdPs
Source: https://developers.authlete.com/deployment-and-operations/integration-with-api-gateways/integration-with-external-idps
An article that explains how to integrate Authlete with external IdPs.
# Preface
This document is a tutorial to describe how to integrate an Authlete backed authorization server (AS) with external identity providers (IdPs) so that you can build API authorization infrastructure with highly secure industry standards like [Financial-grade API](https://fapi.openid.net/) (FAPI) while offloading user authentication and management functions to such IdPs.
This integration would be beneficial for service providers that have already built a repository of user credentials and information using IDaaS (Identity as a Service), and been required to implement FAPI-based security provisions defined by ecosystems such as [UK Open Banking](https://www.openbanking.org.uk/), [Australian Consumer Data Right](https://www.cdr.gov.au/) and [Open Banking Brasil](https://openbankingbrasil.org.br/).
In this tutorial, you can see an integration example using [Okta](https://www.okta.com/) as an OpenID Connect (OIDC) IdP and [java-oauth-server](https://github.com/authlete/java-oauth-server) as an AS backed with Authlete.
# Prerequisites
These accounts would be needed before you begin this tutorial:
* An Authlete account (see [Using Demo Authorization Server](/get-started/quickstarts/using-demo-authorization-server))
* An Okta account (see [Okta website](https://www.okta.com/))
You also need to have an environment where you can install and run [java-oauth-server](https://github.com/authlete/java-oauth-server).
# Identity federation between AS and IdP
How an AS authenticates end-users is intentionally not defined in the specifications of OAuth 2.0 ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html)) and OIDC ([OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)). Therefore, AS implementations may choose to delegate end-user authentication to an external OIDC IdP, and obtain information about the user from the IdP. The diagram below illustrates the flow of **identity federation** between an AS and an external OIDC IdP.
The following is explanation about the flow in the diagram in words.
1. A **client application** sends an **authorization request** to the **authorization endpoint** of the **authorization server** (**AS**) via a web browser.
2. The authorization endpoint of the AS generates an **authorization page** and returns it to the web browser. If the AS supports **identity federation** with external OpenID Providers, the authorization page includes links for identity federation.
3. The end-user clicks a link for identity federation in the displayed authorization page. The click makes the web browser access a Web API ("(federation/initiation)" in the diagram) which initiates identity federation with the chosen OpenID Provider.
4. The Web API builds an **authentication request** that conforms to **OpenID Connect**. The authentication request is a URL that points to the authorization endpoint of the **OpenID Provider** (**OP**) and includes request parameters in the query part.
5. The Web API returns `302 Found` (or whatever triggers redirection) with the `Location` header to the web browser in order to make the web browser send the authentication request to the authorization endpoint of the OP.
6. The web browser sends the authentication request to the authorization endpoint of the OP.
7. The authorization endpoint of the OP generates an authorization page and returns it to the web browser. The authorization page tells that the AS (not the Client) is requesting permissions.
8. The end-user authenticates herself at the OP and authorizes the authorization request from the AS.
9. The OP generates an **authorization code** and returns `302 Found` (or whatever triggers redirection) with the `Location` header to the web browser in order to make the web browser access the **redirection URI** of the AS (not of the Client) with the authorization code. From a viewpoint of the OP, the AS is a client application and the redirection URI is one among those the AS has registered to the OP in advance.
10. The redirection endpoint of the AS ("(federation/callback)" in the diagram) receives the authorization code.
11. The redirection endpoint sends a **token request** to the **token endpoint** of the OP with the issued authorization code.
12. The token endpoint of the OP generates an **access token** and an **ID token** and returns them.
13. The redirection endpoint accesses the **userinfo endpoint** of the OP with the issued access token.
14. The userinfo endpoint of the OP returns information about the end-user.
15. The redirection endpoint regards that the end-user has logged in the AS, re-generates an authorization page and returns it to the web browser.
16. The end-user authorizes the original authorization request from the Client.
17. The AS generates an authorization code and returns `302 Found` (or whatever triggers redirection) with the `Location` header to the web browser in order to make the web browser access the redirection URI of the Client (not of the AS) with the authorization code. This assumes that the Client has registered at least one redirection URI to the AS in advance.
18. The redirection endpoint of the Client ("Redirection Endpoint" in the diagram) receives the authorization code.
19. The Client sends a token request to the token endpoint of the AS with the authorization code which it received via the redirection endpoint.
20. The token endpoint of the AS generates an access token and returns it to the Client.
The next sections will introduce how to configure Okta as an IdP and integrate it with an Authlete-backed AS (java-oauth-server) to enable the flow shown above.
# Configuring IdP (Okta)
You need to create a new "**App Integration**" in the server which is hosted on Okta so that an AS can act as a client application of the OIDC IdP.
The table below shows points for App Integration settings.
| Item | Value |
| :- | :- |
| Sign-in method | OIDC - OpenID Connect |
| Application Type | Web Application |
| Sign-in redirect URIs | `http://localhost:8080/api/federation/callback/okta` |
| Controlled access | Allow everyone in your organization to access |
You may have noticed that the value of "Sign-in redirect URIs" implies that this tutorial will later instruct you to run an AS on your local machine at the port number 8080 and that the AS provides `/api/federation/callback/okta` API.
Images below are screenshots of App Integration settings.
After you create the App Integration, Okta issues a pair of **Client ID** and **Client secret**. The pair will be used in the next section.
# Configuring AS (java-oauth-server)
In this tutorial, we use **[java-oauth-server](https://github.com/authlete/java-oauth-server)** as an AS. It is an open-source sample implementation of AS that uses **Authlete** as backend.
First, download java-oauth-server and edit `authlete.properties`. You need to change the values of `service.api_key` and `service.api_secret` in the file at least.
```
$ git clone https://github.com/authlete/java-oauth-server
$ cd java-oauth-server
$ vi authlete.properties
```
Second, open `federations.json` with your text editor. This file describes configurations of identity federations. The initial content looks like below. Replace `YOUR_COMPANY`, `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` with actual values which have been assigned to you from Okta. See Appendix/[federations.json](#federations-json) for details about `federations.json`.
```json theme={null}
{
"federations": [
{
"id": "okta",
"server": {
"name": "Okta-hosted IdP",
"issuer": "https://YOUR_COMPANY.okta.com"
},
"client": {
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"redirectUri": "http://localhost:8080/api/federation/callback/okta",
"idTokenSignedResponseAlg": "RS256"
}
}
]
}
```
The above are all the necessary configuration. Start java-oauth-server on your local machine.
```
$ docker-compose up
# or mvn jetty:run
```
If you confirm logs like below, java-oauth-server is ready to work.
```
app_1 | [INFO] Started ServerConnector@7c5ae5c3{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
app_1 | [INFO] Started @41702ms
app_1 | [INFO] Started Jetty Server
```
# Walk-through
In the diagram shown in the "[Identity federation between AS and IdP](#id-federation)" section, the client uses the OIDC authorization code flow (`response_type=code`). But here, to quickly confirm that identity federation with Okta works, we use the OIDC implicit flow which issues an ID token only (`response_type=id_token`).
The URL below is an authorization request. Replace `CLIENT_ID` with a client ID issued from Authlete (not from Okta) and `SERVICE_API_KEY` with a service API key issued from Authlete, and then copy and paste the URL to the address bar of your web browser.
```
http://localhost:8080/api/authorization?client_id=CLIENT_ID&response_type=id_token&scope=address+email+openid+phone+profile&redirect_uri=https://api.authlete.com/api/mock/redirection/SERVICE_API_KEY&nonce=mynonce&state=mystate&prompt=login
```
You'll see an authorization page like below. Click the link "Okta-hosted IdP" which is located over the "Authorize" button.
The web browser will display the authorization page like below which was generated by Okta. Input your login ID and password which you use to login the server hosted on Okta.
The web browser will re-display the original authorization page generated by java-oauth-server. This time, java-oauth-server regards that you have logged in. Press the "Authorize" button.
java-oauth-server will issue an ID token and redirect the web browser to the redirection endpoint. The mock implementation of the redirection endpoint hosted on an Authlete server (`/api/mock/redirection/SERVICE_API_KEY`) displays parameters it receives.
When a request includes an unencrypted ID token, the redirection endpoint decodes the payload part of the ID token and shows it like below.
The table below indicates data sources of claims in the issued ID token. All the user attributes originate from Okta.
| Claim | Data Source |
| :- | :- |
| `email` | Okta |
| `email_verified` | Okta |
| `family_name` | Okta |
| `given_name` | Okta |
| `locale` | Okta |
| `name` | Okta |
| `phone_number_verified` | Okta |
| `preferred_username` | Okta |
| `updated_at` | Okta |
| `zoneinfo` | Okta |
| `iss` | Authlete / Service configuration, `issuer` |
| `sub` | java-oauth-server / `subject` parameter passed to Authlete's `/api/auth/authorization/issue` API |
| `aud` | Authlete / Client configuration, `clientId` (unmodifiable) |
| `exp` | Authlete / expiration date of the ID token |
| `iat` | Authlete / issue date of the ID token |
| `auth_time` | java-oauth-server / `authTime` parameter passed to Authlete's `/api/auth/authorization/issue` API |
| `nonce` | Client / the value of the `nonce` request parameter |
| `s_hash` | Authlete / computed based on the `state` request parameter |
You may wonder where the value of the `sub` claim (`"00ucabhbjLVphPrXF696@okta"` in the example) originates from. It is just a result of concatenating the value of the `sub` claim returned from Okta with the string `"@okta"`. The concatenation is conducted in `createUserEntity()` method in [FederationEndpoint.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/FederationEndpoint.java). There is no big meaning there and it is up to you how to determine the value of the `sub` claim.
# Conclusion
In this tutorial, we were able to confirm an integration pattern between an Authlete backed authorization server and an external identity provider, and a real walk-through using java-oauth-server and Okta.
# Appendix
## Implmenentation Details
You can learn implementation-level details about identity federation by looking into [Federation.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/federation/Federation.java) and [FederationEndpoint.java](https://github.com/authlete/java-oauth-server/blob/master/src/main/java/com/authlete/jaxrs/server/api/FederationEndpoint.java) of [java-oauth-server](https://github.com/authlete/java-oauth-server).
After receiving user information from the userinfo endpoint of an OIDC IdP, the current implementation of java-oauth-server registers a user record into its on-memory user database. However, commercial implementations may choose other approaches.
For example, an implementation may avoid copying user data from an OIDC IdP into its database and instead just remember the access token issued from the IdP so that the implementation can retrieve up-to-date user information from the userinfo endpoint of the IdP at any time it wants to.
To implement such behavior, the AS may prepare a database to remember access tokens issued from IdPs. Another possible approach is to use Authlete's **[Extra Properties](/configuration-reference/tokens-and-claims/how-to-add-extra-properties-to-an-access-token)** feature with which an AS can associate arbitrary key-value pairs with an access token and make Authlete remember IdP-issued access tokens. (cf. [How to add extra properties to an access token](/configuration-reference/tokens-and-claims/how-to-add-extra-properties-to-an-access-token))
## federations.json
`federations.json` is a configuration file that describes configurations of identity federations. [java-oauth-server](https://github.com/authlete/java-oauth-server) loads the file at the first time it receives an authorization request. Identity federations listed in the file will appear in the authorization page of java-oauth-server.
The default location of the file is `"federations.json"`. The environment variable `FEDERATIONS_FILE` and the system property `federations.file` can be used to specify a different location of the file.
The content of `federations.json` must be a JSON object which has `"federations"` as a top-level property like below.
```json theme={null}
{
"federations": [
"(configurations of identity federations)"
]
}
```
Each entry in the `"federations"` array represents a configuration of identity federation and must be a JSON object which has `"id"`, `"server"` and `"client"` as top-level properties.
```json theme={null}
{
"federations": [
{
"id": "(unique identifier among the configurations)",
"server": {
"(server configuration)"
},
"client": {
"(client configuration)"
}
}
]
}
```
The value of `"id"` is used as `federationId` in the following API paths of java-oauth-server.
* `/api/federation/initiation/federationId`
* `/api/federation/callback/federationId`
`"server"` describes the configuration of the target OpenID Provider. Its value is a JSON object which has properties shown in the table below.
| Property | Description |
| :- | :- |
| `name` | The display name of the OpenID Provider. This is used in the authorization page. |
| `issuer` | The issuer identifier of the OpenID Provider. The value must match the value of `"issuer"` in the discovery document of the OpenID Provider. The OpenID Provider must expose its discovery document at `{issuer}/.well-known/openid-configuration` so that identity federation of java-oauth-server can work. |
`"client"` describes the configuration of the Relying Party, which is always java-oauth-server. Note that java-oauth-server is a client application from a viewpoint of external OpenID Providers. The value of `"client"` is a JSON object which has properties shown in the table below.
| Property | Description |
| :- | :- |
| `clientId` | Client ID issued by the OpenID Provider. |
| `clientSecret` | Client secret issued by the OpenID Provider. If this property is set, token requests sent to the token endpoint of the OpenID Provider will include an `Authorization` header for client authentication. This behavior assumes that the token endpoint supports `client_secret_basic` as a method of client authentication. |
| `redirectUri` | A redirect URI that you have registered into the OpenID Provider. |
| `idTokenSignedResponseAlg` | The algorithm the OpenID Provider uses when it signs ID tokens. If this property is omitted, `"RS256"` is used as the default value. |
# Migrating Access Tokens Issued by other System to Authlete
Source: https://developers.authlete.com/deployment-and-operations/migration-from-existing-system/migrating-access-tokens-issued-by-other-system-to-authlete
Migrating access tokens from another system to Authlete with a detailed explanation of the process using the Authlete token management APIs.
## Preface
On replacing existing authorization server system (old system) to Authlete, you might have to consider how to manage access tokens which are issued by the old system.\
If you want Authlete to verify both old and new access tokens coming from resource servers (i.e. resource servers don't distinguish between old and new ones), both kinds of tokens must be managed by Authlete.
This article explains practice to migrate the access tokens issued by other system to Authlete.
***
## How to migrate
Authlete provides [/auth/token/create](https://docs.authlete.com/#auth-token-create-api) , one of token management APIs. It enables authorization server to have Authlete to create arbitrary tokens without any authorization request as well as token request. The following section in API reference explains parameters for the API.
* [/auth/token/create API](https://docs.authlete.com/#auth-token-create-api)
The accessToken parameter is optional but important one. If some value are specified to the parameter, Authlete takes it as a value of access token which is newly minted. By the way, Authlete hashes the value and stores it into token database.
Thus you can achieve token migration using this API to create a token, with the accessToken parameter specified a value of an existing token issued by the old system.
## Example
### Creating tokens
An administrator, who has information of previously issued tokens, makes a request to [/auth/token/create API](https://docs.authlete.com/#auth-token-create-api) to create new tokens with the old values. (folded for readability)
* Request
```shell highlight={7,9} theme={null}
curl -s -X POST .../auth/token/create \
-u ...:... \
-H 'Content-type: application/json' \
-d '{"grantType":"AUTHORIZATION_CODE", \
"clientId":"...", \
"subject":"john", \
"accessToken":"existingAccessTokenValue", \
"accessTokenDuration":3600, \
"refreshToken":"existingRefreshTokenValue", \
"refreshTokenDuration":86400, \
"properties":[ \
{ \
"hidden":true, \
"key":"amount", \
"value":"100" \
} \
], \
"scopes":["openid","payment"]}'
```
* Response
```json highlight={4,17} theme={null}
{
"resultCode": "A109001",
"resultMessage": "[A109001] An access token was created successfully: authorization_code, client = 17201083166161",
"accessToken": "existingAccessTokenValue",
"action": "OK",
"clientId": 17201083166161,
"expiresAt": 1594020768318,
"expiresIn": 3600,
"grantType": "AUTHORIZATION_CODE",
"properties": [
{
"hidden": true,
"key": "amount",
"value": "100"
}
],
"refreshToken": "existingRefreshTokenValue",
"scopes": [
"openid",
"payment"
],
"subject": "john",
"tokenType": "Bearer"
}
```
### Introspection
On receiving an API request with “the previously issued access token,” a resource server attempts to verify the token and obtain its related information. The following example shows these operations using Authlete's [/auth/introspection API](https://docs.authlete.com/#introspection-endpoint) . (folded for readability)
> If you are using [JWT-formatted access tokens](/configuration-reference/tokens-and-claims/using-jwt-based-access-tokens) , resource servers are responsible to verify the tokens and extract the related information.
* Request
```shell highlight={5} theme={null}
curl -s -X POST \
.../auth/introspection \
-u ...:... \
-H 'Content-type: application/json' \
-d '{"token":"existingAccessTokenValue"}'
```
* Response
```
{
"resultCode": "A056001",
"resultMessage": "[A056001] The access token is valid.",
"action": "OK",
"clientId": 17201083166161,
"clientIdAliasUsed": false,
"existent": true,
"expiresAt": 1594020768000,
"properties": [
{
"hidden": true,
"key": "amount",
"value": "100"
}
],
"refreshable": true,
"responseContent": "Bearer error=\"invalid_request\"",
"scopes": [
"openid",
"payment"
],
"subject": "john",
"sufficient": true,
"usable": true
}
```
### Refreshing tokens
On receiving a refresh token request with “the previously issued refresh token,” an authorization server attempts to process the token request by calling Authlete's [/auth/introspection API](https://docs.authlete.com/#introspection-endpoint) . (folded for readability)
* Request
```shell highlight={8} theme={null}
curl -s -X POST $AL_API/auth/token \
-u $AL_SVC \
-H 'Content-type: application/json' \
-d '{
"clientId": "...",
"clientSecret": "...",
"parameters": "grant_type=refresh_token
&refresh_token=existingRefreshTokenValue"
}'
```
* Response
```
{
"resultCode": "A053001",
"resultMessage": "[A053001] The token request (grant_type=refresh_token) was processed successfully.",
"accessToken": "a4utoeZ-R3WwpbD2q-HbNBlEkie2GZZyrdAmmoeuKd0",
"accessTokenDuration": 300,
"accessTokenExpiresAt": 1594018713161,
"action": "OK",
"clientId": 17201083166161,
"clientIdAliasUsed": false,
"grantType": "REFRESH_TOKEN",
"properties": [
{
"hidden": true,
"key": "amount",
"value": "100"
}
],
"refreshToken": "9JhH1r5-Q54-XApNhGmSS_dxwwUp_SjjoaX81gev_po",
"refreshTokenDuration": 900,
"refreshTokenExpiresAt": 1594019313161,
"responseContent": "{\"access_token\":\"a4utoeZ-R3WwpbD2q-HbNBlEkie2GZZyrdAmmoeuKd0\", \"refresh_token\":\"9JhH1r5-Q54-XApNhGmSS_dxwwUp_SjjoaX81gev_po\", \"scope\":\"openid payment\", \"token_type\":\"Bearer\", \"expires_in\":300}",
"scopes": [
"openid",
"payment"
],
"subject": "john"
}
```
***
## For migrating enormous tokens
If you are going to migrate tokens enormously, it might by appropriate to take another method; dump token information from the old system, and then transform and import them to Authlete. Please ask Authlete support representative for details.
***
## Supplemental information
* New Architecture of OAuth 2.0 and OpenID Connect Implementation
* [6.1. Access Token Creation](https://medium.com/@darutk/new-architecture-of-oauth-2-0-and-openid-connect-implementation-18f408f9338d#5922)
> Authlete's [/api/auth/token/create](/api-reference/token-operations/create-access-token) API is an example. By using the API, developers can create access tokens without user interaction.
# Migrating Settings to Authlete 3.0
Source: https://developers.authlete.com/deployment-and-operations/migration-from-existing-system/migrating-settings-from-an-older-version-of-authlete
Migrate services and clients from Authlete 2.2 or 2.3 to a new Authlete 3.0 environment.
Upgrading to Authlete 3.0 cannot be done directly to the running Authlete 2.x instance. Instead, a new Authlete 3.0 environment is created and all services and clients are migrated to it.
During services and clients migration, optionally access tokens can also be migrated via the Token Migrator. This is a new service introduced to handle token copying and updating from the Authlete 2.2/2.3 environment into the Authlete 3.0 environment which will allow existing tokens issued in the Authlete 2.2/2.3 environment to be valid and usable in the new Authlete 3.0 environment. See [Migrating Tokens to Authlete 3.0](/deployment-and-operations/migration-from-existing-system/migrating-tokens-from-an-older-version-of-authlete) for details.
# Migrating using the Configuration Migrator (Dedicated and Self-Managed)
## Downloading the Configuration Migrator
Steps 1 to 4 below are performed on the Authlete Shared environment ([console.authlete.com](https://console.authlete.com)), not on the destination environment. The organization and token you create there are used only to pull the image, and are separate from the destination environment's credentials in the `Preparation` section below.
To access the container image from the Authlete registry, follow these steps:
### 1. Create an Organization
* Log in to the [Authlete Management Console](https://console.authlete.com).
* Create an organization for your company.
* Note down the **Organization ID**.

*Organization creation page*
### 2. Request Access
* Share the **Organization ID** and **Organization Name** with Authlete Support.
* Authlete will authorize registry access for your organization.
### 3. Generate a Registry Access Token
* In the Authlete Management Console, generate a **Token** for your organization with the **Read Only** permission preset.
* This token is used only for `docker login`. Do not use it as the `V3_ORG_ACCESS_TOKEN` environment variable of the configuration migrator.
* Keep the **Organization ID** and **Token** handy for authentication.
*Organization Access Tokens section*
*Token creation dialog*
### 4. Authenticate and Pull Images
Log in to the Authlete registry with the **Organization ID** and the registry access token, then pull the image:
```bash theme={null}
# Authenticate to Authlete registry
docker login artifacts.authlete.com -u "${SHARED_ORGANIZATION_ID}" -p "${SHARED_ORGANIZATION_ACCESS_TOKEN}"
# Pull the image
docker pull artifacts.authlete.com/configuration-migrator:1.1
```
## Preparation
In order to begin migrating service and client configuration, you will need a running 2.2/2.3 source environment, and another running Authlete 3.0 destination environment. Each value below is set in the configuration migrator as the environment variable named next to it (see [Migration Settings](#migration-settings)).
From your 2.2/2.3 source environment:
* The API URL of the environment (e.g. `https://api.source-environment`): used for `V2_API_URL`
* Your Service Owner `API Key`, found in the `Your Account` page in the Service Owner Console: used for `V2_SO_API_KEY`
* Your Service Owner `API Secret`, found in the `Your Account` page in the Service Owner Console: used for `V2_SO_API_SECRET`
*Your Account page in the Service Owner Console*
From your 3.0 destination environment:
* The API URL of the environment (e.g. `https://api.destination-environment`): used for `V3_API_URL`
* The API server ID of the server that the services and clients will be imported in, and the ID of the organization that they will be created in: used for `V3_API_SERVER_ID` and `V3_ORG_ID`
* Select the cluster from the cluster drop-down in the console navigation bar. The page then shows the API server ID as `Cluster ID` under `Cluster Information`, and the organization ID as `Organization ID` under `Organization Overview`
*API server ID (Cluster ID) and organization ID (Organization ID)*
* Your IdP URL (e.g. `https://login.destination-environment`): used for `V3_IDP_URL`
* Your Organization Access Token for the organization that the services and clients will be created in: used for `V3_ORG_ACCESS_TOKEN`
* Can be issued from the `Organization Settings` page under the `General` tab of the 3.0 destination environment's console and must be created with the `create_service` and `modify_service` permissions
* It is a different token from the registry access token used to pull the image
*Organization access token for the destination environment*
## Prerequisites for Migrating Dynamically Registered Clients
If you plan on migrating dynamically registered clients ([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)), then you must have the following versions deployed to your environments and enable the appropriate feature flags in order for the migration endpoints for dynamically registered clients to be available. On Dedicated environments, contact Authlete support and request for these features to be enabled. On self-managed environments, deploy these versions and enable the feature flags yourself.
* For 2.2 source environments - Version `2.2.65+` must be deployed and the feature flag `DCR_RAT_ENDPOINT` must be enabled
* For 2.3 source environments - LTS Version `2.3.51+` must be deployed and the feature flag `DCR_RAT_ENDPOINT` must be enabled
* For 3.0 destination environments - Version `3.0.36+` must be deployed and the feature flag `MIGRATE_CLIENT_DCR_ENDPOINT` must be enabled
## How the Configuration Migrator Works
When it runs, the `configuration-migrator` performs the following steps:
1. It will fetch services/clients from the 2.2/2.3 source environment
2. For each of the services, it will check if the service already exists, and if not will create a service in the 3.0 environment via the IdP
3. For each client within the service, it will check whether the client already exists, and if not will create the client in the 3.0 environment
4. (If client secret migration is enabled) Whether the client was newly created or not, it will check and align the client secret credential, updating the 3.0 environment with the client secret value from the 2.2/2.3 source environment
5. (If dynamically registered client migration is enabled and the source client is a dynamically registered client) If the matching destination client is not already configured as a dynamically registered client, then it will finalize the client's configuration and set their registration access token from the 2.2/2.3 source client
6. (If client deletion is enabled) It will delete destination clients that no longer exist in the 2.2/2.3 source environment
By default, the migrator runs once and exits. Services and clients that already exist in the destination environment are not created again, but the client secrets and dynamic client registration configuration of existing clients are still aligned (steps 4 and 5). Other configuration on existing services and clients is not updated.
## Migration Settings
Once the 3.0 destination environment is running, the `configuration-migrator` can be used to move service and client configuration from your 2.2/2.3 source environment to your 3.0 destination environment. If you migrate dynamically registered clients, first deploy the Authlete versions and enable the feature flags listed in [Prerequisites for Migrating Dynamically Registered Clients](#prerequisites-for-migrating-dynamically-registered-clients).
The `configuration-migrator` performs this migration entirely through the Authlete APIs, so it will require environment endpoint and API credentials in order to successfully migrate the service and client configuration.
### Available Settings
#### Required Settings
| Environment Variable | Type | Description |
| - | - | - |
| `V2_API_URL` | URL | The base URL to the 2.2/2.3 source environment `server`. |
| `V2_SO_API_KEY` | String | The service owner "API Key" from the 2.2/2.3 source environment. Found in the "Your Account" page in the Service Owner Console. |
| `V2_SO_API_SECRET` | String | The service owner "API Secret" from the 2.2/2.3 source environment. Found in the "Your Account" page in the Service Owner Console. |
| `V3_API_URL` | URL | The base URL to the 3.0 destination environment `server`. |
| `V3_API_SERVER_ID` | Number | The API server ID of the server that you would like to import the services and clients into. |
| `V3_IDP_URL` | URL | The base URL to the 3.0 destination environment IdP. |
| `V3_ORG_ACCESS_TOKEN` | String | The organization token for the organization that the migrated services and clients should be created within. |
| `V3_ORG_ID` | Number | The organization ID of the organization that the migrated services and clients should be created within. |
#### Optional Settings
| Environment Variable | Type | Description | Default Value |
| - | - | - | - |
| `DRY_RUN` | Boolean | When enabled the migrator will be placed into dry run mode where only API retrieval actions will be performed. No new services or clients will be created, no client secrets will be updated and no clients will be deleted. All intended actions will be logged to understand what the migrator will execute when dry run is disabled. | false |
| `SYNC_CLIENT_TOKENS` | Boolean | When enabled newly imported clients will have the `inScopeForTokenMigration` property set to `true`. This will allow the Token Migrator to begin token migration from these clients if it is running. Having this flag enabled for clients in the 3.0 destination environment is a pre-condition for token migration so it is recommended to keep this enabled unless you do not intend to migrate tokens. | true |
| `MIGRATE_CLIENT_SECRETS` | Boolean | When enabled client's secrets will be migrated and updated in the 3.0 destination environment to match the client secret from the 2.2/2.3 source environment. Clients having the same client secret is a pre-condition for token migration so it is recommended to keep this enabled unless you do not intend to migrate tokens. | true |
| `MIGRATE_DCR_CLIENTS` | Boolean | When enabled dynamically registered clients from the source APIs will be migrated to the destination environment. The registration access token hash will be configured into the newly created destination client. If this setting is disabled dynamically registered clients will be skipped and not created in the destination environment. | true |
| `SERVICE_IDS` | Number\[] | Expected as a comma-separated list of service IDs. E.g. `12345,67891`. This property is used to filter and only perform migration on services whose ID is in this list along with the clients in these services. If this property is left empty then all services in the 2.2/2.3 source environment under the provided service owner account will be migrated. | \[] |
| `MAX_V2_RPS` | Number | The maximum requests per second (RPS) that the migrator will perform against the source 2.2/2.3 API as part of the migration. All outbound requests to the 2.2/2.3 API will not exceed this limit. | 5.0 |
| `MAX_V3_RPS` | Number | The maximum requests per second (RPS) that the migrator will perform against the destination 3.0 API as part of the migration. All outbound requests to the 3.0 API will not exceed this limit. | 5.0 |
| `RETRY_MAX` | Number | The maximum amount of retry attempts per endpoint. | 5 |
| `RETRY_BASE_DELAY` | Number | The base delay value used when calculating the backoff period between each retry. This value is in seconds. | 1.0 |
| `OUTPUT_DIR` | String (Path) | The migration summary output directory, mapped to `/app/migration-output` inside the container. | "./migration-output" |
| `CONTINUOUS_SYNC` | Boolean | When enabled, the migrator repeats the migration in a loop until it is stopped, creating new services and clients and checking client secrets and dynamic client registration configuration on each cycle. When disabled, the migrator runs once and exits. | false |
| `SYNC_CLIENT_DELETION` | Boolean | When enabled, clients in the destination environment that do not exist in the source environment are deleted, including clients created directly in the destination. | false |
| `SYNC_INTERVAL_SECONDS` | Number | The number of seconds to wait between synchronization cycles. Only applies when `CONTINUOUS_SYNC` is enabled. | 5 |
| `CLIENT_LIST_REQUEST_COUNT` | Number | The number of clients requested per page from the source and destination client list APIs. Change this only if the client list API in your environment is configured to return a different maximum number of clients per page. | 100 |
When `CONTINUOUS_SYNC` is enabled, the migrator repeats steps 1–6 of [How the Configuration Migrator Works](#how-the-configuration-migrator-works) in a loop, waiting `SYNC_INTERVAL_SECONDS` between cycles. Each cycle only creates services and clients that are new in the source environment, and checks that existing clients' secrets and dynamic client registration configuration are correctly configured (when the corresponding settings are enabled). If `SYNC_CLIENT_DELETION` is enabled, each cycle also deletes destination clients that no longer exist in the source environment. The migrator keeps running until you stop the container (see [Running the Migration](#4-running-the-migration)).
`CONTINUOUS_SYNC`, `SYNC_CLIENT_DELETION`, `SYNC_INTERVAL_SECONDS` and `CLIENT_LIST_REQUEST_COUNT` are available in `configuration-migrator` 1.1 or later.
## Migration Steps
We recommend running the migration in dry run mode first to confirm the expected migration changes and then run again with dry run mode disabled to perform the migration.
If you plan to use the [zero downtime (parallel cutover)](/deployment-and-operations/migration-from-existing-system/migrating-tokens-from-an-older-version-of-authlete#zero-downtime-parallel-cutover) approach, deploy the changes to your authorization server before you run the migration in step 4.
### 1. Confirm Environments Are Set Up and Running
Confirm that the 2.2/2.3 source environment and 3.0 destination environment APIs are available from the `configuration-migrator`'s environment.
### 2. Set Up the Configuration Migrator
Using the tables outlined above, configure the `configuration-migrator` with the appropriate properties, including the correct endpoints, credentials and service/organization IDs.
Below is a sample Docker Compose configuration with dry run enabled (`DRY_RUN=true`). In dry run mode the migrator reads from both environments and logs what it would do, but does not perform any creation or write operations.
The sample reads each setting from an environment variable of the same name, such as `V2_API_URL` and `V3_ORG_ACCESS_TOKEN`. Set them in your shell or in a `.env` file next to the Compose file before running it.
```yaml theme={null}
name: configuration-migrator-sample
services:
configuration-migrator:
image: artifacts.authlete.com/configuration-migrator:1.1
volumes:
- ./migration-output:/app/migration-output
environment:
# API / required configuration
- V2_API_URL=${V2_API_URL} # e.g. https://api.source-environment
- V2_SO_API_KEY=${V2_SO_API_KEY}
- V2_SO_API_SECRET=${V2_SO_API_SECRET}
- V3_API_URL=${V3_API_URL} # e.g. https://api.destination-environment
- V3_API_SERVER_ID=${V3_API_SERVER_ID}
- V3_ORG_ID=${V3_ORG_ID}
- V3_ORG_ACCESS_TOKEN=${V3_ORG_ACCESS_TOKEN}
- V3_IDP_URL=${V3_IDP_URL}
# Migration Options
- DRY_RUN=true
# - SERVICE_IDS=${SERVICE_IDS} # Un-comment and set service IDs (e.g. 12345,67891) if you do not wish to migrate all services available to the service owner
# Continuous Sync Options
# - CONTINUOUS_SYNC=true # Un-comment to keep synchronizing services and clients until the migrator is stopped
# - SYNC_INTERVAL_SECONDS=5 # Seconds to wait between synchronization cycles when continuous sync is enabled
```
Please note that the mounted `volumes` directory will be used to write output summary files. The container runs as UID 1000 so the host directory must be writable by that user.
When `SYNC_CLIENT_DELETION` is enabled (`true`), the migrator deletes clients from the destination environment that do not exist in the source environment, including clients that were created directly in the destination rather than migrated from the source. Deletion happens in a single run as well as in continuous sync mode. Before you enable it for the real migration, check which clients would be deleted with the dry run in step 3.
### 3. Running in Dry Run Mode
Run the migrator with `DRY_RUN=true` and capture the log output:
```bash theme={null}
docker compose up 2>&1 | tee dry-run-migration.log
```
Leave `CONTINUOUS_SYNC` disabled for the dry run so that the migrator runs once and exits. Once execution is complete, review the log output. No services or clients have been migrated at this point, but the logs show what will be created, updated and deleted when the migrator is run with dry run disabled. If you plan to enable `SYNC_CLIENT_DELETION`, enable it for the dry run as well and check the logs for `[DRY RUN] Removed destination client …` lines, which list the clients that would be deleted.
### 4. Running the Migration
When you are ready to perform the real migration, set `DRY_RUN=false` in the Docker Compose file and run the migrator again, capturing the log output:
```bash theme={null}
docker compose up 2>&1 | tee migration.log
```
With dry run disabled, the migrator creates services and clients and updates client secrets in the destination environment. If `SYNC_CLIENT_DELETION` is enabled, it also deletes destination clients that do not exist in the source environment.
If `CONTINUOUS_SYNC` is enabled, the migrator does not exit after the first cycle. Stop the container (for example, with `docker compose stop`) once you are sure no further clients are being registered in the source environment. If `SYNC_CLIENT_DELETION` is also enabled, stop it before any clients are registered in the destination environment, for example before you switch your applications to the 3.0 environment; otherwise each cycle deletes those clients. The migrator prints the full summary when it stops.
### 5. Checking the Result
The main indicator confirming that the migration was completed successfully is the `Errors` count being set to `0`.
The migrator logs each operation while it runs and prints a summary for each service at the end, followed by an overall summary. The summary shows the status of each service (`serviceStatus`), the number of services processed, clients created, dynamically registered clients updated, client secrets updated and the number of clients deleted (if client deletion is enabled). Errors for individual services or clients appear in the log above the summary.
You can safely rerun the migrator, for example if it stopped unexpectedly because of network issues: services and clients that already exist in the destination are not created again, and client secrets are realigned. If `SYNC_CLIENT_DELETION` is enabled, a rerun also deletes destination clients that do not exist in the source environment. You can also rerun the migrator after a successful migration to confirm that everything was migrated correctly.
The sample output below shows an example service summary and overall migration summary for two services:
1. My Service (`apiKey=309958490093`)
* A newly created service (denoted by `serviceStatus=created`)
* With a total of `1241` clients created and migrated
* `612` of the total `1241` clients were dynamically registered clients, so they also were successfully updated further as part of the dynamically registered client migration path
* Since all clients are new, all of their client secrets are updated (`Client secrets synced: 1241`)
* As the service is new, there are no destination clients to delete (`Clients deleted: 0`)
2. Another Service (`apiKey=309958490091`)
* An existing service (`serviceStatus=already_exists`)
* `121` new clients were detected in this service and migrated to the destination environment
* `5` of the total `121` clients were dynamically registered clients, so they also were successfully updated further as part of the dynamically registered client migration path
* All clients were new, so all of their secrets were updated (`Client secrets synced: 121`)
* `3` clients existed in the destination environment but not in the source environment, so they were deleted (`Clients deleted: 3`)
The overall summary includes the total services and clients processed along with totals for all of the events captured in the service level reports.
```
2026-09-29 06:56:55 [INFO] ============================================================
2026-09-29 06:56:55 [INFO] Service Summaries
2026-09-29 06:56:55 [INFO] ============================================================
2026-09-29 06:56:55 [INFO] Service name: My Service (apiKey=309958490093) serviceStatus=created overallStatus=OK
2026-09-29 06:56:55 [INFO] ------------------------------------------------------------
2026-09-29 06:56:55 [INFO] Total clients in service: 1241
2026-09-29 06:56:55 [INFO] Clients created: 1241
2026-09-29 06:56:55 [INFO] DCR clients updated: 612
2026-09-29 06:56:55 [INFO] Client secrets synced: 1241
2026-09-29 06:56:55 [INFO] Client deletion sync:
2026-09-29 06:56:55 [INFO] Clients deleted: 0
2026-09-29 06:56:55 [INFO] Errors: 0
2026-09-29 06:56:55 [INFO] ------------------------------------------------------------
2026-09-29 06:56:55 [INFO] Service name: Another Service (apiKey=309958490091) serviceStatus=already_exists overallStatus=OK
2026-09-29 06:56:55 [INFO] ------------------------------------------------------------
2026-09-29 06:56:55 [INFO] Total clients in service: 121
2026-09-29 06:56:55 [INFO] Clients created: 121
2026-09-29 06:56:55 [INFO] DCR clients updated: 5
2026-09-29 06:56:55 [INFO] Client secrets synced: 121
2026-09-29 06:56:55 [INFO] Client deletion sync:
2026-09-29 06:56:55 [INFO] Clients deleted: 3
2026-09-29 06:56:55 [INFO] Errors: 0
2026-09-29 06:56:55 [INFO] ------------------------------------------------------------
2026-09-29 06:56:55 [INFO] ============================================================
2026-09-29 06:56:55 [INFO] Migration Summary
2026-09-29 06:56:55 [INFO] ============================================================
2026-09-29 06:56:55 [INFO] ------------------------------------------------------------
2026-09-29 06:56:55 [INFO] Total services processed: 2
2026-09-29 06:56:55 [INFO] Total clients processed: 1362
2026-09-29 06:56:55 [INFO] Created clients: 1362 (skipped 0, failed 0)
2026-09-29 06:56:55 [INFO] DCR clients updated: 617/617 created (0 skipped, failed 0)
2026-09-29 06:56:55 [INFO] Client secrets synced: 1362/1362 (0 skipped, failed 0)
2026-09-29 06:56:55 [INFO] Client deletion sync:
2026-09-29 06:56:55 [INFO] Clients deleted: 3/3 (0 skipped, failed 0)
2026-09-29 06:56:55 [INFO] Errors: 0
2026-09-29 06:56:55 [INFO] Report saved to /app/migration-output/configuration-migration-report-1790665015.json
```
When `CONTINUOUS_SYNC` is enabled, the migrator also prints a one-line iteration summary for each service at the end of every cycle. When the container is stopped, it prints the full summary covering every cycle run up to that point. In this mode the overall migration summary also shows an `Iteration count`, and lists only the total for each event, without the attempted, skipped and failed counts shown in the sample above.
An output sample of the iteration logs is below containing the event metrics for operations performed within the previous iteration of the migration:
* 10 clients created, all with their secrets updated; 5 of them were DCR clients
* 3 clients deleted within the same iteration
```
2026-09-29 06:51:11 [INFO] Iteration summary [Another Service (309958490091)]: clients created 10 (skipped=0 failed=0) secrets updated 10 (attempted=10 skipped=0 failed=0) dcr clients configured 5 (attempted=5 skipped=0 failed=0) clients removed 3 (attempted=3 skipped=0 failed=0) errors 0
```
## Token Migration (Dedicated and Self-Managed)
Tokens are migrated separately by the Token Migrator. See [Migrating Tokens to Authlete 3.0](/deployment-and-operations/migration-from-existing-system/migrating-tokens-from-an-older-version-of-authlete).
# Migrating using the Authlete Management Console (Shared environment only)
In the case of migrating an account residing on one of the Authlete Shared environments, the migration will be done through the Authlete Management Console UI.
The general upgrade path is as follows:
1. Log in to the Authlete Management Console of the new Authlete 3.0 environment
2. Create and configure the amount of Organization(s) that are needed for the migration
3. Service and Client Migration (Refer to below section) - Using the `Import/Migrate` feature, import the appropriate service(s) and client(s) into this organization
4. Token Migration - Optional, if existing tokens need to be available in the new 3.0 environment
5. Update OAuth Server / Applications
If you use the [zero downtime (parallel cutover)](/deployment-and-operations/migration-from-existing-system/migrating-tokens-from-an-older-version-of-authlete#zero-downtime-parallel-cutover) approach, deploy the changes to your authorization server before step 3.
The Token Migrator only has read access to the source DB and is therefore unable to alter data of any kind.
## Service & Client Migration
1. Log into the [Authlete Management Console](https://console.authlete.com/), and go to `Organization Settings > Basic Settings > Import/Migrate`
2. Log into the Authlete 2.x [Service Owner Console](https://so.authlete.com/accounts/login), and copy/paste the Service Owner API Key and API Secret from your account (can be found in the `Your Account` page)

*Your Account page in the Authlete 2.x Service Owner Console*
3. Enter the Authlete API URL for your cluster
* Enter one of the corresponding URLs below:
* US : [https://api.authlete.com](https://api.authlete.com)
* Japan : [https://tokyo.authlete.com](https://tokyo.authlete.com)
* Brazil : [https://latam.authlete.com](https://latam.authlete.com)
4. Click `Continue`

*Service migration form*
5. The API should return a list of services that are attached to that account
6. Tick the checkbox next to the services you would like to migrate
7. If you plan to use the Token Migrator, tick the `Synchronize Client Tokens` and `Migrate Client Secrets` checkboxes. This will ensure clients belonging to the service are detected, and their tokens and/or secrets are migrated to the 3.0 environment. This property can also be updated individually per client after the import, under `Client Settings > Basic Settings > Advanced > Migration Settings`.
8. Click on the `Select cluster` dropdown to choose a cluster under which to migrate services

*Service selection*
9. Click `Continue`
10. Click `Import` on the confirmation screen
11. The services (and attached clients) should now appear inside the Authlete Management Console navigation

*Migration confirmation*
## Token Migration (Shared)
Tokens are migrated separately by the Token Migrator. See [Migrating Tokens to Authlete 3.0](/deployment-and-operations/migration-from-existing-system/migrating-tokens-from-an-older-version-of-authlete).
# Migrating Tokens to Authlete 3.0
Source: https://developers.authlete.com/deployment-and-operations/migration-from-existing-system/migrating-tokens-from-an-older-version-of-authlete
Copy tokens from Authlete 2.2 or 2.3 to Authlete 3.0 and plan the cutover of your authorization server.
## Overview
Token migration copies tokens issued in the Authlete 2.2/2.3 environment (the source environment) to the new Authlete 3.0 environment (the destination environment), so that the tokens remain usable and valid after your authorization server switches to the Authlete 3.0 environment. Token migration is optional.
If you do not migrate tokens, tokens issued in the source environment do not exist in the Authlete 3.0 environment. Depending on your implementation, refreshing those tokens and introspecting them fail, so in most cases users have to authenticate again.
Token migration is performed by the Token Migrator, a service provided by Authlete. While running, the Token Migrator periodically checks the source environment for new tokens and changes to existing tokens, and copies them to the destination environment. The Token Migrator only has read access to the source database and does not change any data there.
### What Is Migrated
* Access tokens of clients in scope. A client is in scope when it meets the [Client Token Migration Conditions](#client-token-migration-conditions).
* Refresh tokens. A refresh token is stored together with its access token, so it is migrated with the access token. There is no separate step or setting for refresh tokens.
### What Is Not Migrated
* Token deletion events.
* Temporarily stored data, such as authorization codes and request URIs.
* Tokens issued or changed in the destination environment. The copy is one-directional, from the source environment to the destination environment.
## Supported Versions and Deployment Models
Token migration is only possible from Authlete 2.2/2.3 to the latest Authlete 3.0 release, for the same deployment model and region. If you wish to migrate to Authlete 3.0 but aren't currently using Authlete 2.2 or 2.3, please contact our support team so we can assist you in the process to upgrade your environment.
Who runs the Token Migrator depends on the deployment model:
| Deployment model | Who runs the Token Migrator | Starting and stopping | Dry run |
| - | - | - | - |
| Shared | Authlete | Runs continuously and cannot be stopped | Not available |
| Dedicated | Authlete | Request from Authlete support | Not available |
| Self-managed | You | Deploy and remove it yourself | Available |
Dry run is a mode in which the Token Migrator runs without writing to the destination environment.
On Shared and Dedicated environments:
* Disabling the `Synchronize Client Tokens` setting of a client takes the client out of scope only from the next check onward. `Synchronize Client Tokens` is the client setting that marks a client for token migration.
* Deleting the source service does not stop the Token Migrator.
For running the Token Migrator in a self-managed Kubernetes environment, see [Migration Phase (Optional)](/deployment-and-operations/self-managed-deployment/kubernetes-installation-guide#migration-phase-optional) in the Kubernetes installation guide. For operating it, see also [Token Migrator Timing and Intervals](/deployment-and-operations/self-managed-deployment/token-migrator/token-migrator-timing-and-intervals), [Stopping and Resuming the Token Migrator](/deployment-and-operations/self-managed-deployment/token-migrator/stopping-and-resuming-token-migrator), and [Interpreting Token Migrator Logs](/deployment-and-operations/self-managed-deployment/token-migrator/interpreting-token-migrator-logs).
## Client Token Migration Conditions
The Token Migrator migrates a client's tokens only when it identifies the client in the source environment and the client in the destination environment as matching clients. The requirements are strict to ensure that a client's tokens are migrated to the correct client in the destination environment. Both clients must meet the following conditions:
* The `clientId` value must match - `clientIdAlias` is ignored
* The `serviceId` (`api_key`) that both clients exist in must be the same in both environments
* The destination client must have the `Synchronize Client Tokens` setting enabled
* The `clientSecret` value must match
### Meeting the Conditions
When you migrate service and client configuration, set the following so that the clients meet the conditions:
* With the [Authlete Management Console import](/deployment-and-operations/migration-from-existing-system/migrating-settings-from-an-older-version-of-authlete#migrating-using-the-authlete-management-console-shared-environment-only) (Shared), tick the `Synchronize Client Tokens` and `Migrate Client Secrets` checkboxes. If you do not tick `Migrate Client Secrets`, the client secret is randomly generated.
* With the [configuration migrator](/deployment-and-operations/migration-from-existing-system/migrating-settings-from-an-older-version-of-authlete#migrating-using-the-configuration-migrator-dedicated-and-self-managed) (Dedicated and self-managed), keep `SYNC_CLIENT_TOKENS=true` and `MIGRATE_CLIENT_SECRETS=true` (the defaults).
After the import, you can also update individual clients:
* Enable `Synchronize Client Tokens` under `Client Settings > Basic Settings > Advanced > Migration Settings`.
* Update the secret of a specific client by calling the [Update Client Secret](/api-reference/client-management/update-client-secret) API (`/api/{serviceId}/client/secret/update/{clientIdentifier}`).
To test the end to end migration process at a smaller scale, enable `Synchronize Client Tokens` on a single client so that only its tokens are migrated. Keep in mind that tokens will only be migrated while the Token Migrator is running.
### Checking Clients Excluded from Migration
* Shared and Dedicated: Authlete does not notify you of clients that are excluded from token migration. Check the conditions above before you start the migration.
* Self-managed: the Token Migrator logs the clients in scope and the clients whose secrets do not match. See [Determining in-scope clients](/deployment-and-operations/self-managed-deployment/token-migrator/interpreting-token-migrator-logs#determining-in-scope-clients).
## How Long the Migration Takes
The duration depends mainly on the following, in order of impact:
* The number of clients in scope: roughly 1–2 seconds per client on the first pass.
* The token volume: on the order of 1,000,000 tokens per 70 minutes within a single client.
Measure in a verification environment before scheduling a downtime window.
On Shared environments, delay may also occur because other users may be migrating tokens at the same time.
## Choosing a Cutover Approach
Cutover is the switch of your authorization server from the source environment to the Authlete 3.0 environment. Any applications that are communicating with the Authlete source environment need to be updated to point to the new Authlete 3.0 environment.
The changes required vary depending on your setup but this could include:
* Changing the API version if you are using our SDKs
* Updating endpoints due to the change of endpoint structure in Authlete 3.0 compared to Authlete 2.2/2.3
* Updating authentication credentials due to the change of authentication mechanism in Authlete 3.0 compared to Authlete 2.2/2.3
* Configuration changes
* New version updates for your own authorization server(s)
Choose one of the following approaches:
| Approach | Summary | Downtime | Prerequisite |
| - | - | - | - |
| [Bulk token migration](#bulk-token-migration) | Stop your applications, migrate the tokens, then switch | Between steps 1 and 4 | — |
| [Just In Time (JIT) migration synchronization](#just-in-time-jit-migration-synchronization) | Migrate the tokens and keep them in sync while your applications run, then switch | Only in step 3 | — |
| [Zero downtime (parallel cutover)](#zero-downtime-parallel-cutover) | Your authorization server uses both environments while the tokens are migrated | None | Authorization server changes deployed before you migrate services and clients |
Between bulk token migration and JIT migration synchronization, choose mainly by the amount of tokens that need to be migrated and how much downtime of your own service can be tolerated.
If you roll back by reconnecting your authorization server to the source environment, tokens issued in the Authlete 3.0 environment after the cutover remain in the 3.0 environment but are not copied back to the source environment, so they are lost for your service.
### Bulk Token Migration
1. Stop the applications connected to the Authlete source environment (on Dedicated, at a time scheduled with Authlete support)
2. Migrate the clients and synchronize their settings so that they meet the [Client Token Migration Conditions](#client-token-migration-conditions) (see [Meeting the Conditions](#meeting-the-conditions)). Their tokens are then migrated by the Token Migrator. On Dedicated environments, also request Authlete support to start the Token Migrator; on self-managed environments, deploy it
3. Wait until the migration is complete (on Dedicated, Authlete support notifies you; otherwise, see [Monitoring Progress](#monitoring-progress))
4. Update applications to connect to the new Authlete 3.0 environment
5. On Dedicated and self-managed environments, stop the Token Migrator (see [After the Cutover](#after-the-cutover))
* Rollback: roll back authorization server changes to connect back to the Authlete source environment
### Just In Time (JIT) Migration Synchronization
1. Migrate the clients and synchronize their settings so that they meet the [Client Token Migration Conditions](#client-token-migration-conditions) (see [Meeting the Conditions](#meeting-the-conditions)). Their tokens are then migrated by the Token Migrator. On Dedicated environments, also request Authlete support to start the Token Migrator at a time set with them; on self-managed environments, deploy it
2. Wait until all tokens have been created in the Authlete 3.0 environment (on Dedicated, Authlete support notifies you)
3. Update applications to connect to the new Authlete 3.0 environment
4. On Dedicated and self-managed environments, stop the Token Migrator (see [After the Cutover](#after-the-cutover))
Between steps 2 and 4, the Token Migrator keeps monitoring token changes in the source environment and copies them over.
* Rollback: roll back authorization server changes to connect back to the Authlete source environment
### Zero Downtime (Parallel Cutover)
This approach requires changes to your authorization server before you start the migration of services and clients into the Authlete 3.0 environment, and the Authlete 3.0 environment must be available.
A sample authorization server that implements this approach is available in [authlete/java-oauth-server-migration](https://github.com/authlete/java-oauth-server-migration).
In this approach, the authorization server connects to both the Authlete 3.0 server (the primary) and the Authlete 2.2/2.3 server (the secondary). Incoming calls are delegated to the primary first by default. Depending on the primary's response, or on whether the endpoint is available only in 3.0, the authorization server either returns the primary's response or delegates the request to the secondary.
While the Token Migrator is running in the background and tokens are becoming available in the Authlete 3.0 environment, the primary API will be able to handle more and more of the incoming requests.
Once all tokens are copied/updated in the Authlete 3.0 environment, all of the incoming calls to your application are handled by the Authlete 3.0 environment. At this time, the migration is complete. No other token changes should be occurring in the Authlete 2.2/2.3 environment.
## Monitoring Progress
Token migration progress can be viewed in the organization's audit logs:
* The audit log entry shows the number of tokens created and updated within a rolling 4 hour time window.
* The entry is written only when tokens are actually persisted in the destination environment.
* The entry's timestamp is not refreshed. Only the created and updated counts increase as tokens are migrated.
* An unchanged entry does not mean the Token Migrator has stopped.
On self-managed environments, see also [Token migration iteration](/deployment-and-operations/self-managed-deployment/token-migrator/interpreting-token-migrator-logs#token-migration-iteration) in the log guide.
## Verifying the Migration
Once the migration is complete, you can verify that your tokens have been properly migrated and are valid:
* Pass the tokens to Authlete's [Process Introspection Request](/api-reference/introspection-endpoint/process-introspection-request) API on the 3.0 environment.
* On self-managed environments with direct database access, compare token counts per client or service in the source and destination databases.
## After the Cutover
Once the cutover is completed, perform all operations on tokens only in the Authlete 3.0 environment.
Stop the Token Migrator according to your deployment model:
* Shared: the Token Migrator keeps running and cannot be stopped.
* Dedicated: request Authlete support to stop it.
* Self-managed: remove it as described in [Remove the token migrator](/deployment-and-operations/self-managed-deployment/kubernetes-installation-guide#5-remove-the-token-migrator).
## Troubleshooting
### Tokens of a client are not migrated
Check that the client meets all of the [Client Token Migration Conditions](#client-token-migration-conditions). If the client secrets do not match, do one of the following:
* Run the configuration migrator again with `MIGRATE_CLIENT_SECRETS=true`.
* Update the secret of the destination client by calling the [Update Client Secret](/api-reference/client-management/update-client-secret) API.
# Preserving Existing Client ID Values
Source: https://developers.authlete.com/deployment-and-operations/migration-from-existing-system/preserving-existing-client-id-values
Preserving existing client ID values by utilizing Authlete's Client ID Alias feature.
In some deployments, you might want to keep using client ID values that have been registered both an existing authorization server and its clients, even after migrating the old server to a new one with Authlete.
Authlete has a feature of “Client ID Alias,” that enables two client ID values for each client. One is automatically generated by Authlete and the other one is arbitrary.
Read the following article for details.
* [Using “Client ID Alias”](/v2/configuration-reference/client-management/client-id-alias)
# Accessing Authlete API Server through a Proxy Server
Source: https://developers.authlete.com/deployment-and-operations/networking/accessing-authlete-api-server-through-a-proxy-server
Technical guide on accessing the Authlete API server through a proxy server using Java properties.
Please refer to the followings when accessing the Authlete API server through a proxy server
## Proxy-related Properties
java.net.useSystemProxies\
http.proxyHost\
http.proxyPort\
https.proxyHost\
https.proxyPort\
http.nonProxyHosts
## Examples
Example 1: Parse parameters as a java command
```
java -Djava.net.useSystemProxies=true ...
```
Example 2: Implement with code
```
System.setProperty(
"java.net.useSystemProxies",
"true"
);
```
## External References
[https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html](https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html)
[https://stackoverflow.com/questions/120797/how-do-i-set-the-proxy-to-be-used-by-the-jvm](https://stackoverflow.com/questions/120797/how-do-i-set-the-proxy-to-be-used-by-the-jvm)
[http://www.coppermine.jp/docs/notepad/2014/12/jersey-client-with-proxy.html](http://www.coppermine.jp/docs/notepad/2014/12/jersey-client-with-proxy.html)
# Long-Term Support (LTS) Policy
Source: https://developers.authlete.com/deployment-and-operations/operational-policies/long-term-support-lts-policy
Overview of the LTS policy for Authlete versions
## Introduction
Authlete has continuously evolved to deliver meaningful improvements to both its UI and APIs. At the same time, we recognize that for our users, stability and reliability are critical.
To better support these needs, we are introducing Long-Term Support (LTS) versions of Authlete. LTS releases provide a stable and predictable foundation for production environments, allowing you to rely on consistent behavior over time, while still giving you the flexibility to adopt the latest standards and features at your own pace.
## What is LTS?
Long-Term Support (LTS) is a commitment from Authlete to provide a stable version of the product that will no longer undergo major architectural changes or API/UI overhauls during its lifecycle. This ensures that users can rely on a supported environment without needing to upgrade frequently.
## Supported Versions
As of April 2nd, 2026, only `Authlete 2.3` is eligible for LTS. The current patch version for LTS is `2.3.47` and will be upgraded when the need to deploy an update arises.
Note that `Authlete 3.0` is also scheduled for LTS. Details will be announced at a later date during H2 2026.
## Checking Your Current Version
The Authlete management consoles (Client Developer Console and Service Owner Console) do not currently display the Authlete server version on screen. To find out which version you are running, use one of the methods below.
### Call the `/api/info` endpoint
The Authlete server exposes the `/api/info` endpoint, which returns the current server version and the list of enabled features. No authentication is required.
```shell theme={null}
curl -s https:///api/info
```
Replace `` with the API host of your environment.
Example response:
```json theme={null}
{
"version": "2.3.47",
"features": ["ciba", "dcr", "fapi", "..."]
}
```
The `version` field contains the version of the server you are currently using.
If you are using the IP restriction option on a Dedicated Cloud environment, you will need to call the endpoint from an allowed IP address.
### Contact Authlete Support
If the methods above are not convenient, please contact Authlete Support and we will look up the version of your environment for you.
## Supported Period
LTS versions will be supported for a period of **3 years**.
## Available Updates
LTS versions will not benefit from backports of new features or specifications. However, until their official EoL date, **they will keep receiving fixes for critical bugs and security issues**.
1. **Security Patches →** Addressing vulnerabilities that may impact the integrity, confidentiality, or availability of the product
2. **Critical Bug Fixes →** Resolving bugs that significantly affect the product's core functionality or cause severe issues in production environments
3. **Stability Updates →** Fixing major performance or compatibility issues without introducing new features
Once a version reaches EoL, it will no longer receive updates of any kind. Users may continue to use the version at their own discretion, understanding that Authlete does not provide patches, fixes, or technical support for EoL versions. We strongly recommend to upgrade to the LTS version to maintain support, or to the active 3.0 version to benefit from the latest features.
| Update Type | Active Development | LTS | Legacy Grace Period | End of Life |
| - | :-: | :-: | :-: | :-: |
| New features & specifications | ✅ | ❌ | ❌ | ❌ |
| Critical bug fixes | ✅ | ✅ | ❌ | ❌ |
| Security patches | ✅ | ✅ | ✅ | ❌ |
| Stability & performance fixes | ✅ | ✅ | ❌ | ❌ |
| Technical support | ✅ | ✅ | 🟠 | ❌ |
## EoL Policy
We understand it is simply not possible for the majority of our users to immediately upgrade their environment to one of the supported LTS versions. For this reason, we are committed to maintaining support for all versions of Authlete until March 2027.
During that legacy grace period, investigation and troubleshooting support will be provided. However, if an issue is identified as version-dependent, we may recommend upgrading to an LTS version, or the the version in active development.
| Version | Initial Release | LTS Start | EoL Date | Status |
| - | - | - | - | - |
| Authlete 1.1 | June 2017 | Not Eligible | 31st March 2027 | ⚠️ Legacy Grace |
| Authlete 2.0 | August 2018 | Not Eligible | 31st March 2027 | ⚠️ Legacy Grace |
| Authlete 2.1 | December 2019 | Not Eligible | 31st March 2027 | ⚠️ Legacy Grace |
| Authlete 2.2 | March 2021 | Not Eligible | 31st March 2027 | ⚠️ Legacy Grace |
| Authlete 2.3 | January 2023 | April 2026 | 31st March 2029 | ✅ LTS |
| Authlete 3.0 | November 2024 | TBA | TBA | ⏩ Active Development |
Going forward, all new non-LTS versions of Authlete will be supported for 6 months.
## Upgrade Schedule
Authlete 3.0 is the active development version and will keep receiving updates every month, as has been the case for the past few years. Additionally, a new version will be chosen for LTS **once every two years**. This overlap with the LTS support period ensures that there will always be room for a smooth transition from one LTS version to another.
If data migration is required for an upgrade, our team will make sure to provide the necessary documentation and support to users.
Version upgrades to an LTS version or a version in active development are free of charge.
## Version Deprecation & Communication
Authlete is committed to giving customers sufficient advance notice before any version reaches End of Life:
* **Minimum 6 months** notice before an **LTS version** reaches EoL
* **Minimum 3 months** notice before a **non-LTS version** reaches EoL
* Notifications will be delivered via email to account administrators, in-product banners within the Authlete console, and announcements on the Authlete changelog and website
* Versions approaching or past their EoL date will display a visible deprecation notice within the product UI, recommending to upgrade
## *Disclaimer*
*Versions that have reached End of Life* ***are provided as-is***\*. Authlete accepts no responsibility for security vulnerabilities, bugs, data loss, or service disruptions arising from the continued use of unsupported versions. Customers using EoL versions do so at their own risk and are strongly encouraged to upgrade to a supported version.\*
***
# FAQ
### Q: How to check the version my environment is running?
A: You can check your current version at `https://api.example.com/api/info`.
### Q: Will there be any service interruptions or impacts when upgrading from version 2.2 to 2.3?
A: Yes, we expect some service interruption to happen when upgrading from 2.2 to 2.3.
### Q: Does switching to the LTS version require a separate build or switching process for the "LTS version"?
A: No. Continuing to use version 2.3 will qualify you for LTS support. The current LTS version is `2.3.47`.
### Q: Is there an option to use or not use an LTS-eligible version in Shared Cloud (SaaS version)?
A: No. Shared Cloud always provides the latest version applied by Authlete. As of May 2026, Shared Cloud (so.authlete.com, etc.) has also been updated to 2.3.47 LTS.
### Q: Will it become impossible to contact support even with a paid support contract after reaching End of Life (EoL)?
A: After reaching End-of-Life (EoL), patches, bug fixes, and technical support will no longer be provided (this applies even to paid support contracts). The current EoL for 2.3 LTS is March 31, 2029. During the extended support period before EoL, we will continue to accept version-independent general investigations and troubleshooting requests.
### Q: Where does the version deprecation banner appear on the management console?
A: A banner will be displayed in the Shared Cloud management console. The Self-Managed (on-premise) management console will not display a banner; information will be provided via email and through the support desk.
### Q: Will there be any additional costs for upgrading to the LTS version?
A: No. There are no additional charges for upgrading to the LTS version or the latest version. For Self-Managed (on-premise) versions, any labor costs associated with the upgrade process will be communicated by our sales representative based on your individual contract and the scope of work.
### Q: Will I be able to continue using the service after support for version 2.2 ends at the end of March 2027?
A: While the service can still be used after the End of Life (EoL), patches, bug fixes, and technical support will not be provided. We strongly recommend to upgrade to an active LTS version, or to the latest Authlete version.
### Q: Will the LTS target version increase with each minor version update, requiring updates to keep up? What will be the support period for each old patch?
A: Patches are only applied to an LTS version if deemed necessary. For instance, if there needs to be a security update for Authlete 2.3.47, version 2.3.48 will be released, and both versions will be treated as LTS (Long-Term Support).
Regarding support policies, as stated above, we recommend updating from a security standpoint, but you should consider updating based on the necessity of the fixes.
### Q: When an LTS patch is released, will individual end-of-support dates be set for each previous LTS patch?
A: There are no individual End-of-Life (EoL) dates set for each older patch. We will continue to accept inquiries during the 2.3 LTS availability period (until March 31, 2029), but new critical fixes will be included in the latest LTS patch. If you require fixes, please consider updating to the latest patch.
### Q: If using an SC environment, is an update necessary?
A: The SC environment has been updated to the LTS version, so no version upgrade is required on the part of the user company.
### Q: Can I select an LTS version in the SC environment?
A: No. The version on the Shared Cloud will always be the latest version provided by Authlete.
# Rate Limit Best Practices
Source: https://developers.authlete.com/deployment-and-operations/operational-policies/rate-limit-best-practices
Provides guidance on what to do when hitting rate limits and what to do to avoid them.
## Overview
When using Authlete's API endpoints you should consider Authlete's Rate Limit Policy and develop your applications considering resilient strategies to prevent service degradation, avoid self-inflicted Distributed Denial of Service (DDOS) patterns, and ensure continuous operation during peak load. The following technical best practices are essential we recommend our customers to consider.
## Implement Intelligent Caching for Query Endpoints
Caching is the primary defense against unnecessary API calls and rate-limit violations. In the case of Authlete there are endpoints that are used regularly and return information that do not change that often and can be cached to improve performance and to reduce the risk of being rate limited by external factor. The following table is an non-exhaustive list of endpoints that should be considered when planning caching in your application.
| Endpoint | Usage | Comments |
| - | - | - |
| **Get Service Configuration** `/api/{serviceId}/service/configuration` | This API is intended to be called from within the implementation of the configuration endpoint of the service where the service that supports OpenID Connect and [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html). | Many OpenID SDKs and libraries use the discovery endpoint to simplify its configuration and usage. When this libraries are deployed as part of a web application or site your OpenID Provider might receive a large number of requests. The configuration of a service does not change often so this is an endpoint that can be safely cached. |
| **Get JWK Set** `/api/{serviceId}/service/jwks/get` | This API is intended to be called from within the implementation of the jwk set endpoint of the service where the service that supports OpenID Connect must expose its JWK Set information. | Many OpenID SDKs and libraries query the JWKs endpoint to get the public keys of the OpenID Provider and simplify its configuration. When this is applied in mobile or web apps it the JWKs endpoint of the OpenID Provider. Key rotation happens in longer periods of time, weeks or months, so it is safe to cache the result of this endpoint. |
| **Get Client** `/api/{serviceId}/client/get/{clientId}` | This API retrieves the details and configuration of a client. | Unlike the previous endpoints this endpoint does not back an specific endpoint but it is commonly used in some processes. If you need to use the Client metadata as part of a regular flow we suggest you cache the response of this endpoint. |
| **Get Verifiable Credential Issuer Metadata** `/api/{serviceId}/vci/metadata` | This API is intended to be called from within the implementation of the metadata endpoint of a service that supports [OpenID for Verifiable Credentials](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-metadata). | The metadata endpoint is designed to be consumed by the Wallets and therefore its usage can grow depending on usage. Like other service configuration endpoints the content included in the response does not change often so this can be cached. |
| **Get JSON Web Key Set** `/api/{serviceId}/vci/jwks` | This API is intended to be called from within the implementation of the JWKs endpoint of the service where SD-JWT is implemented. | Similar to the OpenID JWKs the public keys are requested regularly by Wallets and that might lead to a large number of requests. Usually key rotation happens in longer periods of time, weeks or months, so it is safe to cache the result of this endpoint. |
| **Process Introspection Request** `/api/{serviceId}/auth/introspection` | This API is intended to be called from within the implementations of protected resource endpoints of the authorization server implementation in order to get information about the access token which was presented by the client application. | Getting the information associated with a token might happen multiple times depending on how the applications and resource servers communicate with each other. In some use cases, caching responses from Authlete's introspection endpoint improves performance of response at resource server APIs and reduce the load on the Authlete service. For more details read [this article](/deployment-and-operations/performance/caching-introspection-responses) |
| **Process OAuth 2.0 Introspection Request** `/api/{serviceId}/auth/introspection/standard` | This API is intended to be called from within the implementations of the introspection endpoint of your service | This endpoint in similar in intent to the previous one with the difference that it supports a standard way for third party controlled Resource Servers to verify the status of a token. Because there is no control on the Resource Servers using the standard introspection endpoint it is possible that they try to verify the same token multiple times in very short periods of time. In this scenarios caching might be possible. |
When implementing caching your application keep in mind the following:
* **Cache Query Responses:** Store mainly successful responses from idempotent (safe and repeatable) endpoints for a defined period. Use an in memory cache or an internal cache system (e.g., Redis) to serve repeated queries, significantly reducing the load on the Authlete API.
* **Configuration Review:** Ensure caching infrastructure is robustly configured to handle traffic patterns, including setting adequate limits for maximum connections to prevent resource exhaustion and cascading failures during high-traffic events.
* **Time-to-Live (TTL) Management:** Define appropriate TTLs based on the data's volatility. Non-critical or static data can tolerate longer TTLs (e.g., 5-10 minutes).
## Employ Conditional Retry Logic
Applications using Authlete's API must employ a retry logic that is focused only on transient failures and that allows the system to process all requests. Applications that retry executing all requests immediately compound errors and can overwhelm system resources, leading to connection exhaustion, timeouts and even a self-inflicted DDoS.
When implementing Authlete retries in your application keep in mind the following:
* **Retry Only on Transient Errors:** Limit automatic retries to specific, transient HTTP status codes indicating temporary server issues:
* *429 Too Many Requests*: Indicates the rate limit has been exceeded. A retry should only occur after the duration specified in the *Ratelimit-Reset* header.
* *503 Service Unavailable*: Indicates the Authlete API is temporarily overloaded or down for maintenance.
* *502 Bad Gateway Error*: Indicates that the Authlete API is down for maintenance.
* Other *5xx* errors (e.g., *500*): Only retry if the error is suspected to be non-permanent and be specific to the endpoint where the error is coming from.
* **Avoid Retrying Permanent Errors:** Do not implement retries for permanent errors (*4xx)* like *400 Bad Request,* *401 Unauthorized or 403 Forbidden* as these will never succeed without modifying the content of the request.
* **Avoid Layering Retries:** Make sure that your application tracks retries across the context of a request to avoid retrying an operation that has already been retried before in the context. By doing this check you will avoid extending retries unnecessarily.
The *Ratelimit-Reset* header is active and it is present in response for our Shared Cloud environments. Dedicated Cloud environments running older versions might not include the header until strict rate limits are rolled out.
## Introduce Delays with Exponential Backoff
To prevent a sudden surge of requests (a "retry storm"), that may only worsen a rate-limit violation, introduce carefully controlled delays between retry attempts.
* **Use Exponential Backoff:** Implement an exponential backoff algorithm where the delay between retries increases exponentially with each consecutive failure. This mechanism gradually reduces the request rate, giving Authlete API time to process other requests. We recommend that the first retry waits at least 500 milliseconds, 2nd after 1 second, 3rd after 2 seconds, etc., potentially with a small random jitter to prevent synchronized retries.
* **Define a Max Retries and Jitter:** Set a maximum number of retry attempts, or time spent in retries, and incorporate a randomized "jitter" factor into the delay calculation. Jitter prevents all failed clients from retrying at the exact same moment, which mitigates the risk of a renewed traffic spike.
## Implement a Circuit Breaker Pattern
Even with exponential backoff and conditional retries, a persistent or high-volume series of transient errors can cause a major service disruption on your application. Continuously attempting failed operations in this scenario can deplete your application resources and exacerbate the load on the Authlete API, leading to self-inflicted outages.
When implementing the Circuit Breaker pattern in your application:
* **Define a Failure Threshold:** Configure the circuit breaker to "open" (stop routing requests) when the rate or number of transient errors (e.g., 429 Too Many Requests, 503 Service Unavailable) exceeds a predefined threshold within a short time window.
* **Implement the Three States (Open, Half-Open, Closed):** Closed: Normal operation. Monitor error count. Open: Immediately fail requests without calling the backend. Use a short, fixed time-out (e.g., 60 seconds) before transitioning to Half-Open. Half-Open: Allow a limited number of test requests to pass through to determine if the backend has recovered. If the test requests succeed, return to Closed; otherwise, return to Open.
* **Use Fast-Fail Fallbacks:** When the breaker is Open, provide an immediate and non-retriable fallback response (e.g., serving stale data for endpoints that can use cache) to maintain a degraded but functional user experience without burdening the application or the failing service.
* **Isolate Resources:** Apply circuit breakers at a granular level (e.g., per endpoint or per service dependency) to isolate failures. This can allow you to prioritize some flows critical to your application (e.g. introspection or refresh token exchange) while limiting access to others (e.g. client management)
This approach provides a final layer of defense by quickly halting operations against an unhealthy service, preserving your application's stability and protecting the overall system from cascading failures.
# Rate Limit Policy
Source: https://developers.authlete.com/deployment-and-operations/operational-policies/rate-limit-policy
Overview of the rate limit policy for Authlete Managed deployments.
## Overview
Authlete uses rate limits to protect our platform and ensure high availability and performance for our customers. This document provides an overview of the types of rate limits we enforce in order to allow customers to plan accordingly when building their applications.
This document and all rate limit content is specific to Authlete managed deployments. Self-Managed deployments might apply other protective measures.
## Types of Rate Limits
In order to ensure that the the platform is available and stable we use different types of limits but as general rule limits apply to all calls to [Authlete APIs](https://docs.authlete.com/en/shared/latest#overview) and once a limit is reached requests will be rate limited meaning they will not be executed and Authlete will return a response with status code 429.
The following is a description of the different types of rate limits we apply and when we use them.
* **Environment Limits:** This is a fixed global limit that ensures the total number of requests received in an environment do not exceed the capacity deployed. This limit is used in Enterprise Plans using Dedicated Cloud deployments where customers contract a specific capacity, ie. 1500 RPS, and it is focused on the API endpoints used in the authorization process (`/auth/*`)
For Dedicated Cloud customers exceeding the environment capacity regularly does not only result on requests being rate limited but it might invalidate the Service Level Agreement.
* **Service Limits:** This is a limit that controls the maximum number of requests made to the API within the context of a service. In this case the limit will depend on the subscription plan and the limit will be applied regardless of the capacity available in the environment. This limit is enforced in our Shared Cloud deployments for Free and Business Plans.
* **Requester Limits:** This is a limit intended to be our first line of defense from rogue or faulty actors. It is applied based on the requester IP Address and the requester API key or token. This limit is applied only in our Shared Cloud offering, where it is generally set to 20 RPS.
Although Requester Limits can be enabled in Dedicated Cloud offerings it is common that most requests come from well known sources in which case customers instead use the ability to restrict connectivity from a known list of IP addresses.
* **User Limits:** This limit is focused on the interactions that users have via the Authlete Management Console and other web applications. The limit is applied based on the user identity and it is enforced in all environments.
The following table provides a summary of the limits and where they are used
| Type of Limit | Key | Limit | Shared/Public Cloud | Dedicated/Private Cloud |
| - | :-: | :-: | :-: | :-: |
| **Environment** | - | Capacity contracted | ❌ | ✅ |
| **Service** | Service ID | Organization or Service Owner Plan | ✅ | ❌ |
| **Requester** | IP Address API Key API Token | 20 RPS | ✅ | ❌ |
| **User** | User ID | 5 rps | ✅ | ✅ |
## Burst or Grace Limits
Both **Environment** and **Service** limits include a grace allowance, permitting up to 20% additional requests for a limited period—typically up to 3 minutes—to manage sudden load bursts before the strict rate limit is enforced.
## Rate Limit Quotas
### Shared Cloud Service Limits
The following table shows the rate limits enforced in Shared Cloud based on the plan of the organization or service owner the service belongs to:
| Plan Authlete v3.0 | Plan Authlete v2.3 | RPS per service |
| :-: | :-: | :-: |
| Free | Free | 5 |
| Business | Premium | 20 |
### Dedicated Environment Limits
As mentioned before Dedicated Cloud environments have a fixed global limit that depends on the customer contract. For example for a customer using a dedicated environment with a 100RPS capacity the limit will enforce that limit with a grace limit of 120RPS for a couple of minutes.
# Caching Introspection Responses
Source: https://developers.authlete.com/deployment-and-operations/performance/caching-introspection-responses
Optimizing response performance at resource server APIs by caching responses from Authlete's introspection endpoint.
## Overview
In some use cases, caching responses from Authlete's introspection endpoint improves performance of response at resource server APIs.\
Instruction Example
***
1. Install a cache server of your choice e.g. Redis at your authorization server or resource server(s).
2. Configure the cache server to store responses from Authlete's introspection endpoint.
## Notes
* (TBD)
# AWS ECS Installation Guide
Source: https://developers.authlete.com/deployment-and-operations/self-managed-deployment/aws-ecs-installation-guide
Follow our Container Runtime Installation Guide to deploy Authlete using AWS ECS with Ansible Playbook
The purpose of this document is to provide a clear and concise guidance to customers using AWS ECS, who would like to deploy the self-hosted version of Authlete through the Ansible solution.
## Prerequisites
### AWS
* ECS Cluster (Fargate)
* ECR
* Load Balancer
* Target Group
* RDS (Engine: MySQL Community)
* Cloud Map
* EFS
* CloudWatch Logs groups
### Ansible Execution Environment
* Python (3.12 or later)
* Python modules: boto3, botocore, requests
* Ansible (11.5.0 or later)
* AWS CLI
* Docker
* Crane ([https://github.com/google/go-containerregistry/tree/main/cmd/crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane))
## Installation Steps
### Setup Phase: Authlete
#### 1. Create an Organization
* Log in to the [Authlete Console](https://console.authlete.com).
* Create an organization for your company.
* Note down the **Organization ID**.

#### 2. Request Access
* Share the **Organization ID** and **Organization Name** with Authlete Support.
* Authlete will authorize registry access for your organization.
#### 3. Generate Organization Token
* In the Authlete Console, generate a **Token** for your organization.
* Keep the **Organization ID** and **Token** handy for authentication.
### Setup Phase: Ansible Execution Environment
Setup Ansible execution environment.
#### 1. Install Python Requirements:
Install `boto3` and `botocore` for AWS interactions. Use the following command:
```console theme={null}
pip3 install boto3 botocore
```
You will also need the Python `requests module` which you can install with the following command:
```console theme={null}
pip3 install requests
```
#### 2. Install Ansible
To install `ansible`, run the following: Ansible version (>=11.5.0) is recommended.
```shell theme={null}
pip3 install "ansible>=11.5.0"
```
#### 3. Install Ansible Collections
**Option 1:** Install the latest Ansible collection: `community.aws` and `amazon.aws` dependency:
```shell theme={null}
ansible-galaxy collection install --force community.aws:==9.2.0
ansible-galaxy collection install --force amazon.aws:==9.4.0
```
**Option 2:** Create and add the `community.aws` collection and `amazon.aws` dependency to a requirements.yml file:
```yaml theme={null}
collections:
- name: community.aws
version: 9.2.0
- name: amazon.aws
version: 9.4.0
```
Then, install the requirements:
```shell theme={null}
ansible-galaxy collection install -r requirements.yml
```
#### 4. Setup AWS CLI Environment
Before anything, you will need to set up your AWS account. Contact your AWS administrator and have them create credentials for you.
**Install AWS CLI**
Use the [following guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) to install or update to the latest version of AWS CLI. Skip this step if your AWS CLI environment is already up to date.
**AWS Configuration**
If you do not have a pre-existing folder, create `~/.aws`.
Once done, create a `~/.aws/credentials` file containing the following information :
```shell theme={null}
[default]
aws_access_key_id =
aws_secret_access_key =
```
You can find these keys in your `AWS IAM` account settings.
Then, create another `~/.aws/config` file containing the following information :
```shell theme={null}
[default]
region =
output = json
```
This corresponds to the region under which you have created your ECS cluster.
### Preparation and Configuration Phase
#### 1. Pull the ansible-playbook
First, log into the artifact registry using your organization identifiers :
```shell theme={null}
docker login artifacts.authlete.com -u -p
```
Then, pull the Ansible playbook from the artifact registry :
```shell theme={null}
crane pull artifacts.authlete.com/authlete-platform-playbook:1.0.0 ansible-playbook-image.tar.gz
```
Finally, extract the files on your system :
```shell theme={null}
mkdir extract-image
tar xzf ansible-playbook-image.tar.gz -C extract-image
ls extract-image
73d73aa9481500d08886e6510eed900ff301dfcaccc55f5c80ac488b78caa470.tar.gz
manifest.json
sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
mkdir ansible-playbook
tar xzf extract-image/73d73aa9481500d08886e6510eed900ff301dfcaccc55f5c80ac488b78caa470.tar.gz -C ansible-playbook
# This tarball file will have a random name
ls ansible-playbook
ansible.cfg
deployment-guide.md
files
galaxy.yml
group_vars
inventories
playbooks
README.md
roles
rm -r ansible-playbook-image.tar.gz extract-image
```
Create configuration files from sample files for further editing.
```shell theme={null}
cd ansible-playbook
cp -a group_vars/all.yml.sample group_vars/all.yml
cp -a group_vars/aws_ecs.yml.sample group_vars/aws_ecs.yml
```
You can check the `group_vars/aws_ecs.yml` file provided by Ansible for configuration details.
This file contains all the required keys with dummy placeholder values that need to be replaced with your AWS resource identifiers.
* `ecs_cluster_name`
* `execution_role_arn`
* `efs_filesystem_id`
* `efs_access_point_id`
* `efs_access_point_arn`
* `aws_logs_groups`
* `cloudmap_registry_arns`
The following steps will show you how to configure the values in `group_vars/aws_ecs.yml` file.
#### 2. Create AWS ECS
Once your AWS account is set up, create a `Fargate ECS cluster` with a name of your choosing.
Use the following `aws cli` command to display your ECS cluster information:
```shell theme={null}
aws ecs describe-clusters --clusters
```
In the `group_vars/aws_ecs.yml` file, you can now replace the following AWS resource identifiers with your AWS ECS values:
```yaml theme={null}
# AWS account identifier
aws_account_id: "123455673100"
# Region in which ECS resources will be provisioned
region: us-east-2
# ECS cluster name where services will be deployed
ecs_cluster_name:
```
#### 3. Create AWS ECR Repositories
Create ECR repositories. Navigate to the ECR dashboard and create the following repositories:
1. `/server-db-schema`
2. `/server`
3. `/idp-db-schema`
4. `/idp`
5. `/authlete-bootstrapper`
6. `/console`
7. `/nginx`
8. `/alpine`
9. `/valkey` (Optional, you can disable the `valkey` option in the `all.yml` file)
***NOTE:*** Keep in mind that the `namespace` in the repository name must be the same as your ``.
To correctly setup the `ecr_repo_url`, copy/paste the URI of one of your ECR repositories, and remove the repository name at the end (the final result should end with `amazonaws.com`)
Use the following command to list your repositories.
```shell theme={null}
aws ecr describe-repositories | grep repositoryArn
```
In the `group_vars/aws_ecs.yml` file, replace the value of `repo`. Make sure not to configure the container images and tags.
```yaml theme={null}
#### #### #### #### ####
# ECR Container Images
# Dont change the Container images or tags, unless specified by support@authlete.com or
# unless mentioned in our deployment guide
repo: 123456789.dkr.ecr.us-east-2.amazonaws.com
```
#### 4. Create AWS ECS Task
From your AWS ECS cluster, go to Tasks and follow instructions to create your task `role` and `execution`.
When your task is launched, find and replace the following AWS resource identifiers with your AWS ECS values.
* The `execution_role_arn` and `task_role_arn` values can be found under the task definitions.
* The `subnet_id` values can be found under the tasks.
In the `group_vars/aws_ecs.yml` file, configure the following:
```yaml theme={null}
# IAM role for ECS tasks to pull images and write logs
execution_role_arn: arn:aws:iam::123455673100:role/ecsTaskExecutionRole
# IAM role for ECS tasks to access AWS services at runtime
task_role_arn: arn:aws:iam::123455673100:role/ecsTaskRole
# Security group ID attached to the ECS service for network rules
security_group_id: sg-0f8bef00
# Subnet ID (within a VPC) where ECS tasks will be launched
subnet_id: subnet-4af11000
```
#### 5. Create AWS Target Groups
In the AWS EC2 dashboard, under `Load Balancing`, create the following target groups:
1. `authlete-console-tg`
2. `authlete-idp-tg `
3. `authlete-api-tg`
4. `authlete-proxy-tg`, Health check path=/health
5. `authlete-valkey-tg`
Make sure these target groups should be of type `IP address` and use the corresponding container port number.
Finally, in the `group_vars/aws_ecs.yml` file, replace the following target group ARNs with your AWS EC2 resource identifiers.
```yaml theme={null}
# Application Load Balancer (ALB) target groups per microservice
api_target_group_arn: arn:aws:elasticloadbalancing:us-west-1:123456789012:targetgroup/my-api-tg/00be0deb21695fcde5
idp_target_group_arn: arn:aws:elasticloadbalancing:us-west-1:123456789012:targetgroup/my-idp-tg/abcd1234ijkl5678
console_target_group_arn: arn:aws:elasticloadbalancing:us-west-1:123456789012:targetgroup/my-console-tg/abcd1234mnop5678
proxy_target_group_arn: arn:aws:elasticloadbalancing:us-west-1:123456789012:targetgroup/my-proxy-tg/abcd1234qrst5678
valkey_target_group_arn: arn:aws:elasticloadbalancing:us-west-1:123456789012:targetgroup/my-valkey-tg/abcd1234qrst5876
```
#### 6. Create AWS ALB
Create the following load balancers:
1. `authlete-lb`
2. `idp-lb`
3. `console-lb`
4. `proxy-lb`
Set the following fields for each of the load balancers:
* Availability zones
* Security group
* Listener port and forwarding
* Certificate (required for HTTPS protocol only)
#### 7. Create AWS Cloud Map
* Navigate to [AWS Cloud Map](https://docs.aws.amazon.com/cloud-map/latest/dg/what-is-cloud-map.html) and create a new namespace.
This namespace must have the same name as your previously created ECS cluster.
Navigate to the newly created `CloudMap namespace` and create the following `services`:
1. console
2. idp
3. authlete-api
Replace the following AWS resource identifiers with your AWS Cloud Map values.
To list any AWS services, use the following:
```shell theme={null}
aws servicediscovery list-services
```
In the `group_vars/aws_ecs.yml` file, configure the following:
```yaml theme={null}
# Service discovery (Cloud Map) registry ARNs for internal DNS-based discovery
api_cloudmap_registry_arn: arn:aws:servicediscovery:us-west-1:123456789012:service/srv-xxxxxxxxxxxxxxxx
idp_cloudmap_registry_arn: arn:aws:servicediscovery:us-west-1:123456789012:service/srv-yyyyyyyyyyyyyyyy
console_cloudmap_registry_arn: arn:aws:servicediscovery:us-west-1:123456789012:service/srv-zzzzzzzzzzzzzzzz
```
To correctly set this up, copy/paste your `CloudMap namespace` ARN, then replace `namespace/...` with `service/{SERVICE_ID}` for each registry
#### 8. Create AWS CloudWatch Log Group
In the `AWS CloudWatch` dashboard, under Logs, create a new log group with a name of your choosing.
In the `group_vars/aws_ecs.yml` file:
```yaml theme={null}
# CloudWatch Logs group name for ECS container logs
aws_logs_groups: /ecs/authlete
```
#### 9. Create AWS RDS Database
Create an AWS RDS Database with the following configurations:
* Engine type: MySQL
* Engine version: 8.0.X
Before starting the installation, ensure that both of the following databases are created.
```shell theme={null}
create database authlete;
create database idp;
```
After creating RDS Database, configure the following values:
```shell theme={null}
api_db_user:
api_db_password:
api_db_root_password:
api_db_host: .*****.ap-northeast-1.rds.amazonaws.com
api_db_port: 3306
api_db_type_name: mysql
api_db_type: mariadb
api_db_name: authlete #The name of database you created
idp_db_user:
idp_db_password:
idp_db_root_password:
idp_db_host: .*****.ap-northeast-1.rds.amazonaws.com
idp_db_port: 3306
idp_db_type_name: mysql
idp_db_type: mariadb
idp_db_name: idp #The name of database you created
```
#### 10. Create AWS EFS
* Create an [Amazon EFS](https://aws.amazon.com/efs/?did=ap_card\&trk=ap_card) and replace the following AWS resource identifiers with your Amazon EFS Filesystem values.
In the `group_vars/aws_ecs.yml` file, configure the following EFS values:
```yaml theme={null}
# Amazon EFS (Elastic File System) configuration for persistent shared storage
efs_filesystem_id: fs-02e6123720a9550b5
efs_access_point_id: fsap-02e6123720a9550b5
efs_access_point_arn: arn:aws:elasticfilesystem:us-west-1:123456789012:file-system/fs-02e6123720a9550b5
efs_filesystem_id_valkey: fs-0ba6a21018f65eaas
efs_access_point_id_valkey: fsap-007fd448ce3basass
efs_access_point_arn_valkey: arn:aws:elasticfilesystem:us-west-1:123456789012:file-system/fs-0ba6a21018f65eaas
#### #### #### #### ####
```
#### 11. Container Images
The Ansible playbook automates the transfer of Authlete-provided container images to a customer's own container registry. This avoids direct runtime dependency on Authlete's registry, and ensures reproducible deployments.
* Make sure not to modify the following container images and tags in the `group_vars/aws_ecs.yml` file.
| Image | Description | Supported Version Tags |
| - | - | - |
| server | Core API server that handles OAuth 2.0 and OpenID Connect operations | 3.0.11 |
| server-db-schema | Database schema initialization tool for the API server | v3.0.11 |
| idp | Identity Provider server for user authentication and management | 1.0.5 |
| idp-db-schema | Database schema initialization tool for the IDP server | v1.0.5 |
| console | React based management console for platform configuration and monitoring | v1.0.5 |
| nginx | Nginx-based reverse proxy for handling TLS termination and routing | 1.26.3 |
| valkey | Caching service for improved performance and reduced database load | 8.0.1 |
| alpine | A minimal Docker image based on Alpine Linux, designed for security, simplicity, and resource efficiency. Commonly used as a base image for lightweight containers. | 3.18 |
| authlete-bootstrapper | Initialization service for the platform. Only used during first deployment. | 1.0.0 |
```yaml theme={null}
images:
api:
name: server
tag: "3.0.11"
server_db_schema:
name: server-db-schema
tag: "v3.0.11"
idp:
name: idp
tag: "1.0.5"
idp_db_schema:
name: idp-db-schema
tag: "v1.0.5"
console:
name: console
tag: "1.0.5"
bootstrapper:
name: authlete-bootstrapper
tag: "1.0.0"
proxy:
name: nginx
tag: "1.26.3"
valkey:
name: valkey
tag: "8.0.1"
proxy_sidecar:
name: alpine
tag: "3.18"
#### #### #### #### ####
```
#### 12. Authlete URLs
Configure Authlete URLs:
Make sure you follow the required format to avoid errors:
In the `group_vars/aws_ecs.yml` file, configure the following:
1. `authlete_api_url`
2. `authlete_idp_base_url`
3. `authlete_idp_console_url`
```yaml theme={null}
#### #### #### #### ####
#### URLs to be used (without http:// or https://)
authlete_api_url: "" # eg: authlete-api.example.com
authlete_idp_base_url: "" # eg: authlete-idp.example.com
authlete_idp_console_url: "" # eg: authlete-console.example.com
url_scheme: "https" # either http or https
#### #### #### #### ####
```
#### 13. Authlete Email and Password
Configure your `admin_user_email` and `admin_user_password`
* Your first Authlete user will be instantiated with these values.
In the `group_vars/aws_ecs.yml` file, configure the following:
```yaml theme={null}
# It create's necessary configuartions for Authlete to start
admin_user_email: "" # eg: admin@example.com, Admin email used by the bootstrapper to create or log into the Authlete system
admin_user_password: # Corresponding password for the admin user (used during initial setup only)
boot_task_def: bootstrapper-task # ECS task definition name for the bootstrapper container/task
```
#### 14. Configure Authlete Organization ID and Token
Log into the Authlete console and find the Authlete `Organization Id` and `Organization Token` for the following step.
Configure your Authlete `Organization Id` and `Organization Token` in the `group_vars/all.yml` file:
```yaml theme={null}
authlete_org_id: ""
authlete_org_token: ""
```
### Validation Phase
Now that you have created and configured all the required AWS resources, you will need to update values in the Ansible group\_vars so that your installation correctly points towards them.
Run the environment validation playbook to verify that all required AWS infrastructure is in place. However, you may choose to skip this step as it will be automatically executed during the installation process.
Run the following ansible playbook command:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags prereq --skip-tags=install,uninstall,upgrade,rollingupgrade
```
If the output displays `failed=0`, it means this step is completed.
### Deployment Phase
#### 1. Install the Authlete Stack
* To install the Full Authlete Stack, run the following ansible commands:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags aws,install --skip-tags upgrade,uninstall,rollingupgrade
ansible-playbook playbooks/site.yml --tags aws,upgrade --skip-tags install,uninstall,rollingupgrade
```
Each of these commands will take approximately from 10 to 20 minutes to complete.
### Uninstall Phase
#### 1. Uninstall the Stack
* To uninstall the full Authlete Stack:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags aws,uninstall --skip-tags install,upgrade,rollingupgrade
```
### Manage Individual Components
#### 1. Authlete API
* To install:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags api,install_api --skip-tags uninstall,upgrade,rollingupgrade
```
* To uninstall:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags api,uninstall_api --skip-tags install,upgrade,rollingupgrade
```
* Rolling upgrade:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags api,rollingupgrade --skip-tags install,upgrade,uninstall
```
#### 2. Authlete IDP
* To install:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags idp,install_idp --skip-tags uninstall,rollingupgrade
```
* To uninstall:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags idp,uninstall_idp --skip-tags upgrade,rollingupgrade
```
Rollling upgrade:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags idp,rollingupgrade --skip-tags upgrade,uninstall
```
#### 3. Authlete Console
* To install:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags console,install_console --skip-tags uninstall,rollingupgrade
```
* To uninstall:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags console,uninstall_console --skip-tags upgrade,rollingupgrade
```
* Rolling upgrade:
```shell theme={null}
ansible-playbook playbooks/site.yml --tags console,rollingupgrade --skip-tags upgrade,uninstall
```
# HTTPS settings for Authlete API servers
Source: https://developers.authlete.com/deployment-and-operations/self-managed-deployment/https-settings-for-authlete-api-servers
Technical information on configuring HTTPS settings for Authlete API servers in on-premises deployments.
For **Authlete 2.x** documentation, see [2.x version](/v2/deployment-and-operations/self-managed-deployment/https-settings-for-authlete-api-servers).
In a typical on-premises deployment, Authlete API servers are configured to enable HTTPS in either way:
* Making Java Servlet containers (that hosts Authlete) HTTPS-enabled
* Terminating TLS connection at load balancers in front of Authlete API servers
# Kubernetes Installation Guide
Source: https://developers.authlete.com/deployment-and-operations/self-managed-deployment/kubernetes-installation-guide
Follow our Kubernetes Installation Guide to deploy Authlete using Helm in a Kubernetes environment.
## Prerequisites
* A Kubernetes cluster (v1.24 or later) with access to your MySQL instance (v8.0+)
* Minimum cluster resources: 4 vCPUs, 16GB RAM
* Database credentials with read/write and delete access to the tables
* Helm (version 3.17.0 or later)
* Domain names for API, Console and IdP endpoints
## Installation Steps
To access Helm charts and container images from the Authlete registry, follow these steps:
### Setup Phase
#### 1. Create an Organization
* Log in to the [Authlete Console](https://console.authlete.com).
* Create an organization for your company.
* Note down the **Organization ID**.

#### 2. Request Access
* Share the **Organization ID** and **Organization Name** with Authlete Support.
* Authlete will authorize registry access for your organization.
#### 3. Generate Organization Token
* In the Authlete Console, generate a **Token** for your organization.
* Keep the **Organization ID** and **Token** handy for authentication.

### Preparation Phase
#### 1. Create a Kubernetes Namespace
```bash theme={null}
kubectl create ns authlete
```
#### 2. Log in to the Helm Registry
Use the following command to log in:
```bash theme={null}
helm registry login -u -p artifacts.authlete.com
```
Replace `` and `` with your actual values.
Once logged in, you can pull Helm charts from the registry.
#### 3. Pull Helm Chart
Authlete Helm chart is distributed using the OCI format. Use the following command to pull and extract the chart locally:
```bash theme={null}
#Current stable version is 2.1.7
helm pull oci://artifacts.authlete.com/authlete-platform-chart --version 2.1.7 --untar
cd authlete-platform-chart
```
#### 4. Image Registry Setup
You have two options for accessing Authlete container images:
**Option A: Direct Registry Access (Development/Evaluation)**
For development or evaluation environments, you can pull images directly from Authlete's registry:
```bash theme={null}
# Create a secret for registry authentication
kubectl create secret docker-registry authlete-registry \
-n authlete \
--docker-server=artifacts.authlete.com \
--docker-username= \
--docker-password=
# Configure the default ServiceAccount to use this secret
kubectl patch serviceaccount default \
-n authlete \
-p '{"imagePullSecrets": [{"name": "authlete-registry"}]}'
```
**Option B: Mirror Images (Production Recommended)**
For production environments, see the section below for instructions on mirroring images to your private registry.
#### 5. Mirror Images
For improved reliability and control, we recommend customers mirror Authlete-provided container images to their own container registry. This avoids direct runtime dependency on Authlete's registry, and ensures reproducible deployments.
| Image | Description | Supported Version Tags |
| - | - | - |
| server | Core API server that handles OAuth 2.0 and OpenID Connect operations | 3.0.35 |
| server-db-schema | Database schema initialization tool for the API server | v3.0.35 |
| idp | Identity Provider server for user authentication and management | 1.0.30 |
| idp-db-schema | Database schema initialization tool for the IdP server | v1.0.30 |
| console | React based management console for platform configuration and monitoring | 1.0.25 |
| token-migrator | A stateful background job that migrates OAuth tokens from Authlete v2.3 to v3.0 | 1.4 |
| authlete-ops | A cronjob to run scheduled cleanup | 0.3.1 |
##### Mirror using docker
```bash theme={null}
# Authenticate to Authlete registry
docker login artifacts.authlete.com -u -p
# Pull an image
docker pull artifacts.authlete.com/:
# Tag and push to your own registry
docker tag artifacts.authlete.com/: registry.mycompany.com/:
docker push registry.mycompany.com/:
```
Update your `values.yaml` to use the mirrored image paths before running the installation.
> **Note**: `docker pull` resolves a multi-architecture image to a single architecture, so
> the image you push to your own registry has a different digest from the one Authlete
> published, and the signature and SBOM attached to the original no longer apply to it. Use
> the `crane` method below if you intend to verify the images after mirroring.
##### Mirror using crane
Alternatively, you can use [crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane) if you want to directly push the images to your own registry.
```bash theme={null}
# Set your target registry base
TARGET_REGISTRY="ghcr.io/your-org-name"
# Image copy commands
crane cp artifacts.authlete.com/server:3.0.35 $TARGET_REGISTRY/server:3.0.35
crane cp artifacts.authlete.com/server-db-schema:v3.0.35 $TARGET_REGISTRY/server-db-schema:v3.0.35
crane cp artifacts.authlete.com/idp:1.0.30 $TARGET_REGISTRY/idp:1.0.30
crane cp artifacts.authlete.com/idp-db-schema:v1.0.30 $TARGET_REGISTRY/idp-db-schema:v1.0.30
crane cp artifacts.authlete.com/console:1.0.25 $TARGET_REGISTRY/console:1.0.25
crane cp artifacts.authlete.com/token-migrator:1.4 $TARGET_REGISTRY/token-migrator:1.4
crane cp artifacts.authlete.com/authlete-ops:0.3.1 $TARGET_REGISTRY/authlete-ops:0.3.1
crane cp nginx:1.30.5 $TARGET_REGISTRY/nginx:1.30.5
```
`crane cp` copies the image itself. Signatures and SBOM attestations are stored as separate
tags alongside it and are not copied with it, so mirror them explicitly if you want to keep
them:
```bash theme={null}
for img in server:3.0.35 server-db-schema:v3.0.35 idp:1.0.30 idp-db-schema:v1.0.30 console:1.0.25; do
cosign copy --only=sig,att artifacts.authlete.com/$img $TARGET_REGISTRY/$img
done
```
> **Note**: The image tags listed here correspond to the latest Helm chart version. For older Helm chart versions, please refer to
> the image tags defined within that specific chart version.
#### 6. Verify Image Signatures and SBOMs (Optional)
Authlete signs its container images with [cosign](https://github.com/sigstore/cosign) and
attaches a CycloneDX Software Bill of Materials (SBOM) to them as a signed attestation.
Verifying them confirms that an image was published by Authlete and has not been altered,
and gives you the component inventory of that image for your own vulnerability scanning.
The following images carry both a signature and an SBOM attestation:
* `server` and `server-db-schema`
* `idp` and `idp-db-schema`
* `console`
`token-migrator` and `authlete-ops` carry neither.
> **Note**: signatures and SBOM attestations are available from `server` 3.0.35,
> `console` 1.0.25 and `idp` 1.0.30 onwards; `server-db-schema` and `idp-db-schema`
> follow their respective versions. Earlier releases carry neither.
This step requires [cosign](https://github.com/sigstore/cosign) and
[crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane).
##### Authlete's signing key
Save the following as `cosign.pub`:
```
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmOCQfS4MuzCm8p3vKd8ccRYRZ0m/
P0dR3b5ChXjUtt/w3kQ38VbbomXVaNe4X703fc0y1TOx/lLy0Fa8btUcag==
-----END PUBLIC KEY-----
```
The SHA-256 fingerprint of the DER-encoded public key is
`fafe88f5f3ca31cc29557de31828fed2f57faa9aeefa4f9c8b5b209f80d845de`, which you can confirm
with:
```bash theme={null}
openssl pkey -pubin -in cosign.pub -outform DER | sha256sum
```
##### Verify an image and retrieve its SBOM
Signatures and SBOM attestations are attached to the per-architecture image digests rather
than to the multi-architecture tag, so resolve the digest of the architecture you run before
verifying.
```bash theme={null}
IMAGE=artifacts.authlete.com/server
TAG=3.0.35
# Resolve the digest for your architecture
DIGEST=$(crane digest --platform linux/amd64 $IMAGE:$TAG)
# Verify the signature
cosign verify --key cosign.pub --insecure-ignore-tlog=true $IMAGE@$DIGEST
# Verify the SBOM attestation and extract the SBOM
cosign verify-attestation --type cyclonedx --key cosign.pub --insecure-ignore-tlog=true \
$IMAGE@$DIGEST | jq -r .payload | base64 -d | jq .predicate > server-amd64.cdx.json
```
Repeat with `--platform linux/arm64` for the arm64 image. The two SBOMs differ, because the
operating system packages of the two images are not the same.
The extracted SBOM is a standard CycloneDX document and can be scanned offline, without
pulling the image:
```bash theme={null}
trivy sbom server-amd64.cdx.json
```
> **Note**: `--insecure-ignore-tlog=true` is required. Authlete signs with a long-lived key
> held in a hardware-backed key management service and does not publish signatures to the
> public Rekor transparency log.
### Configuration Phase
#### 1. Configure Values and Secrets
* The default `values.yaml` is already bundled inside the chart. You can inspect or modify it for custom configurations.
* Update the `global.repo` to your own registry.
```yaml theme={null}
global:
id: "authlete-platform"
repo: "registry.your-company.com" # Required: Your container registry
```
* Update the `domains` section with your domain names:
```yaml theme={null}
# Required: These domains must be accessible from your users
api: "api.your-domain.com" # API server
idp: "login.your-domain.com" # IdP server
console: "console.your-domain.com" # Management console
```
> **Important**: Domain names cannot be changed after the initial installation. If you need to change a domain name, you must reinitialize the database and reinstall Authlete from scratch. Choose your domain names carefully before proceeding.
#### 2. Configure TLS Certificates
* Get a TLS certificate for your domain (e.g. from your certificate authority or Let's Encrypt), and create a Kubernetes secret of type `kubernetes.io/tls` in the `authlete` namespace before installation. The certificate should cover all domains used by the platform (e.g. `api.example.com`, `login.example.com`, `console.example.com`). You can use a wildcard or a SAN certificate.
```yaml theme={null}
kubectl create secret tls proxy-certs \
--cert=./tls.crt \
--key=./tls.key \
-n authlete
```
> **Note**: If you want to manage proxy-certs through External Secret Manager and skip this step,
> refer to the [External Secret Manager](#9-external-secret-manager) section below
#### 3. Enabling TLS connection to database and memory cache (Optional)
##### Application Configuration
TLS encryption can be enabled with simple configuration changes.
**Redis**: To enable TLS encryption for the Redis connection, set `tls: true` in the cache connection configuration:
```yaml theme={null}
cache:
api:
enabled: true
provider: standalone
auth:
enabled: false
connection:
tls: true
host: redis
port: 6379
idp:
enabled: true
provider: standalone
auth:
enabled: false
connection:
tls: true
host: redis
port: 6379
```
**Database**: TLS connection to the database can be enabled with `sslMode` property:
```yaml theme={null}
database:
idp: # IdP Server Database
name: idp
type: mysql # mysql|postgres (defaults to mysql if unset)
host: localhost
connectionParams:
allowPublicKeyRetrieval: true
sslMode: verify-ca # Configure the sslMode here.
api: # API Server Database
name: authlete
type: mysql # mysql|postgres (defaults to mysql if unset)
host: localhost
connectionParams:
allowPublicKeyRetrieval: true
sslMode: verify-ca # Configure the sslMode here.
```
> **Note**: If the certificate presented by Redis or Database over TLS is issued by a private CA, you should create a custom bundle containing all certificates and load the bundle into the application. Please refer to the [Private CA](#4-private-ca-optional) section for more details.
#### 4. Private CA (Optional)
To use TLS with private CA certificate, a custom bundle containing all necessary certificates needs to be prepared outside of the Helm Chart. Next, we will create a ConfigMap that contains the bundles, which satisfies the following contract:
* ConfigMap must be named `custom-ca-bundle`
* It must contain at least 2 keys: `ca-bundle.p12` and `ca-bundle.crt`
##### Creating CA bundle
We recommend extracting the public CA bundle as described in the [trust-manager documentation](https://cert-manager.io/docs/trust/trust-manager/#securely-maintaining-a-trust-manager-installation). Below is an explanation of how to create a ConfigMap with the bundle.
##### Creating bundle with Trust Manager (Optional)
Trust Manager makes it easy to manage trust bundles in Kubernetes. For installation instructions please consult [trust-manager's official documentation](https://cert-manager.io/docs/trust/trust-manager). Once `trust-manager` is installed, you will need to create a single bundle object. The ConfigMap containting all necessary certificates will be then generated automatically.
Here is an example `Bundle` from which trust-manager will assemble the final bundle. This Bundle's manifest file name is assumed to be `bundle-creation.yaml`
```yaml theme={null}
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: custom-ca-bundle
spec:
sources:
- useDefaultCAs: true
# A manually specified PEM-encoded cert, included directly into the bundle
- inLine: |
-----BEGIN CERTIFICATE-----
MIIC5zCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
...
... contents of proxy's CA certificate ...
-----END CERTIFICATE-----
- inLine: |
-----BEGIN CERTIFICATE-----
... contents of mysql's CA certificate ...
-----END CERTIFICATE-----
target:
# All ConfigMaps will include a PEM-formatted bundle, here named "root-certs.pem"
# In this case we also request binary formatted bundles in PKCS#12 format, here named "bundle.p12"
configMap:
key: "ca-bundle.crt"
additionalFormats:
pkcs12:
key: "ca-bundle.p12"
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: "authlete" # Deployment namespace
```
Finally, create the bundle with the following command:
```bash theme={null}
kubectl create -f bundle-creation.yaml -n authlete
```
> **Note**: Please note that if you're using an official cert-manager-provided Debian trust package, you should check regularly to ensure the trust package is kept up to date.
##### Manual creation of bundle
The custom bundle can created in a few simple steps by using Docker commands.
> **Note**: The tutorial below demonstrates sample code only. Customers are responsible for creating, storing, and updating bundles securely.
1. Create file named `gen-bundles.sh`
```bash theme={null}
# Input/output directories
SRC="/certs" # Location to place crt files
DST="/usr/local/share/ca-certificates/custom" # Directory monitored by update-ca-certificates
OUT="/bundle" # Output directory for the bundle
# Clean up output directory
rm -f $OUT/*
# Create output directories
mkdir -p "$DST"
# Install required packages (ca-certificates-java is necessary for generating the Java truststore)
apt-get -y update
apt-cache madison ca-certificates
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssl default-jre-headless ca-certificates-java "ca-certificates=20230311+deb12u1"
# 1) Place additional certificates (*.pem / *.crt placed as .crt)
cp -v ${SRC}/* ${DST}/
# If you want to unify the extension so that .pem files are also treated as .crt, enable the following:
# for f in "$DST"/*.pem; do mv -v "$f" "${f%.pem}.crt"; done
# 2) Update system & Java truststore
update-ca-certificates -f
# 3) Output (PEM and PKCS12)
cp -v /etc/ssl/certs/ca-certificates.crt "$OUT/ca-bundle.crt"
keytool -importkeystore -srckeystore /etc/ssl/certs/java/cacerts -destkeystore $OUT/ca-bundle.p12 -deststoretype PKCS12 -srcstorepass changeit -deststorepass changeit
```
2. Prepare certificates to trust
Place the additional trusted certificates (one or more files) under the `certs` directory with `.crt` extension.
```
./certs/
├── sql-ca.crt
├── redis-ca.crt
└── ...
```
3. Build the bundle
```bash theme={null}
docker run --rm --user root -v "$PWD/certs:/certs:ro" -v "$PWD/out:/bundle" -v "$PWD/gen-bundles.sh:/usr/local/bin/gen-bundles.sh:ro" --entrypoint bash docker.io/library/debian:12-slim -c "sh /usr/local/bin/gen-bundles.sh"
```
4. After preparing `ca-bundle.p12` and `ca-bundle.crt` files, you can create the kubernetes ConfigMap with the following command:
```bash theme={null}
kubectl create cm custom-ca-bundle --from-file=ca-bundle.p12 --from-file=ca-bundle.crt
```
> **Note**:
> The current debian base image used for the trust-manager's bundle creation is [docker.io/library/debian:12-slim](https://github.com/cert-manager/trust-manager/blob/main/make/00_debian_bookworm_version.mk) and ca-certificates target version is at `20230311+deb12u1.6`
##### Loading the custom bundles from the application
Now that the bundle is created and ready for use, we will need to configure one more property to make the bundles available for applications. Set `.Values.global.tls.caBundle.enabled` to `true` so that the bundle is mounted into the application pods.
```yaml theme={null}
global:
tls:
caBundle:
enabled: true # Enable this
type: ConfigMap
name: custom-ca-bundle
trustStore:
password: "changeit"
```
##### Updating the bundle
If you created a new bundle and replaced the old bundle with it, you should restart IdP, Authlete Server and proxy pods(applications). On a restart, applications will load the updated bundle.
#### 5. Configure Database Connection
The platform requires two databases: one for the API server and one for the IdP server. Configure the connection details in `secret-values.yaml`:
> **Note**: For MySQL 8.0+, ensure your databases are configured with:
>
> * Character set: `utf8mb4`
> * Collation: `utf8mb4_0900_ai_ci`
>
> If using PostgreSQL 16 or 17, you can use the standard configuration below:
>
> * Character set: `UTF8`
> * Collation: `en_US.UTF8`
* A template `secret-values.yaml` file is also included in the chart archive. Modify `secret-values.yaml` with your database and Authlete admin credentials.
```yaml theme={null}
database:
idp: # IdP Server Database
user: authlete # Database user
password: !raw ***** # User password
api: # API Server Database
user: authlete
password: !raw ******
idp:
auth:
adminUser:
email: "admin@authlete.com"
password: !raw ******
encryptionSecret: ********
```
> **Note**: For AWS-EKS, you may use a DNS name for your MySQL Database with or without RDS-Proxy:
>
> * with RDS-Proxy: `host: sample-db.proxy-stu901vwx234.us-east-1.rds.amazonaws.com`
> * without RDS-Proxy: `host: sample-db.cluster-stu901vwx234.us-east-1.rds.amazonaws.com`
#### 6. Configure Caching
Below are the caching configuration options currently supported by the platform.
##### Managed Cache Services
For production environments, you might prefer using managed cache services. The platform supports various managed services including:
* **Google Cloud Platform**: Memorystore for Valkey
* **Amazon Web Services**: ElastiCache for Redis or Serverless-Valkey
* **Azure**: Azure Cache for Redis
#### 7. Configure Maintenance Tasks
To keep the platform stable, avoid using unnecessary storage and ensure optimal performance, deployments must run regularly scheduled maintenance tasks. Review the following tasks and configure the required tasks.
##### Data Cleanup
As Authlete handles high volumes of OAuth 2.0 and OpenID Connect transactions, it accumulates transient records such as expired tokens, authorization codes, and other types of codes. To maximize API throughput and during runtime operations, Authlete does not immediately delete all expired records at the exact moment of expiration. Instead, they are marked as expired and are deleted as part of this task. If left unchecked, this data bloat can degrade database query performance and consume excessive storage.
You should schedule the cleanup task to run regularly to perform the physical removal of all expired or delete records. To do so modify the following section in the `values.yml` file to provide the times at which the process should be run. The `schedule` property uses the format of schedules in kubernetes).
```yaml theme={null}
ops:
cleanup:
enabled: true
schedule: "0 3 * * *"
timezone: "Etc/UTC"
```
> **Important:** For production environments, we recommend executing the cleanup process at least once a day during off-peak hours (e.g., 2:00 AM local time).
###### Use a dedicated API instance
The maintenance task uses Authlete API to perform the actual removal of data and it has been designed to have little to no impact on the instances used to perform the data removal. However, in some environments it is desired that the maintenance task do not use any of the API instances serving customer requests, in these cases you can enable the option `deployApiSidecar: true` which will deploy a specific copy of API to be used by the maintenance task. Keep in mind that this will require additional resources.
#### 8. HSM configuration
Authlete currently supports Google Cloud HSM and AWS KMS. Refer to the configurations below if your setup relies on either one of those.
For Google CloudHSM:
```yaml theme={null}
hsm:
enabled: true
provider: "google"
google:
projectId: "" # required - e.g. "authlete-project-1"
location: "" # required - e.g. "us-west1-c"
aws:
region: ""
```
For AWS KMS:
```yaml theme={null}
hsm:
enabled: true
provider: "aws"
google:
projectId: ""
location: ""
aws:
region: "" # required - e.g. "ap-northeast-1"
```
#### 9. External Secret Manager
When using External Secret Manager, make sure the secrets needed for the helm deployment are pre-configured and present on the cluster. Any absence of the following secrets will result in deployment/upgrade failures.
Secrets needed are `authlete-credentials` and `proxy-certs` (only if you want ExternalSecretManager to manage `proxy-certs`)
##### Create a secret named `authlete-secret` in your External Secret Manager.
##### Without cache auth enabled:
```yaml theme={null}
{
"database": {
"idp": {
"user": "root",
"password": ""
},
"api": {
"user": "root",
"password": ""
}
},
"idp": {
"adminUser": {
"email": "admin@authlete.com",
"password": "mypassword"
},
"encryptionSecret": ""
},
"api": {
"shutdownToken": ""
}
}
```
##### With cache auth enabled:
```yaml theme={null}
{
"database": {
"idp": {
"user": "root",
"password": ""
},
"api": {
"user": "root",
"password": ""
}
},
"idp": {
"adminUser": {
"email": "admin@authlete.com",
"password": "mypassword"
},
"encryptionSecret": ""
},
"api": {
"shutdownToken": ""
},
"cache": {
"api": {
"auth": {
"user": "default",
"password": ""
}
},
"idp": {
"auth": {
"user": "default",
"password": ""
}
}
}
}
```
```yaml theme={null}
kubectl apply -f - < with the correct ClusterSecretStore name in your cluster
kind: ClusterSecretStore
name:
target:
name: authlete-credentials
creationPolicy: Owner
data:
# IDP Database
- secretKey: authlete-idp-db-user
remoteRef:
key: authlete-secret
property: database.idp.user
- secretKey: authlete-idp-db-password
remoteRef:
key: authlete-secret
property: database.idp.password
# API Database
- secretKey: authlete-api-db-user
remoteRef:
key: authlete-secret
property: database.api.user
- secretKey: authlete-api-db-password
remoteRef:
key: authlete-secret
property: database.api.password
# API Graceful Shutdown token
# Bearer token used by the API Pod's preStop lifecycle hook when calling
# /api/lifecycle/shutdown. Generate any 32-character random alphanumeric
# string and register it in the external secret manager.
- secretKey: authlete-api-shutdown-token
remoteRef:
key: authlete-secret
property: api.shutdownToken
# IDP Admin
- secretKey: authlete-idp-admin-email
remoteRef:
key: authlete-secret
property: idp.adminUser.email
- secretKey: authlete-idp-admin-password
remoteRef:
key: authlete-secret
property: idp.adminUser.password
- secretKey: authlete-idp-encryption-secret
remoteRef:
key: authlete-secret
property: idp.encryptionSecret
# Only needed if cache auth is enabled
- secretKey: authlete-api-memcache-auth-user
remoteRef:
key: authlete-secret
property: cache.api.auth.user
- secretKey: authlete-api-memcache-auth-password
remoteRef:
key: authlete-secret
property: cache.api.auth.password
- secretKey: authlete-idp-memcache-auth-user
remoteRef:
key: authlete-secret
property: cache.idp.auth.user
- secretKey: authlete-idp-memcache-auth-password
remoteRef:
key: authlete-secret
property: cache.idp.auth.password
EOF
```
If you want secret manager to manage your proxy certificates (proxy-certs) then create a secret by name: `authlete-proxy-secret`
```yaml theme={null}
# When storing certificates in the external secret manager, ensure the JSON keys are exactly:
- `tls.crt` for the certificate chain
- `tls.key` for the private key
# Example format:
{
"tls.crt": "-----BEGIN CERTIFICATE-----\nMIIE...server-cert-data...==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIF...intermediate-cert...==\n-----END CERTIFICATE-----",
"tls.key": "-----BEGIN PRIVATE KEY-----\nMIIE...private-key-data...==\n-----END PRIVATE KEY-----"
}
# Moving TLS certificates to the External Secret Manager is optional.
# Else follow the deployment guide to configure TLS certificates
# (Installation Steps -> Configuration Phase -> Configure TLS Certificates)
```
```yaml theme={null}
kubectl apply -f - < with the correct ClusterSecretStore name in your cluster
kind: ClusterSecretStore
name:
target:
name: proxy-certs
creationPolicy: Owner
data:
# TLS Certificates
- secretKey: tls.crt
remoteRef:
key: authlete-proxy-secret
property: tls.crt
- secretKey: tls.key
remoteRef:
key: authlete-proxy-secret
property: tls.key
EOF
```
**Note**: To enable the `externalSecrets` feature in the Helm chart, refer to the values below inside `values.yaml`
```yaml theme={null}
global:
externalSecrets:
enabled: true # Set to true to enable ESM | default 'false'
```
#### 10. Configure Pod Affinity
To enable pod affinity, add or modify the following in your `values.yaml`:
```yaml theme={null}
lobal:
apiAffinity: true
```
#### 11. Configure Cleanup
To enable cleanup within your deployment, modify the following in your `values.yaml`:
```yaml theme={null}
ops:
cleanup:
enabled: false
```
By default, `apiAffinity` is set to `false`, meaning no pod affinity preferences are applied.
Enabling pod affinity schedules proxy pods on the same node as API pods to reduce network latency, and improve performance between these tightly-coupled components.
#### 12. Other configuration options
##### Proxy configuration
If your domain name is very long, you might hit a limit on the `map_hash_max_size` parameter. You can work around this problem by increasing the values of `mapHashBucketSize` and `serverNamesHashBucketSize`.
```yaml theme={null}
configs:
mapHashBucketSize: 128 # Change this
serverNamesHashBucketSize: 128 # Change this
```
##### DB Schema update hooks
We introduced DB Schema update hooks to streamline the image version update. With these hooks, new changes in liquibase changesets are automatically applied to ensure database schema is on the same version as the application version. **These hooks are idempotent and will not change the existing database schema as long as there is no change in schema between the upgraded version and the old version.**
These hooks are required to create the database schema during installation and when updating the application version. However, if you are not deploying a new application or expecting any schema changes, they can be disabled.
```yaml theme={null}
hooks:
serviceAccount: ""
idpDbSchemaUpgrade:
enabled: true # You can disable this
serverDbSchemaUpgrade:
enabled: true # You can disable this
```
> **Note for Google Cloud Users**: When using Memorystore for Valkey, we recommend setting up connectivity using Private Service Connect (PSC) with service connection policies. This is the only supported networking method for Memorystore for Valkey. For detailed setup instructions, refer to the [Memorystore for Valkey networking documentation](https://cloud.google.com/memorystore/docs/valkey/networking).
To use a managed cache service, update your `values.yaml` file with the following information:
```yaml theme={null}
cache:
api:
enabled: true
provider: standalone
auth:
enabled: false
connection:
tls: false
host: redis
port: 6379
idp:
enabled: true
provider: standalone
auth:
enabled: false
connection:
tls: false
host: redis
port: 6379
```
> **Note**: If your managed cache service is configured in clustered mode, set the `provider` to `cluster`:
```yaml theme={null}
cache:
api:
provider: cluster
idp:
provider: cluster
```
### Deployment Phase
#### 1. Install Core Platform Components
Install the core platform components using Helm.
##### Without External Secret Manager:
```bash theme={null}
helm install authlete-platform . -n authlete -f secret-values.yaml
```
##### With External Secret Manager:
```bash theme={null}
helm install authlete-platform . -n authlete
```
##### Verify the installation:
```bash theme={null}
# Check pod status
kubectl get pods -n authlete
```
Expected output:
```
NAME READY STATUS RESTARTS AGE
api-6b78f87847-xxxxx 2/2 Running 0 2m
proxy-6c99bdc94b-xxxxx 1/1 Running 0 2m
```
Note: Initial deployment may take 5 minutes while images are pulled and databases are initialized.
#### 2. Install Optional Components
You may also install the following optional components based on your requirements:
* Management Console: Primary interface for Authlete platform configuration
* IdP Server: OIDC compliant identity provider for user authentication and management
##### Without External Secret Manager:
```bash theme={null}
helm upgrade authlete-platform . -n authlete -f secret-values.yaml
```
##### With External Secret Manager:
```bash theme={null}
helm upgrade authlete-platform . -n authlete
```
##### Verify the optional components:
```bash theme={null}
# Check new pod status
kubectl get pods -n authlete
```
Expected output:
```
NAME READY STATUS RESTARTS AGE
console-6b78f87847-xxxxx 1/1 Running 0 2m
idp-6c99bdc94b-xxxxx 2/2 Running 0 2m
```
#### 3. Configure Load Balancer
The final step is to set up a load balancer service to expose your Authlete deployment.
1. First, reserve a static external IP address in your cloud provider.
> Note: The following commands are GCP-specific. For other cloud providers (AWS, Azure, etc.), please refer to your cloud provider's documentation for reserving a static IP address.
> **You must reserve a regional static external IP address in GCP**. This is required because GKE LoadBalancer services only support IPs allocated in the same region as the cluster.
```bash theme={null}
# GCP-specific commands
# Reserve a static IP address
gcloud compute addresses create authlete-ip --region=us-central1
# Get the reserved IP address
gcloud compute addresses describe authlete-ip --region=us-central1
```
2. Using the reserved IP, Create a load balancer service named `proxy-lb-service.yaml`.
Below is an example file for GKE deployment:
```yaml theme={null}
apiVersion: v1
kind: Service
metadata:
labels:
app: proxy
name: proxy-lb
spec:
externalTrafficPolicy: Local
ports:
- name: https
port: 443
protocol: TCP
targetPort: 8443
selector:
app: proxy
sessionAffinity: None
type: LoadBalancer
loadBalancerIP: #external_static_ip # Replace with your reserved static IP
```
Below is an example file for AWS-EKS deployment:
```yaml theme={null}
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/aws-load-balancer-eip-allocations: eipalloc-xxxxxxxxxxxxxxxxx # Replace with your EIP allocation ID
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-subnets: subnet-xxxxxxxxx # Replace with your subnet ID
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
labels:
app: proxy
name: proxy-lb
spec:
externalTrafficPolicy: Local
ports:
- name: https
port: 443
protocol: TCP
targetPort: 8443
selector:
app: proxy
sessionAffinity: None
type: LoadBalancer
```
3. Apply the load balancer configuration:
```bash theme={null}
kubectl apply -f proxy-lb-service.yaml -n authlete
```
4. Verify the load balancer is properly configured:
```bash theme={null}
kubectl get service proxy-lb -n authlete
```
You should see an output similar to the following:
```
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
proxy-lb LoadBalancer 10.x.x.x YOUR_STATIC_IP 443:32xxx/TCP 1m
```
Once `EXTERNAL-IP` shows your static IP (this operation may take a few minutes), your Authlete deployment is accessible via HTTPS on that IP address.
#### 4. Map Domain to Load Balancer
Create DNS records for all three domains pointing to your load balancer IP:
```dns theme={null}
# API Server
api.your-domain.com. IN A YOUR_STATIC_IP
# IdP Server
login.your-domain.com. IN A YOUR_STATIC_IP
# Management Console
console.your-domain.com. IN A YOUR_STATIC_IP
```
Verify the DNS configuration:
```bash theme={null}
# Test DNS configuration
dig +short api.your-domain.com
dig +short login.your-domain.com
dig +short console.your-domain.com
# Test HTTPS endpoints
curl -I https://api.your-domain.com/api/info
```
If all domains resolve to your load balancer IP and the endpoints are accessible, your Authlete deployment is ready for use.
You can now access the Management Console:
1. Navigate to `https://console.your-domain.com`
2. Log in using the admin credentials specified in your `secret-values.yaml`
3. Begin configuring your Authlete services
**Note**: If you cannot access the console, verify the following:
* DNS records have fully propagated
* Load balancer health checks are passing
* TLS certificate is valid for all domains
### Migration Phase (Optional)
If you are performing a migration from a 2.3 environment to a 3.0 environment you might need to enable the **Token Migrator** which is responsible to perform [token migration](/deployment-and-operations/migration-from-existing-system/migrating-tokens-from-an-older-version-of-authlete) between the two environments.
#### 1. Create token migrator client
In order to ensure that the token migrator has access to the 3.0 environment, you need to create a service and a client in the target 3.0 environment. The service and the client you create will be used by the token migrator at startup to confirm it still has access to the environment.
To create and configure the client, follow these steps:
1. Navigate to `https://console.your-domain.com`
2. Log in using your admin credentials
3. Create a new service with the *Client credentials grant* enabled. Take note of the Service ID to be used later
4. Create a service access token with permission to use the service. Take note of the token to be used later
5. Create a client that will be used exclusively by the *token migrator* service with the following characteristics:
* Client ID Alias set to: `migration-service@system.authlete.com`
* Client Authentication Method must be set to `CLIENT_SECRET_POST`
* Supported Grant Types must include `CLIENT_CREDENTIALS`
6. Take a note of the Client Secret for the newly created client
7. In the `secret-values.yaml` file included in the chart archive modify the `tokenmigrator` section to specify the information of the newly create client
```yaml theme={null}
tokenmigrator:
clientSecret: "" # Client secret
serviceApiKey: "" # Authlete v3.0 Service ID
serviceToken: "" # Authlete v3.0 service access token
```
#### 2. Configure Databases
The *token migrator* requires to connect to the 2.3 and 3.0 databases. The information of the 3.0 database is already available in the helm chart but you will need to provide the information to connect to the 2.3 database.
1. In the `values.yaml` file configure `tokenmigrator.sourceDatabase.host` to point to the pre-existing Authlete 2.3 Database. If your 2.3 database requires TLS follow the guidance in section [Enabling TLS Connection to Database and Memory Cache](#3-enabling-tls-connection-to-database-and-memory-cache-optional) to update the `tokenmigrator.sourceDatabase.connectionParams` property.
2. In the `secret-values.yaml` file included in the chart archive modify the `tokenmigrator.database` section to specify the credentials to be used to connect to the 2.3 database.
```yaml theme={null}
tokenmigrator:
database:
user: root # Source (v2.3) database username
password: !raw example** # Source (v2.3) database password
```
#### 3. Configure External Secrets (Optional)
If you are using an *External Secret Manager* you need to add the client information and the database credentials that the token migrator will use to your external secret. To do so, add the token migrator properties using the following snippet as an example:
```yaml theme={null}
{
"database": {
"idp": {
"user": "root",
"password": ""
},
"api": {
"user": "root",
"password": ""
}
},
"idp": {
"adminUser": {
"email": "admin@authlete.com",
"password": "mypassword"
},
"encryptionSecret": ""
},
"tokenmigrator": {
"datasource-username": "",
"datasource-password": "",
"client-secret": "",
"service-api-key": "",
"service-token": ""
}
}
```
And then add the following to the external secret resource in kubernetes
```yaml theme={null}
kubectl apply -f - <