# Implementation Guide

This guide walks you through implementing the complete OIDC4IA authentication flow with DIP.

## Step 1: Create the PAR Request

### 1.1 Generate PKCE Values

Generate a `code_verifier` (43-128 character random string) and compute the `code_challenge`:

```javascript
// Generate code_verifier (43-128 characters)
const codeVerifier = generateRandomString(43);

// Compute code_challenge = BASE64URL(SHA256(code_verifier))
const codeChallenge = base64url(sha256(codeVerifier));
```

### 1.2 Generate State and Nonce

```javascript
const state = generateRandomString(32);
const nonce = generateRandomString(32);
```

Store these values in your session for later verification.

### 1.3 Build the Client Assertion JWT

The client assertion authenticates your client. See [Authentication](https://docs.stoe.no/id-check/getting-started/authentication.md) for full details.

**Header:**

```json
{
  "alg": "ES256",
  "kid": "your-key-id",
  "typ": "JWT"
}
```

**Payload:**

```json
{
  "iss": "your_client_id",
  "sub": "your_client_id",
  "aud": "https://{dip-base-url}",
  "exp": 1759835872
}
```

### 1.4 Build the Request Object JWT

The request object contains the authorization parameters, including the OIDC4IA `claims` parameter that specifies which verified identity data you need.

**Header:**

```json
{
  "alg": "ES256",
  "kid": "your-key-id",
  "typ": "JWT"
}
```

**Payload:**

```json
{
  "iss": "your_client_id",
  "sub": "your_client_id",
  "aud": "https://{dip-base-url}",
  "exp": 1759835872,
  "client_id": "your_client_id",
  "response_type": "code",
  "redirect_uri": "https://your-app.com/callback",
  "scope": "openid",
  "state": "your-state-value",
  "nonce": "your-nonce-value",
  "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
  "code_challenge_method": "S256",
  "claims": { ... }
}
```

#### Understanding the OIDC4IA Claims Request

The `claims` parameter follows the [OpenID Connect for Identity Assurance (OIDC4IA) specification](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html). It allows you to request specific verified identity claims and document evidence.

> **Request only what you need**
>
> Only include the claims and document details that your application requires. This follows the principle of data minimization - you should not request more personal data than necessary for your use case.

The claims request has two main parts:

1. **`verification.evidence`** - Document details you want (e.g., document type, expiry date, issuer)
2. **`claims`** - Identity claims you want (e.g., name, birthdate, nationality)

#### Minimal Example

If you only need the user's given name, family name, and birthdate (no document details):

```json
{
  "claims": {
    "id_token": {
      "verified_claims": {
        "verification": {
          "trust_framework": { "value": "stoe" }
        },
        "claims": {
          "given_name": { "essential": true },
          "family_name": { "essential": true },
          "birthdate": { "essential": true }
        }
      }
    }
  }
}
```

> **Note**
>
> The `evidence` array is optional. Omit it entirely if you only need identity claims and don't need document details (type, expiry date, issuer, etc.).

#### Full Example

If you need identity claims plus document verification details:

```json
{
  "claims": {
    "id_token": {
      "verified_claims": {
        "verification": {
          "trust_framework": { "value": "stoe" },
          "evidence": [
            {
              "type": { "value": "document" },
              "document_details": {
                "type": null,
                "document_number": null,
                "date_of_expiry": null,
                "issuer": null,
                "personal_number": null
              }
            }
          ]
        },
        "claims": {
          "given_name": { "essential": true },
          "family_name": { "essential": true },
          "birthdate": { "essential": true },
          "nationalities": { "essential": true }
        }
      }
    }
  }
}
```

> **Note**
>
> Fields set to `null` in `document_details` indicate you want that data returned. Omit fields entirely if you don't need them.

For the complete list of available claims and document details, see the [PAR Request](https://docs.stoe.no/id-check/api/par.md) documentation.

### 1.5 Send the PAR Request

```http
POST /par HTTP/1.1
Host: {dip-base-url}
Content-Type: application/x-www-form-urlencoded

client_id=your_client_id
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6...
&request=eyJhbGciOiJFUzI1NiIsImtpZCI6...
```

### 1.6 PAR Response

```json
{
  "request_uri": "urn:ietf:params:oauth:request_uri:550e8400-e29b-41d4-a716-446655440000",
  "expires_in": 60
}
```

## Step 2: Redirect User to Authorization Endpoint

Redirect the user to the authorization endpoint with the `request_uri`:

```
GET https://{dip-base-url}/auth
  ?client_id=your_client_id
  &request_uri=urn:ietf:params:oauth:request_uri:550e8400-e29b-41d4-a716-446655440000
```

The user is then handed off to the **BankID app** where they complete identity verification (document scanning and facial recognition).

## Step 3: Handle the Callback

After verification, the **BankID app** redirects the user back to your `redirect_uri` with an authorization code:

```
GET https://your-app.com/callback
  ?code=SplxlOBeZQQYbYS6WxSbIA
  &state=your-state-value
  &iss=https%3A%2F%2F{dip-base-url}
```

> **Warning**
>
> Always verify that the `state` matches what you stored in Step 1.2 to prevent CSRF attacks.

> **Warning**
>
> Always verify that the `iss` parameter matches the expected DIP issuer URL (RFC 9207). This prevents mix-up attacks where an attacker could redirect authorization responses from a different authorization server.

## Step 4: Exchange Code for Tokens

### 4.1 Build a New Client Assertion

Create a fresh client assertion JWT (same format as Step 1.3).

### 4.2 Send the Token Request

```http
POST /token HTTP/1.1
Host: {dip-base-url}
Content-Type: application/x-www-form-urlencoded

client_id=your_client_id
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6...
&grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&redirect_uri=https://your-app.com/callback
```

### 4.3 Token Response

```json
{
  "id_token": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0...",
  "access_token": "not_applicable",
  "token_type": "Bearer",
  "expires_in": 900
}
```

## Step 5: Decrypt and Validate the ID Token

The `id_token` is a JWE (JSON Web Encryption) containing a signed JWT.

### 5.1 Decrypt the JWE

Use your private key to decrypt the JWE and extract the signed JWT.

### 5.2 Verify the JWT Signature

Fetch the DIP public keys from the [JWKS endpoint](https://docs.stoe.no/id-check/api/jwks.md) and verify the signature.

### 5.3 Validate Claims

Verify the following claims:

| Claim   | Validation                         |
| ------- | ---------------------------------- |
| `iss`   | Must match the DIP issuer URL      |
| `aud`   | Must match your `client_id`        |
| `exp`   | Must not be expired                |
| `iat`   | Must not be in the future          |
| `nonce` | Must match the nonce from Step 1.2 |

### 5.4 Extract Verified Claims

The user's verified identity data is in the `verified_claims` object. See [ID Token](https://docs.stoe.no/id-check/id-token.md) for the complete structure.

**Example ID Token payload:**

```json
{
  "iss": "https://{dip-base-url}",
  "sub": "hashed-subject-id",
  "aud": "your_client_id",
  "exp": 1759835872,
  "iat": 1759832272,
  "auth_time": 1759832272,
  "nonce": "your-nonce-value",
  "acr": "urn:bankid:idcheck",
  "amr": ["face", "user"],
  "verified_claims": {
    "verification": {
      "trust_framework": "stoe",
      "evidence": [
        {
          "type": "document",
          "check_details": [
            {
              "check_method": "vcrypt",
              "result": "success"
            },
            {
              "check_method": "data",
              "organization": "id.politiet.no",
              "result": "valid"
            }
          ],
          "document_details": {
            "type": "passport",
            "document_number": "AB1234567",
            "date_of_issuance": "2020-01-15",
            "date_of_expiry": "2030-01-15",
            "issuer": {
              "country_code": "NOR",
              "name": "Politiet"
            },
            "personal_number": "12345678901"
          }
        }
      ]
    },
    "claims": {
      "given_name": "AASAMUND",
      "family_name": "OESTENBYEN",
      "picture": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
      "birthdate": "1990-01-15",
      "gender": "male",
      "nationalities": ["NOR"]
    }
  }
}
```

> **Note**
>
> Any requested evidence or claims that are not available in the ID check data will be omitted from the response. The response only contains data that was successfully retrieved.

## Complete Flow Summary

```mermaid
sequenceDiagram
    participant Client
    participant DIP
    participant BankID as BankID App
    participant User
    
    Client->>DIP: 1. POST /par
    DIP-->>Client: 2. request_uri
    Client->>BankID: 3. Redirect to /auth
    User->>BankID: 4. Complete verification
    BankID->>DIP: 5. Request auth code
    DIP-->>BankID: 6. Return auth code
    BankID->>Client: 7. Redirect with code
    Client->>DIP: 8. POST /token
    DIP-->>Client: 9. Encrypted ID token
    Client->>Client: 10. Decrypt and validate
```

## Next Steps

- [Authentication](https://docs.stoe.no/id-check/getting-started/authentication.md) - Detailed JWT requirements
- [PAR Request](https://docs.stoe.no/id-check/api/par.md) - Full PAR documentation
- [Token Request](https://docs.stoe.no/id-check/api/token.md) - Full token endpoint documentation
- [ID Token](https://docs.stoe.no/id-check/id-token.md) - Complete ID token structure
