Skip to content

Authentication

The License Server API uses Bearer token authentication for administrative endpoints, while public endpoints require no authentication.

Authentication Levels

Public Endpoints

Most endpoints are public and require no authentication:

  • License verification (/verify-license)
  • License activation (/activate-license)
  • Usage tracking (/track-usage)
  • Export license data (/export-license)

Admin Endpoints (Authentication Required)

Administrative operations require a Bearer token:

  • Issue licenses (/issue-license)
  • Revoke licenses (/revoke-license)
  • Delete licenses (/delete-license)
  • View admin statistics (/admin/stats)

Setting Up Authentication

1. Get Your Admin API Key

The admin API key is configured via environment variable:

bash
export ADMIN_API_KEY="your-secure-admin-api-key-here"

Security Best Practices

  • Use a strong, randomly generated key (minimum 32 characters)
  • Store the key securely (environment variables, secrets manager)
  • Rotate the key regularly
  • Never log or expose the key in client-side code

2. Making Authenticated Requests

Include the Bearer token in the Authorization header:

bash
curl -X POST https://your-license-api.com/v1/issue-license \
  -H "Authorization: Bearer your-admin-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "tier": "pro",
    "product_id": "your-product",
    "issued_to": "customer@company.com",
    "expires_at": "2025-12-31T23:59:59Z"
  }'

Client Examples

JavaScript/Node.js

javascript
class LicenseAdminClient {
  constructor(baseURL, adminKey) {
    this.baseURL = baseURL;
    this.headers = {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${adminKey}`
    };
  }

  async issueLicense(licenseData) {
    const response = await fetch(`${this.baseURL}/issue-license`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(licenseData)
    });
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${await response.text()}`);
    }
    
    return response.json();
  }

  async revokeLicense(key, revoked = true) {
    const response = await fetch(`${this.baseURL}/revoke-license`, {
      method: 'POST', 
      headers: this.headers,
      body: JSON.stringify({ key, revoked })
    });
    
    return response.json();
  }
}

// Usage
const adminClient = new LicenseAdminClient(
  'https://your-license-api.com/v1',
  process.env.ADMIN_API_KEY
);

const license = await adminClient.issueLicense({
  tier: 'pro',
  product_id: 'your-product',
  issued_to: 'customer@example.com',
  expires_at: '2025-12-31T23:59:59Z',
  limits: { users: 100 }
});

Python

python
import os
import requests
from typing import Dict, Any

class LicenseAdminClient:
    def __init__(self, base_url: str, admin_key: str = None):
        self.base_url = base_url
        self.admin_key = admin_key or os.getenv('ADMIN_API_KEY')
        
        if not self.admin_key:
            raise ValueError("Admin API key is required")
        
        self.headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {self.admin_key}'
        }
    
    def issue_license(self, license_data: Dict[str, Any]) -> Dict[str, Any]:
        response = requests.post(
            f'{self.base_url}/issue-license',
            json=license_data,
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()
    
    def revoke_license(self, key: str, revoked: bool = True) -> Dict[str, Any]:
        response = requests.post(
            f'{self.base_url}/revoke-license',
            json={'key': key, 'revoked': revoked},
            headers=self.headers
        )
        response.raise_for_status() 
        return response.json()

# Usage
admin_client = LicenseAdminClient('https://your-license-api.com/v1')

license = admin_client.issue_license({
    'tier': 'pro',
    'product_id': 'your-product', 
    'issued_to': 'customer@example.com',
    'expires_at': '2025-12-31T23:59:59Z',
    'limits': {'users': 100}
})

cURL Examples

Issue a License

bash
curl -X POST https://your-license-api.com/v1/issue-license \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tier": "pro",
    "product_id": "your-product",
    "issued_to": "customer@company.com", 
    "expires_at": "2025-12-31T23:59:59Z",
    "limits": {
      "users": 100,
      "api_calls": 10000
    },
    "max_activations": 5
  }'

Revoke a License

bash
curl -X POST https://your-license-api.com/v1/revoke-license \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "7yChyZcfMG23Dx1sjBoLziPFrH4n-6f28",
    "revoked": true
  }'

Get Admin Statistics

bash
curl -H "Authorization: Bearer $ADMIN_API_KEY" \
     https://your-license-api.com/v1/admin/stats

Error Handling

403 Forbidden

When authentication fails, you'll receive a 403 status with an error message:

json
{
  "error": "Unauthorized"
}

Common causes:

  • Missing Authorization header
  • Invalid or expired admin API key
  • Incorrect token format (should be Bearer <key>)

Example Error Handling

javascript
try {
  const response = await fetch('/issue-license', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${adminKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(licenseData)
  });

  if (response.status === 403) {
    throw new Error('Admin authentication failed. Check your API key.');
  }
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || 'Request failed');
  }
  
  return await response.json();
} catch (error) {
  console.error('License operation failed:', error.message);
  throw error;
}

Security Considerations

Token Storage

  • Server-side: Store in environment variables or secure secret managers
  • Never store admin tokens in:
    • Client-side JavaScript
    • Version control systems
    • Log files
    • URLs or query parameters

Token Rotation

  • Rotate admin API keys regularly (recommended: every 90 days)
  • Use different keys for different environments (dev/staging/production)
  • Implement key rotation without downtime using key versioning

Network Security

  • Always use HTTPS in production
  • Consider IP allowlisting for admin operations
  • Implement request signing for additional security if needed

Next Steps