Quadrata Integration
  • INTRODUCTION
    • Introduction to Quadrata
    • Passport Attributes
  • HOW TO INTEGRATE
    • Quadrata SDK
      • Get Started Quickly
      • Advanced
        • Installation
        • SDK Configuration
        • Onboarding Example
        • Client Libraries
          • Client Configuration
          • Client Lazy Loading
          • Client Eager Loading
          • Client Helper Component
          • Client React Hooks
            • useOnboardStatus
          • Client Examples
            • With Wagmi Connect
            • KYC Only
            • KYB Only
            • All In One
        • API Libraries
          • API Configuration
          • API Service Options
          • API Service Libraries
            • Create Access Token
            • Create Privacy Access Token
            • Fetch Attribute Values
            • Fetch Onboard Status
            • Fetch Passport List
            • Fetch Privacy Data
            • Fetch Privacy Grants
            • Fetch Wallet Screening
            • Revoke Privacy Grants
    • Onboard users
      • Individual Passport Onboarding
        • 1. Installation
        • 2. API Authentication
        • 3. API Onboard Status
        • 4. QuadClient Package
        • 5. Privacy Data Permissions
        • 6. Full Example
      • Business Passport Onboarding
        • 1. Installation
        • 2. API Authentication
        • 3. QuadrataKyb Package
        • 4. Privacy Data Permissions
        • 5. Full Example
      • All-In-One Passport Onboarding
        • 1. Installation
        • 2. API Authentication
        • 3. QuadrataReact Package
        • 4. Full Example
    • Request Privacy Data
      • List of Privacy Data
      • Privacy Data Permissions
      • API Requests
        • How to sign API
          • Full Example
          • Generate ECDSA Key Pair
        • API Get Privacy Permissions
        • API Privacy Access Token
        • API Get Privacy Data
        • API Revoke Permissions
    • Query attributes
      • Via Smart Contract
        • Query a single attribute
        • Query multiple attributes
        • Query Helper
      • Via API
    • On-Chain Wallet Screening
      • How to sign API
        • Full Example
        • Generate ECDSA Key Pair
      • API Get On-Chain AML Score
    • Webhooks
      • Onboarding Webhooks
      • Ongoing Monitoring Webhooks
      • Webhook Request Signature
    • Burn Passports
  • additional information
    • Smart contracts
    • Quadrata Sandbox
    • Passport Issuers
    • Privileged Roles & Ownership
    • Constants
    • Flex Kit Attributes
      • Smart Contract Addresses
Powered by GitBook
On this page
  1. HOW TO INTEGRATE
  2. On-Chain Wallet Screening
  3. How to sign API

Full Example

API Signed Request Examples

const buffer = require('buffer');
const crypto = require('crypto');

/* Message Signing Helpers */

/**
 * Convert a string to an array buffer
 * @param {string} str
 * @returns {string}
 */
