# Device binding

This guide explains the device binding feature, including the key concepts and endpoints required to implement it. Device binding is mandatory for all secure flows, such as those requiring a Qualified Electronic Signature (QES) or two-factor authentication (e.g., BankIdentPlus).

## Overview

The device binding API allows customers to register their devices and create public/private key pairs for authenticating Multi-Factor Authentication (MFA) challenges.

Device binding is a prerequisite for [Strong Customer Authentication (SCA)](/guides/authentication/strong-customer-authentication/). It ensures that a request originates from a trusted, registered device.

Once a customer binds their device, they receive authentication challenges that they must confirm using that device (e.g., logging in, changing data, authorizing transactions).

### Restricted vs. Unrestricted keys

Customers authorize actions using one of two key types:

* **Unrestricted keys:** Do not require specific user authentication to sign a request. These are typically used for background processes or low-risk actions.
* **Restricted keys:** Require user authentication via biometrics (e.g., Touch ID, Face ID) to access the private key.
  * **iOS:** Combine `privateKeyUsageflag` with `userPresence` or `touchIDAny`.
  * **Android:** Use `setUserAuthenticationParameters()`.


See the [Strong customer authentication guide](/guides/authentication/strong-customer-authentication/) for the full list of use cases requiring restricted vs. unrestricted keys.

### Security requirements

Your implementation must meet the following security standards:

* **Limit:** Maximum of **five** registered devices per customer.
* **OS:** Android 8.0+ or iOS 12+.
* **Hardware:** Must support hardware-backed security via separate execution environments (e.g., Secure Enclave in iOS, TEE in Android).
* **Access Control:** Your app must enforce a device passcode or biometric lock.
* **Storage:** You must store personalized security credentials and cryptographic keys in the secure environment.


## Integration flow

Solaris supports two methods for device binding. You must choose the method that fits your use case and platform requirements:

* **SMS OTP (Default):** Solaris sends a one-time password to the customer's verified mobile number.
* **Activation Code (QR):** You generate an activation code, convert it to a QR code, and deliver it to the customer (e.g., via snail mail or a secure web interface).


### Flow 1: SMS Binding


```mermaid
sequenceDiagram
    autonumber
    participant C as Customer
    participant App as Your Frontend
    participant S as Solaris API
    
    rect rgb(249, 249, 249)
        note right of C: Phase 1: Initial Device Binding
        C->>App: Enter device details
        App->>App: Generate Unrestricted Key Pair (ECDSA-P256)
        
        App->>S: POST /mfa/devices (Public Key & Device Info)
        S-->>S: Create Unverified Device ID
        S->>C: Send SMS OTP
        S-->>App: Return Challenge ID
        
        C->>App: Enter OTP
        App->>App: Sign OTP with Private Key
        App->>S: PUT /mfa/challenges/signatures (Signature)
        
        alt Signature Valid
            S-->>App: 204 Success (Device Registered)
        else Signature Invalid
            S-->>App: 400 Error (Binding Failed)
        end
    end

    rect rgb(249, 249, 249)
        note right of C: Phase 2: Add New Keys (Optional)
        Note over App: Use existing bound device
        App->>App: Generate new Restricted/Unrestricted Key Pair
        App->>App: Sign new Public Key with EXISTING Private Key
        
        App->>S: POST /mfa/devices/{id}/keys (New Key + Signature)
        S-->>S: Verify Signature with stored Public Key
        
        alt Signature Valid
            S-->>App: 201 Created (New Key Registered)
        else Signature Invalid
            S-->>App: 400 Error (Process Failed)
        end
    end
```

### Flow 2: Activation Code (QR) Binding

In this flow, you must manage the lifecycle of the activation code before the customer can bind their device. This method allows customers to bind a new device even if they have lost access to their previously bound device.


```mermaid
sequenceDiagram
    autonumber
    participant C as Customer
    participant App as Mobile App
    participant BE as Partner Backend
    participant S as Solaris API

    rect rgb(240, 248, 255)
        note right of C: Phase 1: Activation Code Management
        BE->>S: POST /mfa/challenges/activation?invalidate_existing_code=true
        S-->>BE: Return "code" (Activation Code)
        BE->>BE: Convert "code" to QR Image
        BE->>C: Deliver QR Code (e.g., Snail Mail)
        BE->>S: POST /delivery_events (Status: DISPATCHED)
    end

    rect rgb(249, 249, 249)
        note right of C: Phase 2: Device Binding
        C->>App: Scan QR Code
        App->>App: Extract "code"
        App->>App: Generate Unrestricted Key Pair (ECDSA-P256)
        
        App->>S: POST /mfa/devices (challenge_type: activation_code)
        S-->>App: Return Challenge ID
        
        App->>App: Sign "code" with Private Key
        App->>S: PUT /mfa/challenges/signatures/{id} (Signature)
        
        alt Signature Valid
            S-->>App: 204 Success (Device Registered)
        else Signature Invalid
            S-->>App: 400 Error (Binding Failed)
        end
    end
```

