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) orscorm_test_(test) - Length: 64+ characters
- Format: Base64-encoded random bytes
Creating API Keys
Via Dashboard
- Sign in to SCORM API dashboard
- Navigate to Dashboard → API Keys
- Click "Create API Key"
- Configure:
- Name: Descriptive name (e.g., "Production API Key")
- Scopes: Select permissions (read, write, admin)
- Expires In (optional): Days until expiration
- Click "Create"
- 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
- Security Overview - Complete security guide
- Data Isolation - Tenant isolation
- Authentication Guide - Auth details
Last Updated: 2025-01-15