function str2ab(str) {
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

/**
 * Convert a string to base64-url safe encoding
 * @param {string} str
 * @returns {string}
 */
function base64UrlEncode(str) {
    return Buffer.from(str).toString('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=+$/, '');
}

/**
 * Import a private key and return a CryptoKey
 * @param {string} privateKeyDer base64 encoded private key der
 * @returns {Promise<CryptoKey>}
 */
async function getPrivateKeyFromDer(privKeyDer) {
    const decoded = Buffer.from(privKeyDer, 'base64').toString('binary');
    const buf = str2ab(decoded);
    return crypto.subtle.importKey('pkcs8', buf, {
        name: 'ECDSA',
        namedCurve: 'P-256',
    }, true, ['sign']);
}

/**
 * Sign a message with a private CryptoKey
 * @param {CryptoKey} privateKey
 * @param {string} message The message to sign
 * @returns {string} The signed message
 */
async function signMessage(privateKey, message) {
    return crypto.subtle.sign(
        {
            name: 'ECDSA',
            hash: 'SHA-256'
        }, 
        privateKey, 
        new TextEncoder().encode(message)
    );
}

/**
 * Create a request signature
 * @param {CryptoKey} privateKey
 * @param {string} date HTTP header date value
 * @param {string} method HTTP method value
 * @param {string} path HTTP path 
 * @param {string} [queryString] HTTP path query string (? not included)
 */
async function createRequestSignature({
    privateKey,
    date, 
    method, 
    path, 
    queryString = undefined
}) {
    const nonce = Date.now().toString();
    const sigParts = [
        method.toUpperCase(),
        path,
        queryString,
        date,
        nonce
    ].filter(str => str && str.length !== 0);
    const signature = await signMessage(privateKey, sigParts.join('\n'));
    return base64UrlEncode(signature) + '.' + base64UrlEncode(nonce);
}

/* HTTP Request */

const API_URL = 'https://int.quadrata.com';
const API_KEY = '...';

/**
 * Send a signed request
 * @param {CryptoKey} privateKey
 * @param {string} method HTTP method value
 * @param {string} path HTTP path
 * @param {string} [queryString] HTTP path query string (? not included)
 * @param {object} [body] HTTP body payload
 * @param {object} [extraHeaders] Optional headers to send with the request
 * @returns {Promise<Response|void>}
 */
async function makeRequest({
    privateKey, 
    method, 
    path, 
    queryString = undefined,
    body = undefined,
    extraHeaders = undefined
}) {
    const date = (new Date()).toUTCString();
    const signature = await createRequestSignature({
        privateKey,
        method,
        path,
        queryString,
        date
    });
    const headers = {
        ...extraHeaders,
        'Authorization': `Basic ${Buffer.from(API_KEY).toString('base64')}`,
        'Date': date,
        'Signature': signature
    };
    let urlPath = `${API_URL}${path}`;
    if (queryString && queryString.length !== 0) {
        urlPath += `?${queryString}`;
    }
    const response = await fetch(urlPath, {
        method,
        headers,
        body: body ? JSON.stringify(body) : undefined
    });
    if (!response.ok) {
        console.error(response);
        return;
    }
    return response.json();
}

/**
 * Base64 Encoded Private Key DER
 * @type {string}
 */
const privateKeyDer = '...';

/**
 * @type {CryptoKey}
 */
const privateKey = await getPrivateKeyFromDer(privateKeyDer);

/**
 * @type {string}
 */
const walletAddress = '...';

// make the request
const json = await makeRequest({
    method: 'GET',
    privateKey: privateKey,
    path: `/api/v1/wallets/${encodeURIComponent(walletAddress)}/screening`,
});

import base64
import requests

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey, ECDSA
from cryptography.hazmat.primitives import hashes
from datetime import datetime, timezone
from secrets import token_hex
from typing import Any, Dict, Optional, Union

API_URL = 'http://int.quadrata.com'
API_KEY = b'...'
PRIVATE_KEY_DER = b'...'  # base64 encoded private key der

def b64_url_encode(bstr: bytes) -> str:
    return base64.urlsafe_b64encode(bstr).rstrip(b'=').decode('utf-8')

def private_key_from_der(
    der: bytes, 
    password: Optional[bytes] = None
) -> EllipticCurvePrivateKey:
    return serialization.load_der_private_key(base64.b64decode(der), password)

def create_sig(
    priv_key: EllipticCurvePrivateKey,
    method: str,
    path: str,
    date: str,
    query_string: Optional[str] = None
) -> str:
    nonce = token_hex(10)
    return b64_url_encode(        
        priv_key.sign(
            '\n'.join(
                list(
                    filter(
                        lambda s: s and len(s) > 0,
                        [
                            method.upper(),
                            path,
                            query_string if query_string else None,
                            date,
                            nonce,
                        ],
                    )
                )
            ).encode('utf-8'),
            ECDSA(hashes.SHA256()),
        )
    ) + (f'.{b64_url_encode(bytes(nonce, "utf-8"))}')

def make_request(
    path: str,
    method: str = 'get',
    query_string: Optional[str] = None,
    extra_headers: Optional[Dict[str, Any]] = None
) -> str:
    request_action = getattr(requests, method.lower(), None)
    if request_action is None:
        raise ValueError(f'Invalid method provided: {method}')
    date = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')
    sig = create_sig(
        priv_key=private_key_from_der(PRIVATE_KEY_DER),
        method=method,
        path=path,
        date=date,
        query_string=query_string
    )
    url_path = f'{API_URL}{path}'
    if query_string is not None and len(query_string) != 0:
        url_path += f'?{query_string}'
    response = request_action(
        url_path,
        headers={
            'Authorization': f'Basic {base64.b64encode(API_KEY).decode()}',
            'Signature': sig,
            'Date': date
        } | (extra_headers if extra_headers is not None else {})
    )
    return response.json()

wallet_address = '...'

api_response = make_request(f'/api/v1/wallets/{wallet_address}/screening')

PreviousHow to sign APINextGenerate ECDSA Key Pair

Last updated 11 months ago