API Key Security

Complete guide to securing and managing API keys for the SCORM API.

Table of Contents

API Key Overview

What are API Keys?

API keys are long-lived credentials that provide programmatic access to the SCORM API. They are:

  • Tenant-specific (one key per tenant)
  • Scope-based (read, write, admin permissions)
  • Hashed in storage (SHA-256, never plain text)
  • Revocable at any time

Key Format

API keys follow this format:

scorm_live_abc123def456ghi789jkl012mno345pqr678stu901vwx234yz
  • Prefix: scorm_live_ (production) or scorm_test_ (test)
  • Length: 64+ characters
  • Format: Base64-encoded random bytes

Creating API Keys

Via Dashboard

  1. Sign in to SCORM API dashboard
  2. Navigate to DashboardAPI Keys
  3. Click "Create API Key"
  4. Configure:
    • Name: Descriptive name (e.g., "Production API Key")
    • Scopes: Select permissions (read, write, admin)
    • Expires In (optional): Days until expiration
  5. Click "Create"
  6. Important: Copy key immediately - it won't be shown again!

Via Admin Script

cd apps/scorm-api
./scripts/create-api-key.sh <tenant_id> "Key Name" "read,write"

Example:

./scripts/create-api-key.sh \
  550e8400-e29b-41d4-a716-446655440000 \
  "Production API Key" \
  "read,write"

Output:

✅ API Key created successfully!
Key: scorm_live_abc123def456...
Tenant: 550e8400-e29b-41d4-a716-446655440000
Scopes: read, write

⚠️  IMPORTANT: Save this key securely - it will not be shown again!

Via API

curl -X POST https://scorm-api.allurelms.com/api/admin/api-keys \
  -H "X-API-Key: admin-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Production API Key",
    "scopes": ["read", "write"],
    "expires_in_days": 90
  }'

Securing API Keys

Storage

❌ Never Do This:

// ❌ Hardcoded in code
const apiKey = 'scorm_live_abc123...';

// ❌ In version control
// .env (committed to git)
SCORM_API_KEY=scorm_live_abc123...

✅ Always Do This:

// ✅ Environment variable
const apiKey = process.env.SCORM_API_KEY;
if (!apiKey) {
  throw new Error('API key not configured');
}

Environment Variables

Development:

# .env.local (gitignored)
SCORM_API_KEY=scorm_live_abc123...

Production:

# Use secret management
# AWS Secrets Manager
aws secretsmanager get-secret-value --secret-id scorm-api-key

# Vercel
vercel env add SCORM_API_KEY production

# Docker
docker run -e SCORM_API_KEY="scorm_live_..." your-image

Secret Management Services

Recommended:

  • AWS Secrets Manager
  • Google Cloud Secret Manager
  • Azure Key Vault
  • HashiCorp Vault
  • 1Password Secrets Automation

Example (AWS Secrets Manager):

import { SecretsManager } from '@aws-sdk/client-secrets-manager';

const client = new SecretsManager({ region: 'us-east-1' });
const secret = await client.getSecretValue({
  SecretId: 'scorm-api-key'
});

const apiKey = JSON.parse(secret.SecretString).apiKey;

Key Rotation

Why Rotate?

  • Security: Limits exposure if key is compromised
  • Compliance: Meets security requirements
  • Best Practice: Regular rotation reduces risk

Rotation Schedule

Recommended:

  • Production Keys: Every 90 days
  • Development Keys: Every 180 days
  • After Compromise: Immediately

Rotation Process

Step 1: Create New Key

# Create new key
./scripts/create-api-key.sh <tenant_id> "New Production Key" "read,write"

Step 2: Update Integrations

Update all systems using the old key:

// Update environment variable
process.env.SCORM_API_KEY = newApiKey;

// Test new key
const test = await fetch('/api/health', {
  headers: { 'X-API-Key': newApiKey }
});

Step 3: Revoke Old Key

# Via dashboard or API
DELETE /api/admin/api-keys/{old_key_id}

