Signature and Verify

This document defines the signing and verification rules for all parties interacting with the UR ecosystem. It serves as the single source of truth for signature formats and authentication mechanisms.

Overview

The UR ecosystem uses two primary authentication methods depending on the API being accessed:

  1. Partner Authentication (Server-to-Server): Used for UR-OPEN-API and Webhooks. Authenticates the Partner backend using a registered ECDSA key pair.

  2. User Authentication (Wallet-to-Server): Used for UR-API. Authenticates individual end-users using their wallet's private key.

Both methods utilize Ethereum Personal Sign (EIP-191): "\x19Ethereum Signed Message:\n{len(messageToSign)}{messageToSign}"


Part A: Partner Authentication (UR-OPEN-API & Webhooks)

This method is used when a Partner backend calls UR APIs or when UR sends Webhooks to a Partner.

1. HTTP Headers

Header
Description
Required

X-Api-Signature

Hexadecimal signature with 0x prefix.

Yes

X-Api-Deadline

The Unix timestamp (in seconds) indicating when the request expires. Setting this to 5 minutes from the current time.

Yes

X-Api-PublicKey

The Ethereum address of the signer.

Optional

2. Signing Logic

Partner Request (Partner -> UR)

  • Signer: Partner's registered backend Ethereum address.

  • Message Components:

    • requestBody: The exact raw JSON string in the request body.

    • deadline: The value sent in the X-Api-Deadline header, a timestamp within the next 5 minutes.

  • Message to Sign: messageToSign = "{requestBody} {deadline}" (Note the single space between body and deadline).

  • Signing code example: Refers to this.

UR Response / Webhook (UR -> Partner)

  • Signer: UR Server address.

  • Message Components:

    • responseBody: The exact raw JSON string returned in the HTTP body.

  • Message to Sign: messageToSign = "{responseBody}"

3. Verification

The receiver must:

  1. Read the raw body and relevant headers.

  2. Construct the messageToSign as defined above.

  3. Use EIP-191 recovery to extract the signer's Ethereum address from the X-Api-Signature.

  4. Verify that the recovered address matches the expected/whitelisted address.


Part B: User Authentication (UR-API)

This method is used for sensitive user operations (e.g., FX, Transfers) where a direct wallet signature is required.

1. HTTP Headers

Header
Description

sign

The user's wallet signature.

hash

A Keccak256 hash of the business payload or a unique message.

deadline

The Unix timestamp (in seconds) indicating when the request expires. We recommend setting this to 20 minutes from the current time.

tokenId

The user's UR Token ID (URID).

2. Signing Logic

  1. Construct Base Message: baseMessage = hash + deadline (String concatenation).

  2. Generate Intermediate Hash: intermediateHash = Keccak256(baseMessage).

  3. Construct Final Message: finalMessage = "I agree to access my profile. " + intermediateHash.hex().

  4. Sign: The user signs the finalMessage using their wallet (EIP-191).


Key Pair

1. Generate Key Pair Using Node.js (viem)

2. Key Management Recommendations

Private Key Storage: Use key management services like AWS Secrets Manager, HashiCorp Vault Public Key Registration: Synchronize the generated address (publicKey) with the UR system through secure channels


Code Examples

signature

verify

User wallet signature


Important Notes

  • Raw Body: Always use the raw HTTP body bytes. Do not re-serialize JSON, as key ordering or whitespace differences will cause signature mismatches.

  • Deadline Window: Recommended window is 1-5 minutes. Requests with an expired deadline or a deadline too far in the future (>5 mins) will be rejected.

  • Replay Protection: For high-concurrency environments, add a small random offset (1-5 seconds) to the deadline to ensure each request generates a unique signature.

  • Case Sensitivity: Ethereum addresses should be treated as case-insensitive but are typically stored/transmitted in lowercase or checksum format.

Environment Addresses

Environment
UR Server Signature Address

Sepolia

0x4D2AA3f43De8f8BE746E315D291B804a4aBD3939

Mainnet

0xee28dEaD5F114C8405BE3be1144D59A4110B7F79

Last updated