# Prerequisites

The following values are required for both the Onboarding and Authentication flows. You will need them from your Incode dashboard and OIDC configuration.

## Shared Values

| Value             | Description                                                                                                      |
|-------------------|------------------------------------------------------------------------------------------------------------------|
| `AUTH_SERVER_URL` | Base URL of the OIDC authorization server (`https://auth.demo.incode.com`). Also used as the issuer value for ID token validation. |
| `SERVER_URL`      | Base URL of the Incode API server                                                                                |
| `REDIRECT_URI`    | The redirect URI registered with your OIDC client                                                               |
| `API_KEY`         | Your Incode API key                                                                                             |
| `ADMIN_EMAIL`     | Admin account email for the Incode Admin API                                                                    |
| `ADMIN_PASSWORD`  | Admin account password for the Incode Admin API                                                                 |

## Onboarding Client Credentials

| Value           | Description                                          |
|-----------------|------------------------------------------------------|
| `CLIENT_ID`    | Your registered OIDC onboarding client ID           |
| `CLIENT_SECRET`| Your registered OIDC onboarding client secret       |

## Authentication Client Credentials

| Value                | Description                                          |
|----------------------|------------------------------------------------------|
| `AUTH_CLIENT_ID`     | Your registered OIDC authentication client ID        |
| `AUTH_CLIENT_SECRET` | Your registered OIDC authentication client secret    |

### **Keep credentials secure**  
Never expose `CLIENT_SECRET`, `AUTH_CLIENT_SECRET`, `ADMIN_EMAIL`, `ADMIN_PASSWORD`, or `API_KEY` in client-side code. All requests using these values must be made from your server.

# ID Token Validation  
After receiving an ID token from the `/oauth2/token` endpoint, your application is responsible for validating it before trusting any of its claims. Use a well-maintained JWT library rather than implementing validation manually.

## Recommended Libraries

| Language               | Library                      |
|------------------------|------------------------------|
| JavaScript / Node.js   | `jose`, `jsonwebtoken`       |
| Python                 | `python-jose`, `authlib`    |
| Java                   | `nimbus-jose-jwt`           |
| Go                     | `golang-jwt/jwt`            |
| Ruby                   | `jwt`                        |

## Claims to Validate

| Claim  | Expected Value                                                  | Why                                                          |
|--------|-----------------------------------------------------------------|-------------------------------------------------------------|
| `iss`  | Must exactly match `https://auth.demo.incode.com`             | Confirms the token was issued by the authorization server you trust |
| `exp`  | Must be in the future (compare against server time)           | Rejects expired tokens                                      |
| `nonce`| Must match the `nonce` sent during the session                 | Confirms the token was issued in response to your specific request — protects against replay attacks |
| `azp`  | Must equal your `CLIENT_ID` when multiple audiences are present | Confirms your app is the intended recipient of the token    |
| `kid`  | Must be present in the JWKS at `https://auth.demo.incode.com/oauth2/jwks`  | Used to verify the token's cryptographic signature         |

## Verifying the Token Signature (JWKS)
The `kid` value in the token header identifies which public key to use for signature verification. Fetch Incode's public keys and find the matching key:

```undefined
GET https://auth.demo.incode.com/oauth2/jwks
```

**Example (JavaScript using `jose`)**

```javascript
import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://auth.demo.incode.com/oauth2/jwks')
);

const { payload } = await jwtVerify(idToken, JWKS, {
  issuer:   'https://auth.demo.incode.com',
  audience: CLIENT_ID, // Use AUTH_CLIENT_ID for the Authentication flow
});

// Validate nonce manually
if (payload.nonce !== storedNonce) {
  throw new Error('Nonce mismatch — possible replay attack');
}
```

# Create Admin Token  
**Endpoint:**`POST {SERVER_URL}/executive/log-in`

Before retrieving verification results, your server must authenticate with the Incode Admin API to obtain a short-lived, high-privilege admin token.

**Request**

```undefined
POST /executive/log-in
Content-Type: application/json
api-version: 1.0
x-api-key: YOUR_API_KEY
```

JSON

```json
{
  "email": "YOUR_ADMIN_EMAIL",
  "password": "YOUR_ADMIN_PASSWORD"
}
```

**Example (JavaScript)**

```javascript
const response = await fetch(`${SERVER_URL}/executive/log-in`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept':       'application/json',
    'api-version':  '1.0',
    'x-api-key':    API_KEY,
  },
  body: JSON.stringify({
    email:    ADMIN_EMAIL,
    password: ADMIN_PASSWORD,
  }),
});

const { token: adminToken } = await response.json();
```

