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. Request Privacy Data
  3. API Requests
  4. How to sign API

Full Example

API Signed Request Examples

PreviousHow to sign APINextGenerate ECDSA Key Pair

Last updated 8 months ago

If you use the to make your API calls, the request signature is generated for you.

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/privacy/grants/${encodeURIComponent(walletAddress)}`,
});

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/privacy/grants/{wallet_address}')
Quadrata SDK