This feature is a configurable setting in [Workflows](https://developer.incode.com/docs/workflows-20) and [Flows](https://developer.incode.com/docs/flows-1). It is designed to mitigate security risks associated with webflows that rely on redirection.

This functionality enforces execution of the flow or workflow as part of an OAuth 2.0 / OpenID Connect (OIDC) authorization flow. In this mode, the onboarding process is executed during the OIDC authorization request and serves as the authentication mechanism, after which the authorization server issues an authorization code upon successful completion.

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

- A dedicated OIDC client is automatically generated. This OAuth client id is visible in the Workflow/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.
  - Any future change to this URL must be updated on the Workflow/Flow configuration and all client authorize and token requests. Mismatches result in authorization failures.
- Disabling and re-enabling the flag regenerates the OAuth client and invalidates the previous client.

When a Workflow or Flow is configured for OAuth security, the old incodesmile URL scheme does not work for that flowid. Customers must move to new URL scheme and authorization pattern described in this document. This ensures there are no security gaps with 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 generated verifier known only to the client.

## OpenID Connect (OIDC) Authorization Flow

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

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

### **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` as a transformation of the `code_verifier` using Secure Hash Algorithm 256-bit (SHA-256)
3. Generate `state` parameter that is used for CSRF protection.
   - cryptographically random value
   - High entropy (random, unguessable)
   - Per authorization request

See more [here](https://datatracker.ietf.org/doc/html/rfc6749#section-10.12).

Store state in memory for SPA applications or securely in session cookie or in-memory cache (keyed by session) in BFF

4. Generate `nonce` parameter that is used to mitigate id replay attacks.
   - High-entropy
   - Unique per authentication request
   - Same quality as state

See more [here](https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes). Store nonce in memory for SPA applications or securely in session cookie or in-memory cache (keyed by session) in a Backends for Frontends (BFF) layer.

Well known endpoint for automatic discovery of configuration, endpoints, and key sets can be found 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` | Id that identifies user in clients external system - optional |

#### Example Authorization Request

Request

```javascript
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}
```

A breakdown of what this request does:

- The user is redirected to the Incode Authorization Server.
- The associated Workflow/Flow is executed as part of the `/authorize` endpoint
- On successful onboarding completion:
  - The user is redirected back to `redirect_uri`
  - An **authorization code** and **state** are returned in url
  - Client needs to verify state parameter on callback
    - Compare returned state with stored value. If missing or mismatched → abort process
- On failed onboarding completion:
  - The user is redirected back to `redirect_uri`
  - Error and error description url parameters are included ([oidc 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 `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

Response

```text
{
"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 include:

- The Issuer Identifier must exactly match the value of the `iss` (issuer) Claim.
- The `aud` (audience) Claim contains its `client_id` value
- The JWS signature is verified using the algorithm indicated in the JWT header and the corresponding public key obtained from the Issuer’s well-known **JWKS** endpoint.
- The current time must be before the time represented by the `exp` Claim.
- The `nonce` claim must exactly match the `nonce` value sent by the Client in the authentication request.

Validation of the access token is responsibility of the omni server, and it is done is [Step 3](https://developer.incode.com/docs/oauth2-secured-sessions#step-3-api-access).

### Step 3: API Access

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

- Is required for accessing Omni APIs
- Is bound to a single session
- Adheres to Standard OAuth 2.0 token validation rules

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

curl request

```text
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'
```

Endpoints we can call with this token:

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