**Response**

JSON

```json
{
  "token": "<admin-token>"
}
```

| Field | Description                                           |
|-------|-------------------------------------------------------|
| `token` | Short-lived admin token required for score and OCR data retrieval |

# OIDC Onboarding Demo  
## Overview  
The OIDC Onboarding flow verifies a new user's identity by combining OpenID Connect (OIDC) with Incode's biometric verification pipeline. Once the user completes the onboarding session on the frontend, your server exchanges the resulting authorization code for tokens and retrieves the full verification and document data.

**High-level flow:**

```undefined
Your Server           Auth Server (OIDC)            Incode API
  |                         |                            |
  |-- 1. POST /token ------>|                            |
  |<-- access + id token ---|                            |
  |-- 2. GET /userinfo ---->|                            |
  |<-- interview_id --------|                            |
  |-- 3. POST /executive/log-in ----------------------->|
  |<-- adminToken ----------------------------------------|
  |-- 4. GET /omni/get/score -------------------------->|
  |<-- verification score ------------------------------|
  |-- 5. GET /omni/get/ocr-data ----------------------->|
  |<-- extracted document data -------------------------|
```

## Step 1: Exchange the Authorization Code for Tokens
**Endpoint:**`POST {AUTH_SERVER_URL}/oauth2/token`

Once the user has completed the onboarding session on the frontend, your server receives an authorization code. Exchange it for an access token and ID token using your `CLIENT_SECRET`.

**Request**

```undefined
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
```

| Parameter      | Value                                           |
|----------------|--------------------------------------------------|
| `grant_type`   | `authorization_code`                             |
| `client_id`    | `YOUR_CLIENT_ID`                                |
| `client_secret`| `YOUR_CLIENT_SECRET`                            |
| `redirect_uri` | `YOUR_REDIRECT_URI`                             |
| `code`         | The authorization code received from the onboarding session |

**Example (JavaScript)**

```javascript
const response = await fetch(`${AUTH_SERVER_URL}/oauth2/token`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type:    'authorization_code',
    client_id:     CLIENT_ID,
    client_secret: CLIENT_SECRET,
    redirect_uri:  REDIRECT_URI,
    code:          authorizationCode,
  }),
});

const tokens = await response.json();
```

**Response**

JSON

```json
{
  "access_token": "<JWT>",
  "id_token": "<JWT>",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

## Step 2: Retrieve the Interview ID
**Endpoint:**`GET {AUTH_SERVER_URL}/userinfo`

Once your application has a valid access token, call the OIDC UserInfo endpoint to retrieve the `interview_id` for the onboarding session.

**Request**

```undefined
GET /userinfo
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**Example (JavaScript)**

```javascript
const response = await fetch(`${AUTH_SERVER_URL}/userinfo`, {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
  },
});

const userInfo = await response.json();
const interviewId = userInfo.interview_id;
```

**Response (example)**

JSON

```json
{
  "interview_id": "667ce6cd1f9dce127eabe35e",
  "email": "user@example.com"
}
```

## Step 3: Create an Admin Token  
Follow the guide to obtain the admin token required for Steps 4 and 5.

## Step 4: Retrieve the Verification Score
**Endpoint:**`GET {SERVER_URL}/omni/get/score`

**Request**

```undefined
GET /omni/get/score?id=YOUR_INTERVIEW_ID
Content-Type: application/json
api-version: 1.0
x-api-key: YOUR_API_KEY
X-Incode-Hardware-Id: YOUR_ADMIN_TOKEN
```

**Example (JavaScript)**

```javascript
const response = await fetch(
  `${SERVER_URL}/omni/get/score?id=${interviewId}`,
  {
    method: 'GET',
    headers: {
      'Content-Type':         'application/json',
      'api-version':          '1.0',
      'x-api-key':            API_KEY,
      'X-Incode-Hardware-Id': adminToken,
    },
  }
);

const score = await response.json();
```

**Response**

| Section              | Description                                               |
|----------------------|-----------------------------------------------------------|
| `overall`            | Composite score and pass/fail status across all checks   |
| `liveness`           | Liveness detection results                                 |
| `faceRecognition`    | Selfie-to-ID face match results                           |
| `deepsight`          | Multimodal fraud intelligence signals                      |
| `idOcrConfidence`    | Confidence scores for extracted text fields               |
| `governmentValidation`| Government database cross-check results                   |
| `watchlistScore`     | Watchlist screening results                                 |
| `antifraud`          | Aggregate fraud risk assessment                           |
| `deviceRisk`         | Device trust signals                                     |
| `behavioralRisk`     | Behavioral trust signals                                   |
| `reasonMsg`          | Human-readable explanation of the overall outcome         |