Step 4: Monitor

  • Check for errors from old key
  • Verify all systems using new key
  • Monitor for unauthorized access

Automated Rotation

async function rotateApiKey(tenantId: string) {
  // 1. Create new key
  const newKey = await createApiKey(tenantId, {
    name: `Rotated Key - ${new Date().toISOString()}`,
    scopes: ['read', 'write']
  });

  // 2. Update all integrations (your system)
  await updateIntegrations(newKey.id);

  // 3. Wait for propagation (e.g., 24 hours)
  await delay(24 * 60 * 60 * 1000);

  // 4. Revoke old key
  await revokeApiKey(oldKeyId);
}

Key Scopes

Available Scopes

Scope Permissions Use Case
read GET requests only Read-only access
write POST, PUT, PATCH Read and write access
admin All operations including DELETE Full access

Scope Combinations

Read-Only Access:

{
  "scopes": ["read"]
}
  • View packages, sessions, reports
  • No modifications allowed

Read-Write Access:

{
  "scopes": ["read", "write"]
}
  • Upload packages
  • Create sessions
  • Update progress
  • No deletions

Full Access:

{
  "scopes": ["read", "write", "admin"]
}
  • All operations
  • Delete packages
  • Delete sessions
  • Administrative functions

Principle of Least Privilege

Best Practice:

  • Use minimum required scopes
  • Separate keys for different purposes
  • Read-only keys for monitoring
  • Write keys for integrations
  • Admin keys only when needed

Example:

// Monitoring system - read-only
const monitoringKey = {
  scopes: ['read']
};

// Integration - read-write
const integrationKey = {
  scopes: ['read', 'write']
};

// Admin operations - full access
const adminKey = {
  scopes: ['read', 'write', 'admin']
};

Best Practices

1. Use Different Keys Per Environment

# Development
SCORM_API_KEY_DEV=scorm_test_abc123...

# Staging
SCORM_API_KEY_STAGING=scorm_test_def456...

# Production
SCORM_API_KEY_PROD=scorm_live_ghi789...

2. Name Keys Descriptively

# Good names
"Production API Key - Integration Service"
"Development API Key - Testing"
"Monitoring API Key - Read Only"

# Bad names
"Key 1"
"API Key"
"Test"

3. Monitor Key Usage

// Track API key usage
async function trackApiKeyUsage(apiKey: string) {
  const usage = await getApiKeyUsage(apiKey);
  
  // Alert on unusual activity
  if (usage.requestsPerMinute > 100) {
    alert('Unusual API key activity detected');
  }
}

4. Set Expiration Dates

// Create key with expiration
const key = await createApiKey({
  tenant_id: tenantId,
  name: 'Temporary Integration Key',
  scopes: ['read', 'write'],
  expires_in_days: 30 // Expires in 30 days
});

5. Revoke Unused Keys

// Regularly audit and revoke unused keys
async function auditApiKeys(tenantId: string) {
  const keys = await listApiKeys(tenantId);
  
  for (const key of keys) {
    const lastUsed = await getLastUsed(key.id);
    const daysSinceUse = (Date.now() - lastUsed) / (1000 * 60 * 60 * 24);
    
    if (daysSinceUse > 90) {
      await revokeApiKey(key.id);
      console.log(`Revoked unused key: ${key.name}`);
    }
  }
}

6. Implement Key Validation

// Validate API key before use
function validateApiKey(apiKey: string): boolean {
  // Check format
  if (!apiKey.startsWith('scorm_')) {
    return false;
  }
  
  // Check length
  if (apiKey.length < 64) {
    return false;
  }
  
  return true;
}

Security Checklist

  • API keys stored in environment variables
  • Keys never committed to version control
  • Different keys for each environment
  • Keys rotated every 90 days
  • Minimum required scopes used
  • Unused keys revoked
  • Key usage monitored
  • Expiration dates set
  • Keys named descriptively
  • Secret management service used

Related Documentation


Last Updated: 2025-01-15