# OAuth2 Secured Sessions

OAuth2 Secured Sessions is a configurable setting in [Workflows](https://developer.incode.com/docs/workflows-20) and [Flows](https://developer.incode.com/docs/flows-1) that mitigates security risks associated with redirect-based webflows.

When enabled, the Workflow or Flow runs as part of an OAuth 2.0 / OpenID Connect (OIDC) authorization flow. The onboarding process itself serves as the authentication step. After the user completes onboarding successfully, the authorization server issues an authorization code.

When the **OAuth2 Secured** setting is enabled on a Workflow or Flow:

- A dedicated OIDC client is automatically generated. This OAuth client ID is visible in the Workflow or Flow settings after saving. The client is configured with:
  - Authorization Code grant
  - Proof Key for Code Exchange (PKCE) S256
- The Workflow or Flow execution is part of an OIDC authentication process
- A redirect URL becomes mandatory.
  - If you change this URL later, you must update it in the Workflow or Flow configuration and in all client authorize and token requests. Mismatches cause authorization failures.
- Disabling and re-enabling the setting regenerates the OAuth client and invalidates the previous one.

When a Workflow or Flow uses OAuth security, the legacy incodesmile.com URL scheme no longer works for that Flow ID. You must use the new URL scheme and authorization pattern described below. This prevents security gaps caused by manual URL tampering.

## Security Model

The generated OAuth client uses Proof Key for Code Exchange (PKCE) ([RFC 7636](https://www.rfc-editor.org/rfc/rfc7636.txt)) and does not require a client secret. This makes the secured session flow suitable for Single Page Applications (SPAs), mobile applications, or any client unable to securely store secrets.

PKCE protects the authorization code from interception by binding it to a cryptographically random verifier known only to the client.

## OpenID Connect (OIDC) Authorization Flow

The following diagram shows the sequence of a successful onboarding using OIDC:

![Sequence diagram showing a successful OIDC onboarding flow between client, authorization server, and Omni API](https://files.readme.io/e7d3e976c6421299b6311445123f668b5fed0d5b2a56b0d06e7f3a6d32413019-image.png)

The following diagram shows the sequence of a failed onboarding using OIDC:

![Sequence diagram showing a failed OIDC onboarding flow with error response](https://files.readme.io/c6bf5d5b8e5c12ff1d270d2d99e0851a6258b35880895621692ecfa80b0a0c67-image.png)

### Step 1: Authorization Request (`/oauth2/authorize`)

Before initiating the authorization request, the client must:

1. Generate a cryptographically random `code_verifier` and store it

2. Compute the `code_challenge` by hashing the `code_verifier` with SHA-256

3. Generate a `state` parameter that is used for CSRF protection. The value should be:

   - Cryptographically random
   - High entropy (unguessable)
   - Unique per authorization request

   Store `state` in memory for SPAs, or in a secure session cookie or session-keyed in-memory cache for a Backend for Frontend (BFF) layer. For more detail, see [RFC 6749 §10.12](https://datatracker.ietf.org/doc/html/rfc6749#section-10.12).

4. Generate a `nonce` parameter to prevent ID token replay attacks. The value should be:&#x20;
   - High-entropy
   - Unique per authentication request
   - Same quality as state

     Store `nonce` the same way you store `state`. For more detail, see the [OIDC nonce notes](https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes).

The well-known endpoint for automatic discovery of configuration, endpoints, and key sets is at [https://auth.incode.com/.well-known/openid-configuration](https://auth.incode.com/.well-known/openid-configuration).

#### Authorization Request Parameters

| Parameter                              | Description                                                                             |
| :------------------------------------- | :-------------------------------------------------------------------------------------- |
| `client_id`                            | OAuth Client ID generated and stored in the Workflow/Flow settings.                     |
| `redirect_uri`                         | Redirect URI configured in the Workflow/Flow settings.                                  |
| `response_type`                        | `code`                                                                                  |
| `scope`                                | `openid`                                                                                |
| `state`                                | Opaque value used to maintain request/response integrity                                |
| `nonce`                                | Value used to associate the ID Token with the client session and prevent replay attacks |
| `code_challenge_method`                | `S256`                                                                                  |
| `code_challenge`                       | Generated PKCE challenge                                                                |
| `response_mode`                        | `form_post`                                                                             |
| `external_customer_id`<br />_Optional_ | ID that identifies user in clients external system                                      |

Example Authorization Request

```text Request
https://auth.incode.com/oauth2/authorize
?client_id={client_id}
&redirect_uri={redirect_uri}
&scope=openid
&response_type=code
&response_mode=form_post
&state={state}
&nonce={nonce}
&code_challenge_method=S256
&code_challenge={codeChallenge}
&external_customer_id={external_customer_id}
```

Here's what this request does:

- The user is redirected to the Incode authorization server.
- The associated Workflow or Flow runs as part of the `/authorize` endpoint.
- If onboarding succeeds:
  - The user is redirected back to `redirect_uri`.
  - An authorization code and `state` are returned in the URL.
  - The client must verify the `state` parameter on callback by comparing the returned value to the stored one. If it's missing or doesn't match, abort the process.
- If onboarding fails:
  - The user is redirected back to `redirect_uri`.
  - Error and error description parameters are included in the URL (see the [OIDC error spec](https://openid.net/specs/openid-connect-core-1_0.html#AuthError)).

### Step 2: Token Exchange (`/oauth2/token`)

The client exchanges the authorization code for tokens. Client authenticates using the `code_verifier` generated in [Step 1](https://developer.incode.com/docs/oauth2-secured-sessions#step-1-authorization-request-oauth2authorize).

#### Token Request Parameters

| Parameter       | Description                                                                                                                              |
| :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
| `grant_type`    | `authorization_code`                                                                                                                     |
| `code`          | Authorization code from [Step 1](https://developer.incode.com/docs/oauth2-secured-sessions#step-1-authorization-request-oauth2authorize) |
| `client_id`     | Client ID used in the authorize request                                                                                                  |
| `redirect_uri`  | Redirect URI used in the authorize request                                                                                               |
| `code_verifier` | Original PKCE code verifier                                                                                                              |

#### Example Token Response Body

```json Response
{
  "access_token": "eyJraWQiOiI2MzM2NjAy....zAZ4-FboQg",
  "scope": "openid profile",
  "id_token": "eyJraWQiOiI2MzM2NjAyYy05....O3dDfO13Yyg",
  "token_type": "Bearer",
  "expires_in": 86399
}
```

After a successful response, client must validate ID token. Validation must confirm that:

- The issuer identifier exactly matches the `iss` claim.
- The `aud` claim contains the client's `client_id` value.
- The JWS signature is valid, using the algorithm in the JWT header and the public key from the issuer's well-known JWKS endpoint.
- The current time is before the `exp` claim.
- The `nonce` claim exactly matches the `nonce` value the client sent in the authentication request.

Access token validation is the responsibility of the Omni server, and happens in [Step 3](https://developer.incode.com/docs/oauth2-secured-sessions#step-3-api-access).

### Step 3: API Access

Use the `access_token` from [Step 2](https://developer.incode.com/docs/oauth2-secured-sessions#step-2-token-exchange-oauth2token) as a Bearer token in the `Authorization` header instead of the `x-incode-hardware-id` header. The token:

- Is required to access Omni APIs
- Is bound to a single session
- Follows standard OAuth 2.0 token validation rules

#### Example curl request with OAuth Secured Workflows/Flows

```curl curl request
curl --location 'https://saas-api.incodesmile.com/omni/get/score' \
--header 'Content-Type: application/json' \
--header 'api-version: 1.0' \
--header 'x-api-key: 13cf313e0db1507e77bf8d0631f3ca736173ccde' \
--header 'Authorization: Bearer eyJraWQiOiI2MzM2Nj.....Lf_N7hww'
```

You can use this token with the following endpoints:

- `omni/get/score`
- `omni/get/custom-fields`
- `omni/get/onboarding/status`
- `omni/get/ocr-data`

<br />
