Skip to content

API Authentication

Table of Contents

  1. Overview
  2. Authentication Methods
  3. JWT Token Authentication
  4. API Key Authentication
  5. Wallet Signature Authentication
  6. Security Best Practices
  7. Troubleshooting

Overview

Pocketbook API supports three authentication methods, each designed for different use cases:

MethodBest ForExpiryScopes
JWT TokenWeb apps, user sessions24 hoursUser-based permissions
API KeyServer-to-server, automationNo expiry*Configurable scopes
Wallet SignatureOne-time auth, Web3 apps5 minutes**N/A (exchanges for JWT)

* Can be revoked manually ** Timestamp window for signature validation


Authentication Methods

Quick Comparison

javascript
// JWT Token Authentication
fetch('/api/certificate/create', {
  headers: {
    'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIs...',
    'Content-Type': 'application/json'
  }
});

// API Key Authentication
fetch('/api/bulk-mint', {
  headers: {
    'x-api-key': 'pk_0000000000000000000000000000000000000000000000000000000000000001',
    'Content-Type': 'application/json'
  }
});

// Wallet Signature Authentication (initial auth)
fetch('/api/auth', {
  method: 'POST',
  body: JSON.stringify({
    signature: '0x...',
    address: '0x...',
    message: 'Authenticate with Pocketbook: 1234567890',
    timestamp: '1234567890'
  })
});

JWT Token Authentication

Overview

JSON Web Tokens (JWT) provide secure, stateless authentication for user sessions. Best suited for:

  • Web applications
  • Mobile apps
  • User-specific operations
  • Short-lived sessions

Getting a JWT Token

Step 1: Prepare Authentication Message

javascript
const timestamp = Math.floor(Date.now() / 1000);
const message = `Authenticate with Pocketbook: ${timestamp}`;

Message Format: Must exactly match Authenticate with Pocketbook: <unix_timestamp>

Step 2: Sign Message with Wallet

javascript
// Using ethers.js v6
import { ethers } from 'ethers';

const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const signature = await signer.signMessage(message);
const address = await signer.getAddress();
javascript
// Using ethers.js v5
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const signature = await signer.signMessage(message);
const address = await signer.getAddress();
javascript
// Using viem
import { createWalletClient, custom } from 'viem';
import { mainnet } from 'viem/chains';

const client = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum)
});

const [address] = await client.getAddresses();
const signature = await client.signMessage({
  account: address,
  message
});

Step 3: Exchange Signature for JWT

javascript
const response = await fetch('https://api.pocketbook.studio/api/certificate/auth', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    signature,
    address,
    message,
    timestamp: timestamp.toString()
  })
});

const data = await response.json();
if (data.success) {
  const token = data.data.token;
  // Store token securely
  localStorage.setItem('pocketbook_token', token);
}

Using JWT Token

javascript
const token = localStorage.getItem('pocketbook_token');

const response = await fetch('https://api.pocketbook.studio/api/profile', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
});

Token Structure

JWT tokens contain three parts separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOiI2MjdhNjE3YjRjMWYyZDAwMDEiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJyb2xlIjoidXNlciIsImlhdCI6MTY0MDk5NTIwMCwiZXhwIjoxNjQxMDgxNjAwfQ.
5J6k8hK3...

Header.Payload.Signature

Decoded Payload:

json
{
  "userId": "507f1f77bcf86cd799439011",
  "email": "user@example.com",
  "role": "user",
  "walletAddress": "0x1234...",
  "smartAccountAddress": "0x5678...",
  "enterpriseId": "ent_abc123",
  "iat": 1640995200,
  "exp": 1641081600
}

Token Refresh

Tokens expire after 24 hours. Implement token refresh:

javascript
class AuthManager {
  constructor() {
    this.token = null;
    this.expiresAt = null;
  }

  async authenticate(wallet) {
    const timestamp = Math.floor(Date.now() / 1000);
    const message = `Authenticate with Pocketbook: ${timestamp}`;
    const signature = await wallet.signMessage(message);
    const address = await wallet.getAddress();

    const response = await fetch('/api/certificate/auth', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ signature, address, message, timestamp: timestamp.toString() })
    });

    const data = await response.json();
    if (data.success) {
      this.token = data.data.token;
      const decoded = this.decodeJWT(this.token);
      this.expiresAt = decoded.exp * 1000;
      return this.token;
    }

    throw new Error('Authentication failed');
  }

  async getToken(wallet) {
    // Check if token exists and is not expired
    if (this.token && this.expiresAt > Date.now() + 300000) { // 5 min buffer
      return this.token;
    }

    // Token expired or doesn't exist, re-authenticate
    return await this.authenticate(wallet);
  }

  decodeJWT(token) {
    const base64Url = token.split('.')[1];
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    return JSON.parse(atob(base64));
  }
}

// Usage
const authManager = new AuthManager();
const token = await authManager.getToken(wallet);

