> For the complete documentation index, see [llms.txt](https://docs.fortifiedid.se/access/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fortifiedid.se/access/key-components/authenticators/protocol-managers/oidc/authorization-code-flow.md).

# Authorization Code Flow

Authenticator handling authorization code flows in OIDC.

There are two locations where configuration for an OIDC **OpenID Provider (OP)** using the **Authorization Code Flow** can be managed:

1. **Authorization Endpoint** – You can modify the authorization endpoint settings here.
2. **Back-Channel Endpoints** – By clicking the following [link](/access/key-components/modules/oidc/oidc-module.md), you can manage back-channel endpoints such as **Token** and **UserInfo**.

## About

No identification is done by this authenticator. It acts as a controller for issuing Json Web Tokens (JWT). Typically this authenticator is the first point of contact coming from an OpenID Connect Relying Party, requesting identification.

This authentication controller can be considered a start and end touch point. The main purpose is to handle OpenID Connect specifics.

Actual user identification is done elsewhere.

When using this authenticator, the ID token is fetched from the token endpoint.

## Configuration

{% hint style="info" %}
**Authenticator Type:** `OIDCAuthCodeFlow`
{% endhint %}

Common Authenticator configuration can be found [here](/access/key-components/authenticators/common-configuration.md).

{% tabs %}
{% tab title="Properties" %}

<table data-full-width="true"><thead><tr><th>Name</th><th>Description</th><th>Default value</th><th data-type="checkbox">Mandatory</th></tr></thead><tbody><tr><td><code>chain</code></td><td>List of authenticators performing the actual authentication.</td><td>N/A</td><td>true</td></tr><tr><td><code>token_code_ttl</code></td><td>Time to live in milliseconds for the generated code. Previous parameter name <code>token_endpoint_ttl</code> is deprecated.</td><td><code>60000</code></td><td>false</td></tr><tr><td><code>access_token_ttl</code></td><td>Access token time to live in milliseconds. Previous parameter name <code>userinfo_endpoint_ttl</code> is deprecated.</td><td><code>60000</code></td><td>false</td></tr><tr><td><code>enable_refresh_token</code></td><td>Enables the use of refresh tokens.</td><td><code>false</code></td><td>false</td></tr><tr><td><code>refresh_token_ttl</code></td><td>Refresh token time to live in milliseconds.</td><td><code>1800000</code></td><td>false</td></tr><tr><td><code>id_token_ttl</code></td><td>ID token time to live in minutes.</td><td><code>5</code></td><td>false</td></tr><tr><td><code>id_token_headers</code></td><td>ID token headers. Previous parameter name <code>jwt_headers</code> is deprecated.</td><td>N/A</td><td>false</td></tr><tr><td><code>id_token_claims</code></td><td>ID token claims. Previous parameter name <code>jwt_claims</code> is deprecated.</td><td>N/A</td><td>false</td></tr><tr><td><code>access_token_headers</code></td><td>Access Token headers. ONLY used when sending access tokens as JWTs.</td><td>N/A</td><td>false</td></tr><tr><td><code>access_token_claims</code></td><td>Access Token claims. Previous parameter name <code>userinfo_claims</code> is deprecated.</td><td>N/A</td><td>false</td></tr><tr><td><code>use_jwt_access_tokens</code></td><td>Enables access tokens as JWT.</td><td><code>false</code></td><td>false</td></tr><tr><td><code>jwt_access_token_ttl</code></td><td>Access Token time to live in minutes.</td><td><code>5</code></td><td>false</td></tr><tr><td><code>sign_ref</code></td><td>List of OIDC signing keys used for overlapping key rotation. Recommended for new configurations.</td><td>N/A</td><td>false</td></tr><tr><td><code>keystore</code></td><td>Legacy single-key keystore configuration used for JWT signing when <code>sign_ref</code> is not configured.</td><td>N/A</td><td>false</td></tr><tr><td><code>rps</code></td><td><a href="/access/key-components/modules/oidc/relying-party.md">List of relying parties. Must include at least one relying party.</a></td><td>N/A</td><td>true</td></tr><tr><td><code>required_request_parameters</code></td><td>Required parameters.</td><td><code>["response_type", "client_id", "redirect_uri", "scope", "nonce"]</code></td><td>false</td></tr><tr><td><code>request_object_audience</code></td><td>Expected <code>aud</code> value for signed request objects. If configured, the request object's audience must include this value.</td><td>N/A</td><td>false</td></tr><tr><td><code>no_prompt_pass_through</code></td><td>If the prompt request parameter is missing and a user session exists, the user is silently authenticated. Enables the same behaviour as if prompt=none and a user session exists.</td><td><code>false</code></td><td>false</td></tr><tr><td><code>verbose</code></td><td>Turns on deep OIDC TRACE logging for the authorization endpoint, redirects, completion handling, and logout flow when the dedicated logger category <code>fortifiedid.oidc.trace</code> is set to <code>TRACE</code>. Use for troubleshooting only.</td><td><code>false</code></td><td>false</td></tr></tbody></table>
{% endtab %}

{% tab title="Example" %}

```json
{
    "id": "auth00",
    "type": "OIDCAuthCodeFlow",
    "config": {
        "base_path": "/test/authn",
        "failure_location": "/authn/failure.html",
        "chain": [
            {
            "id" : "select",
            "required" : true
            }
        ],
        "token_code_ttl": 90000,
        "access_token_ttl": 90000,
        "id_token_ttl": 6,
        "required_request_parameters": [
            "response_type",
            "client_id",
            "redirect_uri",
            "scope",
            "nonce"
        ],
        "sign_ref": [
            {
                "keystore": {
                    "path": "/fortified_test/oidc-2026-q1.p12",
                    "password": "secret",
                    "key_password": "secret",
                    "alias": "oidc-2026-q1",
                    "type": "PKCS12"
                }
            }
        ],
        "id_token_headers": {
            "kid": ""
        },
        "id_token_claims": {
            "test_claim": "static_claim",
            "email": "${item.mail}",
            "nonce": "${request.nonce}"
        },
        "access_token_claims": {
            "userinfo_claim_test": "static_claim",
            "email": "${item.mail}",
            "email_verified": true,
            "address": {
                "street_address": "my address",
                "postal_code": "my postal code"
            },
            "given_name": "${item.givenName}",
            "subject": "${item.subject}"
        },
        "rps": [
            {
                "client_id": "provided",
                "client_secret": "provided",
                "redirect_uri": [
                    "https://auth.organisation.com/authenticate/oidcrp"
                ],
                "post_logout_redirect_uris": [
                    "https://auth.organisation.com/authenticate/oidcrp/loggedout"
                ],
                "pipe_id": "optional_pipe_id"
            }
        ]
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**NOTE:** id- & access-token claims can be configured globally on the authenticator OR for each relying party (recommended). Claims configured on the relying party trumps the global values.
{% endhint %}

{% hint style="info" %}
For the OIDC signing-key model, overlapping rotation, and legacy compatibility, see [OIDC signing keys](/access/key-components/modules/oidc/signing-keys.md).
{% endhint %}

## Signed Request Objects

If the authorization request contains the OIDC `request` parameter, FortifiedID Integrity expects a signed request object.

To enable this, configure a `request_object_keystore` on the corresponding relying party. Unsigned request objects are not accepted.

Optionally configure `request_object_audience` on the authenticator to require a specific `aud` value in the signed request object.

## Refresh Tokens

Refresh tokens can be described as "token granting token", which means they are sent to the OIDC server to obtain new ones.

Enable by setting the `enable_refresh_token` configuration parameter to `true`.

Optionally configure the `refresh_token_ttl` configuration parameter.

Default value is `1800000` milliseconds. This will enable refresh tokens valid for 30 minutes.

{% hint style="info" %}
The OIDC module must have parameter `use_refresh_token` set to `true`.
{% endhint %}

#### Persisting refresh tokens

There is an option to send the newly created refresh token to a pipe which can be used to store the refresh token without keeping it in memory.

Enable the pipe by configuring the `refresh_token_persist_pipe_id` configuration parameter on the **relying party** section.

#### Loading refresh tokens

Refresh tokens are used with the token endpoint by setting the `grant_type` request parameter to `refresh_token`. If the refresh token was persisted in a pipe, use the `refresh_token_resolve_pipe_id` configuration parameter on the **relying party** section to resolve/load the refresh token, create a new refresh token and persist that new refresh token.

The pipe must return a new `refresh_token` (string), `access_token` (string), `id_token` (string) and `expires_in` (long) in pipe response. All controls are done in the pipe.

## Using the client\_credentials grant

When using the token endpoint with the client\_credentials grant (M2M), a couple of things need to be in place:

* In the discovery\_meta section on the OIDC module add client\_credentials:

"grant\_types\_supported": \[ "authorization\_code", "refresh\_token", "client\_credentials" ],

```
"grant_types_supported": [ 
    "authorization_code", 
    "refresh_token", 
    "client_credentials"
],
```

* In the Relying Party configuration add:

```
"client_credentials_pipe_id": "client_credentials_pipe",
```

* Add corresponding "client\_credentials\_pipe":

```json
// Example pipe for JWT access token with ttl of 60 minutes
{
    "id": "client_credentials_pipe",
    "config": {
        "valves": [
            {
                "name": "CreateJwt",
                "enabled": true,
                "config": {
                    "dest": "access_token",
                    "jwt_ttl": 60,
                    "jwt_headers": {
                        "test_header": "test_header",
                        "typ": "at+jwt"
                    },
                    "jwt_claims": {
                        "test_claim": "test_claim",
                        "sub": "Anna_Auth"
                    },
                    "keystore": {
                        "path": "/fortified_test/config/keystore.p12",
                        "password": "supersecret",
                        "type": "PKCS12"
                    },
                    "keystore_password": "supersecret",
                    "keystore_alias": "jwt"
                }
            },
            {
                "name": "JsonObjectCreate",
                "enabled": true,
                "config": {
                    "src": {
                        "access_token": "${item.access_token}",
                        "token_type": "Bearer",
                        "expires_in": 3600,
                        "scope": "read"
                    },
                    "dest": "client_credentials"
                }
            }
        ]
    }
}
```

```json
// Example pipe for opaque access token with ttl of 60 minutes
{
    "id": "client_credentials_pipe",
    "config": {
        "valves": [
            {
                "name": "CreateItem",
                "enabled": true,
                "config": {
                    "id": "temp",
                    "properties": {}
                }
            },
            {
                "name": "JsonObjectCreate",
                "enabled": true,
                "config": {
                    "src": {
                        "access_token": "",
                        "token_type": "Bearer",
                        "expires_in": 3600,
                        "scope": "read"
                    },
                    "dest": "client_credentials"
                }
            }
        ]
    }
}
```

## Logging

On a successful authentication, an event is logged containing the following:

* WEB\_100101
* IDENTIFIER (user traceid)
* DESTINATION\_SERVICE\_NAME (redirect URI)
* SOURCE\_ADDRESS (user IP address)

## Deep TRACE logging

For difficult troubleshooting of OIDC OP authorization flows, this authenticator can participate in the deep OIDC TRACE logging used by the OIDC module.

This is intended for short-lived diagnostics only. It logs raw OIDC values and does **not** redact tokens, authorization codes, client secrets, cookies, or other credentials.

{% hint style="warning" %}
Enable this only during active troubleshooting and remove it again afterwards.
{% endhint %}

### What is covered

When enabled together with the OIDC module trace logger, this authenticator adds detailed trace logging for:

* incoming authorization requests
* redirects from the authorization endpoint
* authorization code completion
* logout-related redirects and responses

### How to enable it

Both of the following must be true:

1. The dedicated logger category must be set to `TRACE`
2. This authenticator instance must have `verbose: true`

Example Log4j configuration:

```xml
<AsyncLogger name="fortifiedid.oidc.trace" level="TRACE" />
```

Example Authorization Code Flow configuration:

```json
{
  "id": "auth00",
  "type": "OIDCAuthCodeFlow",
  "config": {
    "verbose": true,
    "base_path": "/test/authn",
    "chain": [
      {
        "id": "select",
        "required": true
      }
    ]
  }
}
```

### Full OIDC OP trace

For a complete OIDC OP trace you normally enable this together with `verbose: true` on the [OIDC module](/access/key-components/modules/oidc/oidc-module.md), since the module covers the back-channel endpoints while this authenticator covers the authorization endpoint and logout flow.

## Data sent to PIPE

All data put into the shared authentication state along with the HTTP headers are exposed and sent into the pipe.

Data put into the state by this authenticator is:

OIDC request data

## Expected data from PIPE

In order to use data from PIPE the response must contain one item. All data from that item will be available under the exports scope ( `${exports.mail}` ) during expansion, when creating the ID token and access token.

## Available data for ID\_token and Access Token claims

Data is extracted with the help of expansions.

The following scopes are available:

<table data-full-width="true"><thead><tr><th>Scope</th><th>Description</th><th>Example</th></tr></thead><tbody><tr><td><code>request</code></td><td>The current authentication request including HTTP <code>headers</code> and <code>params</code></td><td><code>${request.client_id}</code></td></tr><tr><td><code>session</code></td><td>The current session.</td><td><code>${session.sub}</code></td></tr><tr><td><code>exports</code></td><td>Exports data. Note: All pipe data is available under the exports scope.</td><td><code>${exports.sub}</code></td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.fortifiedid.se/access/key-components/authenticators/protocol-managers/oidc/authorization-code-flow.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
