Common signing patterns

The Due API uses two distinct signing patterns for different operations:

Pattern 1: Direct JSON Signing (Credential Creation Only)

Used only when creating credentials. You construct and sign a JSON object directly.

Flow:

1. API returns: challenge + clientDataHash
2. You create: JSON object with {clientDataHash, publicKey}
3. You sign: SHA256 hash of the JSON
4. Signature format: Hex-encoded

Example: See Create Credential guide

Pattern 2: Challenge-Response Flow (Everything Else)

Used for all other secured operations: vault creation, transaction signing, credential approval/deactivation, and any future secured endpoints.

Universal Request Structure

All Pattern 2 operations follow the same request/response structure:

Step 1: Initial Request

Send your operation payload:

{
  "payload": {
    // Your operation-specific data
    // Can be empty object {} for some operations like vault creation
  }
}

Examples of payloads:

  • Vault creation: {} (empty payload)
  • Transaction signing: {"keyId": "key_xxx", "hash": "0xabc..."}
  • Credential approval: {"credentialId": "passkey_xxx"}
  • Credential deactivation: {"credentialId": "passkey_xxx"}

Step 2: Challenge Response (403)

The API always returns a 403 error with a challenge:

{
  "success": false,
  "message": "Please sign the following challenge to proceed",
  "httpCode": 403,
  "code": "ACTION_SIGNATURE_REQUIRED",
  "data": {
    "factors": {
      "Key": {
        "credId": "",
        "clientData": "eyJ0eXBlIjoia2V5LmdldCIsImNoYWxsZW5nZS...", // Base64 encoded challenge
        "signature": null
      }
    },
    "challengeIdentifier": "chid1DLr5F2ij7oyYLrGy6ekLnouxbE5SiiaN"
  }
}

Key fields:

  • clientData: Base64-encoded challenge data to sign
  • challengeIdentifier: Unique ID for this challenge (valid for a limited time)

Step 3: Sign the Challenge

Decode and sign the clientData:

# Decode base64 → Sign with SHA256 → Encode to base64 URL-safe format
echo -n "<clientData_from_response>" | base64 -d | openssl dgst -sha256 -sign private.pem | base64 -w 0 | tr '+/' '-_' | tr -d '='

Important: The signature must be in base64 URL-safe format (not hex like Pattern 1).

Step 4: Retry with Signature

Send the same request with both payload and signature:

{
  "payload": {
    // Same payload as Step 1
  },
  "signature": {
    "challengeIdentifier": "<challengeIdentifier_from_step2>",
    "firstFactor": {
      "kind": "Key",
      "credentialAssertion": {
        "credId": "<your_credential_id>",
        "clientData": "<original_clientData_from_step2>",
        "signature": "<your_base64_url_safe_signature>"
      }
    }
  }
}

Response: Success with operation-specific result.

Complete Example: Vault Creation

Request 1 (Initial):

curl -X POST 'https://api.due.network/v1/vaults' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{}'  # Empty payload for vault creation

Response 1 (Challenge):

{
  "success": false,
  "httpCode": 403,
  "code": "ACTION_SIGNATURE_REQUIRED",
  "data": {
    "factors": {
      "Key": {
        "clientData": "eyJ0eXBlIjoia2V5LmdldCIsImNoYWxsZW5nZS..."
      }
    },
    "challengeIdentifier": "chid1DLZSWZlpvQSGTmMrwhUZTpCIFOwaneRi"
  }
}

Sign:

echo -n "eyJ0eXBlIjoia2V5LmdldCIsImNoYWxsZW5nZS..." | base64 -d | openssl dgst -sha256 -sign private.pem | base64 -w 0 | tr '+/' '-_' | tr -d '='

Request 2 (With Signature):

curl -X POST 'https://api.due.network/v1/vaults' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
  "signature": {
    "challengeIdentifier": "chid1DLZSWZlpvQSGTmMrwhUZTpCIFOwaneRi",
    "firstFactor": {
      "kind": "Key",
      "credentialAssertion": {
        "credId": "passkey_xonETR6gAv3wIyhy8ehjx",
        "clientData": "eyJ0eXBlIjoia2V5LmdldCIsImNoYWxsZW5nZS...",
        "signature": "MEUCIAlkmdPEf0B_5xVr4DKK6uvsN0y1YVzsFe4vAOMlPcn4AiEA..."
      }
    }
  }
}'

Response 2 (Success):

{
  "id": "key_2lRmxX5KBYRzMg",
  "address": "0x08bEB4Ad3D8D607646E4d5311eb355Ec2d2396F5"
}

Operations Using Pattern 2

OperationEndpointPayload Structure
Create VaultPOST /v1/vaults{} (empty)
Sign TransactionPOST /v1/vaults/sign{"keyId": "<vault_id>", "hash": "..."}
Approve CredentialPOST /v1/vaults/credentials/approve{"credentialId": "..."}
Deactivate CredentialPOST /v1/vaults/credentials/deactivate{"credentialId": "..."}
Activate CredentialPOST /v1/vaults/credentials/activate{"credentialId": "..."}

Note: In Sign Transaction, keyId is the Due Vault ID (e.g., key_2lRmxX5KBYRzMg) returned when creating the vault.

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as Due API
    participant Signer as Private Key

    Note over Client,API: Pattern 1: Credential Creation
    Client->>API: POST /credentials/init
    API-->>Client: challenge + clientDataHash
    Client->>Client: Create JSON {clientDataHash, publicKey}
    Client->>Signer: Sign SHA256(JSON)
    Signer-->>Client: Hex signature
    Client->>API: POST /credentials with signature
    API-->>Client: Credential created

    Note over Client,API: Pattern 2: Challenge-Response (All Other Operations)
    Client->>API: POST /endpoint with {payload}
    API-->>Client: 403 with base64 challenge
    Client->>Client: Decode base64 challenge
    Client->>Signer: Sign SHA256(decoded)
    Signer-->>Client: Base64 URL-safe signature
    Client->>API: POST /endpoint with {payload, signature}
    API-->>Client: Success response