API Key Security

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

ScopePermissionsUse Case
readGET requests onlyRead-only access
writePOST, PUT, PATCHRead and write access
adminAll operations including DELETEFull 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