Token Exchange API

Your backend calls this endpoint to exchange an HMAC-signed request for a short-lived DropOnAir JWT. Never call this from client-side code, it requires your Server Secret Key.

POSThttps://sdk.droponair.com/api/token/exchange

Required headers

X-DropOnAir-Key: YOUR_APP_ID
X-DropOnAir-Timestamp: 1708361234          // Unix seconds, ±5 min of server time
X-DropOnAir-Nonce: a7f3k9mzq1r8t2xw      // Random ≥16 chars, single-use
X-DropOnAir-Signature: BASE64_HMAC_SHA256  // See signing formula below
Content-Type: application/json

HMAC signing formula

// message = appId + timestamp + nonce + sha256Hex(requestBodyBytes)
// signature = base64( HMAC-SHA256(serverSecretKey, message) )

import { createHmac, createHash } from 'crypto';

function buildSignature(secret: string, appId: string, timestamp: string, nonce: string, bodyBytes: Buffer): string {
  const bodyHash = createHash('sha256').update(bodyBytes).digest('hex');
  const message = appId + timestamp + nonce + bodyHash;
  return createHmac('sha256', secret).update(message).digest('base64');
}

Request body

{
  "customerUserToken": "alice-user-id-123"  // Stable user identifier
}

Response

{
  "accessToken": "eyJhbGci...",  // Short-lived JWT (15 min)
  "expiresIn": 900              // Seconds until expiry
}

Full backend example (Node.js)

app.post('/api/droponair/token', async (req, res) => {
  const userId = req.auth.userId;
  const timestamp = String(Math.floor(Date.now() / 1000));
  const nonce = crypto.randomBytes(16).toString('hex');
  const body = JSON.stringify({ customerUserToken: userId });
  const bodyBytes = Buffer.from(body, 'utf8');
  const sig = buildSignature(process.env.DROPONAIR_SERVER_SECRET, process.env.DROPONAIR_APP_ID, timestamp, nonce, bodyBytes);

  const result = await fetch('https://sdk.droponair.com/api/token/exchange', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-DropOnAir-Key': process.env.DROPONAIR_APP_ID,
      'X-DropOnAir-Timestamp': timestamp,
      'X-DropOnAir-Nonce': nonce,
      'X-DropOnAir-Signature': sig,
    },
    body,
  });

  const { accessToken, expiresIn } = await result.json();
  res.json({ accessToken, expiresIn });
});