## Implementation

Select the binding method you wish to implement to see the specific steps.

### Integration steps

1. **Initiation:** The customer enters device information in your app.
2. **Key Generation:** Your app generates a private/public key pair in `ecdsa-p256` format.
3. **Submission:** Send the public key (hex-encoded) and device info to Solaris via [POST Create device](#post-create-device).
4. **Challenge:** Solaris triggers a verification flow based on your `challenge_type`:
  * **SMS:** Solaris sends a 6-digit OTP via SMS to the customer.
  * **QR:** Solaris validates the Activation Code logic (no SMS is sent).
5. **Input:** The customer provides the verification data:
  * **SMS:** Customer enters the 6-digit OTP.
  * **QR:** Customer scans the QR code to extract the Activation Code string.
6. **Signing:** Your app [creates a signature](#how-to-create-the-signature) using the Private Key and the input data (OTP or Activation Code).
7. **Verification:** Send the signature to Solaris via [PUT Verify the signature](#put-verify-the-signature).
8. **Completion:** If valid, Solaris registers the device.


SMS OTP (Default)
### Step 1: Create a device

Collect the device information and generate a private/public key pair (`ecdsa-p256`) on the device.

**POST Create device**

Call this endpoint to initiate the binding. By default (`challenge_type: "sms"`), this triggers an SMS OTP to the customer's verified number.

**Request URL**


```shell
POST /v1/mfa/devices
```

**Payload:**


```json
{
  "person_id": "ec3d16cbc106f481b72d881d90c89cc5cper",
  "key_type": "ecdsa-p256",
  "key": "04a346c447...",
  "key_purpose": "unrestricted",
  "name": "iPhone 13",
  "challenge_type": "sms"
}
```

**Response:**
Solaris creates a temporary, unverified device and returns a challenge object. The challenge expires in **5 minutes**.

### Step 2: Verify the signature

The customer enters the **6-digit SMS OTP** into your app. You must use this OTP and the stored Private Key to create a cryptographic signature.

**How to create the signature:**

1. **Hash:** Create a `SHA256` hash of the **6-digit OTP**.
2. **Sign:** Sign the hash using the Private Key.
3. **Encode:** Convert the signature to a Hex string (ASN.1 format).
4. **Send:** Submit the hex string to the `PUT /signatures/{id}` endpoint.


**PUT Verify the signature**


```shell
PUT /v1/mfa/challenges/signatures/{id}
```

**Response:**
Returns `204 No Content` on success.

Activation Code (QR)
### Step 1: Manage activation codes

Before binding, you must generate a valid activation code and deliver it to the customer.

**Rules and limits:**

* **Single Active Code:** A customer can have only **one** valid activation code at a time.
* **Expiry:** The default expiry is **90 days** (configurable via partner settings).
* **Usage Limit:** A code can be used a maximum of **5 times** (configurable via partner settings).


**Create an activation challenge**

Call this endpoint to generate a new code.

Handling Conflicts
If a code already exists, you must set `invalidate_existing_code=true` to generate a new one.


```shell
POST /v1/mfa/challenges/activation?invalidate_existing_code=true
```

**Payload:**


```json
{
  "person_id": "ec3d16cbc106f481b72d881d90c89cc5cper",
  "origin": "MOBILE_APP",
  "purpose": "DEVICE_BINDING",
  "delivery_method": "SNAILMAIL"
}
```

**Response:**
Returns the plain text `code`. Convert this to a QR code and deliver it.

* [API Reference: Create Activation Challenge](/api-reference/onboarding/device-management/activation-challenges/paths/~1v1~1mfa~1challenges~1activation/post)


**Handle Customer Support triggers (Webhooks)**

Activation codes may be generated internally—for example, by a Solaris agent via the Backoffice or by your own support team using the Solaris WebUI. To handle these flows, you must subscribe to the `ACTIVATION_CHALLENGE_CREATION` webhook.

1. **Listen:** Receive the webhook event.
2. **Retrieve:** Call `GET /v1/mfa/challenges/activation/{id}` to fetch the `code`.
3. **Deliver:** Convert the code to a QR image and deliver it.


* [API Reference: Retrieve Activation Challenge](/api-reference/onboarding/device-management/activation-challenges/paths/~1v1~1mfa~1challenges~1activation~1%7Bid%7D/get)


**Track delivery status**

Report the status when you dispatch the code (e.g., via mail).


```shell
POST /v1/mfa/challenges/activation/{id}/delivery_events
```

* [API Reference: Create Delivery Event](/api-reference/onboarding/device-management/activation-challenges/paths/~1v1~1mfa~1challenges~1activation~1%7Bid%7D~1delivery_events/post)


**Payload:** `{"type": "DISPATCHED", "provider": "DEUTSCHE POST", "timestamp": "..."}`

**Retrieve delivery history**


```shell
GET /v1/mfa/challenges/activation/{id}/delivery_events
```

* [API Reference: List Delivery Events](/api-reference/onboarding/device-management/activation-challenges/paths/~1v1~1mfa~1challenges~1activation~1%7Bid%7D~1delivery_events/get)
* [API Reference: Retrieve Delivery Event](/api-reference/onboarding/device-management/activation-challenges/paths/~1v1~1mfa~1challenges~1activation~1%7Bactivation_challenge_id%7D~1delivery_events~1%7Bid%7D/get)


### Step 2: Create a device

When the customer scans the QR code, your app should extract the code and initiate the binding.

**POST Create device**

You must explicitly set `challenge_type` to `activation_code`.

**Request URL**


```shell
POST /v1/mfa/devices
```

**Payload:**


```json
{
  "person_id": "ec3d16cbc106f481b72d881d90c89cc5cper",
  "key_type": "ecdsa-p256",
  "key": "04a346c447...",
  "key_purpose": "unrestricted",
  "name": "iPhone 13",
  "challenge_type": "activation_code"
}
```

### Step 3: Verify the signature

Your app must sign the **Activation Code** string extracted from the QR code.

**How to create the signature:**

1. **Hash:** Create a `SHA256` hash of the **Activation Code string**.
2. **Sign:** Sign the hash using the Private Key.
3. **Encode:** Convert the signature to a Hex string (ASN.1 format).
4. **Send:** Submit the hex string to the `PUT /signatures/{id}` endpoint.


**PUT Verify the signature**


```shell
PUT /v1/mfa/challenges/signatures/{id}
```

### Lifecycle Management

You can list or delete codes if a customer reports a lost letter.

* **List:** `GET /v1/mfa/challenges/activation?filter[person_id]={id}`
* **Delete:** `DELETE /v1/mfa/challenges/activation/{id}`
* [API Reference: List Activation Challenges](/api-reference/onboarding/device-management/activation-challenges/paths/~1v1~1mfa~1challenges~1activation/get)
* [API Reference: Delete Activation Challenge](/api-reference/onboarding/device-management/activation-challenges/paths/~1v1~1mfa~1challenges~1activation~1%7Bid%7D/delete)


## Post-binding: Manage keys

After successful binding (via either method), you can add more keys to the device. This is useful if you want to separate keys for different permission levels (e.g., Restricted vs. Unrestricted).

### POST Add new key to device

To add a new key, you must sign the request using the **existing** verified private key.

**Request URL**


```shell
POST /mfa/devices/{id}/keys
```

* [API Reference: Add Key](/api-reference/onboarding/device-management/#tag/Device-binding/paths/~1v1~1mfa~1devices~1%7Bid%7D~1keys/post)


### Manage device keys

* [GET Retrieve a specific key](/api-reference/onboarding/device-management/#tag/Device-binding/paths/~1v1~1mfa~1devices~1%7Bid%7D~1keys~1%7Bkey_id%7D/get)
* [GET Retrieve all keys for a device](/api-reference/onboarding/device-management/#tag/Device-binding/paths/~1v1~1mfa~1devices~1%7Bid%7D~1keys/get)


## Testing in Sandbox

In the Sandbox environment, you cannot generate real QR codes or send physical mail. Use these static activation codes to simulate specific scenarios when calling `PUT /v1/mfa/challenges/signatures/{id}`.

| Scenario | Activation Code String | Expected Result |
|  --- | --- | --- |
| **Successful Binding** | `static_activation_code_valid_abcdefghijklmnopqrstuvwxyz123456789` | `204 No Content` (Success) |
| **Code Expired** | `static_activation_code_expired_abcdefghijklmnopqrstuvwxyz1234567` | `400 Error` (`error_code: activation_code_expired`) |
| **Usage Limit Reached** | `static_activation_code_usage_limit_exceeded_abcdefghijklmnopqrst` | `400 Error` (`error_code: activation_code_usage_limit_reached`) |