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) 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