API Key Authentication

Overview

API keys provide long-lived authentication for server-side integrations. Best suited for:

  • Backend services
  • Automated workflows
  • CI/CD pipelines
  • Third-party integrations

Creating an API Key

Via Dashboard

  1. Log in to your enterprise account at pocketbook.studio
  2. Navigate to SettingsAPI Keys
  3. Click Create New API Key
  4. Configure:
    • Name: Descriptive name for the key
    • Scopes: Select required permissions
    • Rate Limit: Optional custom limit
  5. Copy and securely store the key (shown only once)

Via API

javascript
const response = await fetch('https://api.pocketbook.studio/api/enterprise/api-keys', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${jwtToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Production Backend Key',
    scopes: ['bulk:mint', 'voucher:create', 'certificate:read'],
    rateLimit: 1000 // requests per hour
  })
});

const data = await response.json();
const apiKey = data.data.apiKey.fullKey; // pk_00000...

Using API Keys

javascript
const apiKey = process.env.POCKETBOOK_API_KEY; // Store in environment variables

const response = await fetch('https://api.pocketbook.studio/api/bulk-mint', {
  method: 'POST',
  headers: {
    'x-api-key': apiKey,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    // ... request body
  })
});

API Key Scopes

ScopeDescriptionEndpoints
bulk:mintCreate bulk mint jobsPOST /bulk-mint
bulk:retryRetry bulk mint jobsPOST /bulk-mint/:id/retry
bulk:readRead bulk mint jobsGET /bulk-mint, GET /bulk-mint/:id
voucher:createCreate vouchersPOST /voucher/create
voucher:readRead voucher detailsGET /voucher/:id
voucher:regenerateRegenerate vouchersPOST /voucher/:id/regenerate
certificate:readRead certificatesGET /certificate/:id
certificate:createCreate certificatesPOST /certificate/create
analytics:readRead analyticsGET /dashboard/, GET /reporting/
pos:voucherPOS voucher operationsSpecial POS endpoints

Wallet Signature Authentication

Overview

Wallet signature authentication is a one-time authentication method that exchanges a signed message for a JWT token.

Authentication Flow

javascript
async function authenticateWithWallet(wallet) {
  // 1. Generate timestamp and message
  const timestamp = Math.floor(Date.now() / 1000);
  const message = `Authenticate with Pocketbook: ${timestamp}`;

  // 2. Sign message
  const signature = await wallet.signMessage(message);
  const address = await wallet.getAddress();

  // 3. Submit to API
  const response = await fetch('https://api.pocketbook.studio/api/auth', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      signature,
      address,
      message,
      timestamp: timestamp.toString()
    })
  });

  const data = await response.json();

  if (!data.success) {
    throw new Error(data.error || 'Authentication failed');
  }

  return data.data.token;
}

Security Best Practices

API Key Security

DO:

  • Store API keys in environment variables
  • Use different keys for development and production
  • Rotate keys regularly
  • Implement key rotation without downtime
  • Use scoped keys with minimal permissions
  • Monitor key usage for anomalies
  • Revoke compromised keys immediately

DON'T:

  • Commit keys to version control
  • Share keys via email or chat
  • Use production keys in development
  • Grant unnecessary scopes
  • Embed keys in client-side code
  • Reuse keys across projects

JWT Token Security

DO:

  • Store tokens securely (httpOnly cookies for web, secure storage for mobile)
  • Implement token refresh before expiry
  • Clear tokens on logout
  • Validate token expiry on client side
  • Use HTTPS for all API requests

DON'T:

  • Store tokens in localStorage if handling sensitive data
  • Share tokens between users
  • Ignore token expiry
  • Include tokens in URLs
  • Log tokens in analytics

Troubleshooting

Common Issues

"Invalid signature"

Cause: Signature doesn't match message or address

Solutions:

  1. Verify message format exactly matches: Authenticate with Pocketbook: <timestamp>
  2. Ensure timestamp is current (within 5 minutes)
  3. Check that address matches signing wallet
  4. Confirm wallet is properly connected

"Token expired"

Cause: JWT token has exceeded 24-hour lifetime

Solution: Re-authenticate to get new token

javascript
async function ensureValidToken(authManager, wallet) {
  try {
    return await authManager.getToken();
  } catch (error) {
    if (error.message.includes('expired')) {
      return await authManager.authenticate(wallet);
    }
    throw error;
  }
}

"API key not found"

Cause: Invalid or revoked API key

Solutions:

  1. Verify key format: pk_ followed by 64 hex characters
  2. Check key hasn't been revoked
  3. Ensure key is from correct environment (dev/prod)
  4. Regenerate key if needed

"Insufficient permissions"

Cause: API key lacks required scopes or user role insufficient

Solutions:

  1. Check API key scopes match endpoint requirements
  2. Update key with necessary scopes
  3. For JWT: ensure user has required role (enterprise, admin)

Need Help?

Released under the MIT License.