## Step 5: Retrieve OCR Data
**Endpoint:**`GET {SERVER_URL}/omni/get/ocr-data`

**Request**

```undefined
GET /omni/get/ocr-data?id=YOUR_INTERVIEW_ID
Content-Type: application/json
api-version: 1.0
x-api-key: YOUR_API_KEY
X-Incode-Hardware-Id: YOUR_ADMIN_TOKEN
```

**Example (JavaScript)**

```javascript
const response = await fetch(
  `${SERVER_URL}/omni/get/ocr-data?id=${interviewId}`,
  {
    method: 'GET',
    headers: {
      'Content-Type':         'application/json',
      'api-version':          '1.0',
      'x-api-key':            API_KEY,
      'X-Incode-Hardware-Id': adminToken,
    },
  }
);

const ocrData = await response.json();
```

A successful response returns a `200 OK` containing data extracted from the user's identity document and address statement, including fields such as name, date of birth, address, document number, and expiration date.

# Authentication
## Overview
The OIDC Authentication flow allows a previously onboarded user to authenticate using face recognition. This is a **1:N flow** — the authorization server matches the user's face against the full enrolled population without requiring a known user identifier upfront.

**High-level flow:**

```undefined
Your Server           Auth Server (OIDC)            Incode API
  |                         |                            |
  |-- 1. POST /token ------>|                            |
  |<-- access + id token ---|                            |
  |-- 2. GET /userinfo ---->|                            |
  |<-- interview_id --------|                            |
  |-- 3. POST /executive/log-in ----------------------->|
  |<-- adminToken ----------------------------------------|
  |-- 4. GET /omni/get/score -------------------------->|
  |<-- verification score ------------------------------|
```

## Step 1: Exchange the Authorization Code for Tokens
**Endpoint:**`POST {AUTH_SERVER_URL}/oauth2/token`

Once the user has completed the face authentication session on the frontend, your server receives an authorization code. Exchange it for an access token and ID token using your `AUTH_CLIENT_SECRET`.

**Request**

```undefined
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
```

| Parameter         | Value                                           |
|-------------------|--------------------------------------------------|
| `grant_type`      | `authorization_code`                             |
| `client_id`       | `YOUR_AUTH_CLIENT_ID`                            |
| `client_secret`   | `YOUR_AUTH_CLIENT_SECRET`                        |
| `redirect_uri`    | `YOUR_REDIRECT_URI`                             |
| `code`            | The authorization code received from the authentication session |

**Example (JavaScript)**

```javascript
const response = await fetch(`${AUTH_SERVER_URL}/oauth2/token`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type:    'authorization_code',
    client_id:     AUTH_CLIENT_ID,
    client_secret: AUTH_CLIENT_SECRET,
    redirect_uri:  REDIRECT_URI,
    code:          authorizationCode,
  }),
});

const tokens = await response.json();
```

**Response**

JSON

```json
{
  "access_token": "<JWT>",
  "id_token": "<JWT>",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

## Step 2: Retrieve the Interview ID
**Endpoint:**`GET {AUTH_SERVER_URL}/userinfo`

Once your application has a valid access token, call the OIDC UserInfo endpoint to retrieve the `interview_id` for the authentication session.

**Request**

```undefined
GET /userinfo
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**Example (JavaScript)**

```javascript
const response = await fetch(`${AUTH_SERVER_URL}/userinfo`, {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
  },
});

const userInfo = await response.json();
const interviewId = userInfo.interview_id;
```

**Response (example)**

JSON

```json
{
  "interview_id": "667ce6cd1f9dce127eabe35e",
  "email": "user@example.com"
}
```

## Step 3: Create an Admin Token
Follow the guide to obtain the admin token required for Step 4.

## Step 4: Retrieve the Verification Score
**Endpoint:**`GET {SERVER_URL}/omni/get/score`

**Request**

**Example (JavaScript)**

const score = await response.json();
```

**Response**

| Section              | Description                                               |
|----------------------|-----------------------------------------------------------|
| `overall`            | Composite score and pass/fail status across all checks   |
| `liveness`           | Liveness detection results                                 |
| `faceRecognition`    | Face match results against the enrolled user              |
| `deepsight`          | Multimodal fraud intelligence signals                      |
| `antifraud`          | Aggregate fraud risk assessment                           |
| `deviceRisk`         | Device trust signals                                     |
| `behavioralRisk`     | Behavioral trust signals                                   |
| `reasonMsg`          | Human-readable explanation of the overall outcome         |
