API Key Lifecycle Management¶
Overview¶
API keys authenticate edge collectors to the central server. This document defines the complete lifecycle: generation, provisioning, rotation, revocation, and compromise recovery.
Key Format¶
Example: alpr_7Kx9mPqR2vNwYzA1bCdEfGhIjKlMnOpQ3rStUvWx
| Property | Value |
|---|---|
| Prefix | alpr_ (identifies key type) |
| Random portion | 32 bytes (256 bits) |
| Encoding | Base64URL (URL-safe) |
| Total length | ~49 characters |
| Entropy | 256 bits (cryptographically secure) |
Key Generation¶
Keys are generated exclusively on the central server using cryptographically secure randomness.
Design Decisions:
- 256 bits of entropy (immune to brute force)
- Base64URL encoding (URL-safe, no special characters)
- alpr_ prefix for identification
- SHA-256 hashing for storage (fast lookup, no brute force risk due to high entropy)
Why SHA-256 instead of bcrypt/argon2? - API keys have 256 bits of entropy (not low-entropy passwords) - Need fast validation on every request (~1ms not ~100ms) - No salt needed (key itself is random)
Implementation Details: See API Key PRP for generation functions, hashing patterns, and format validation.
Schema¶
CREATE TABLE api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
collector_id UUID NOT NULL REFERENCES collectors(id),
key_hash CHAR(64) NOT NULL, -- SHA-256 hash
key_prefix VARCHAR(10) NOT NULL, -- First 10 chars for identification
name VARCHAR(100), -- Optional label
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
created_by UUID NOT NULL,
expires_at TIMESTAMPTZ, -- NULL = no expiration
last_used_at TIMESTAMPTZ,
last_used_ip VARCHAR(45),
revoked_at TIMESTAMPTZ,
revoked_by UUID,
revoke_reason TEXT
);
CREATE INDEX idx_api_keys_hash ON api_keys (key_hash) WHERE is_active = true;
CREATE INDEX idx_api_keys_collector ON api_keys (collector_id);
Key Provisioning¶
New Collector Registration¶
- Admin creates collector in UI
- System generates API key
- Key displayed once to admin
- Admin securely transfers key to edge collector
- Only hash stored in database
Admin UI Flow¶
[Create Collector] → [Generate Key] → [Display Key Once] → [Confirm Copied]
↓
"This key will not be shown again.
Copy it now and store securely."
Secure Delivery Options¶
| Method | Security | Convenience |
|---|---|---|
| Manual entry | High | Low |
| Encrypted email | Medium | Medium |
| Provisioning token | High | High |
Provisioning Token (Recommended)¶
For automated edge setup:
CREATE TABLE provisioning_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
collector_id UUID NOT NULL REFERENCES collectors(id),
token_hash CHAR(64) NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
created_by UUID NOT NULL
);
Flow:
1. Admin generates one-time provisioning token (valid 1 hour)
2. Token sent to person setting up edge collector
3. Edge calls POST /api/v1/provision with token
4. Server returns API key (used only once)
5. Edge stores API key locally
POST /api/v1/collectors/provision
{
"provisioning_token": "prov_xyz..."
}
Response:
{
"collector_id": "col_abc123",
"api_key": "alpr_7Kx9mP...", // Only time key is transmitted
"central_url": "https://alpr.example.com"
}
Key Storage¶
Central Server¶
| Data | Storage |
|---|---|
| Full key | Never stored |
| Key hash | api_keys.key_hash |
| Key prefix | api_keys.key_prefix (for UI identification) |
Edge Collector¶
Options (in order of preference):
-
Environment variable
-
Encrypted config file
-
System keyring (if available on Pi)
Never: - Commit to git - Log in plaintext - Transmit unencrypted
Key Validation¶
Every edge-to-central request requires API key validation via the X-API-Key header.
Validation Checks¶
- Key format is valid (
alpr_prefix, correct length) - Key hash exists in database
- Key is active (
is_active = true) - Key not expired (
expires_atis null or future) - Associated collector is active
Usage Tracking¶
On successful validation:
- last_used_at timestamp updated
- last_used_ip address recorded
- Enables stale key detection and audit
Revoked Key Detection¶
Attempts to use revoked keys are logged and can trigger alerts for security monitoring.
Implementation Details: See API Key PRP for validation middleware, FastAPI dependency injection, and revoked key handling.
Key Rotation¶
When to Rotate¶
| Trigger | Action |
|---|---|
| Scheduled (annual) | Proactive rotation |
| Personnel change | Rotate keys they had access to |
| Suspected compromise | Immediate rotation |
| Edge device replacement | New key for new device |
Rotation Process¶
- Generate new key for collector
- Grace period begins - both old and new keys valid
- Update edge collector with new key
- Verify new key works - edge sends heartbeat
- Deactivate old key after confirmation
Grace Period¶
Default: 24 hours (configurable)
-- During rotation, collector has two active keys
SELECT * FROM api_keys
WHERE collector_id = 'col_abc123'
AND is_active = true;
-- Returns both old and new key records
API Endpoints¶
POST /api/v1/collectors/{id}/rotate-key
{
"grace_period_hours": 24
}
Response:
{
"new_key": "alpr_newKey...",
"old_key_expires_at": "2025-01-16T12:00:00Z",
"message": "Update edge collector within 24 hours"
}
Edge Update Methods¶
- Manual - Admin updates edge config, restarts service
- Command polling - Central sends
config_updatecommand via heartbeat - Provisioning endpoint - Edge re-provisions with token
Key Revocation¶
When to Revoke¶
- Suspected key compromise
- Collector decommissioning
- Lost/stolen edge device
- Personnel changes (if key was exposed)
Effects of Revocation¶
- All requests with revoked key return
401 Unauthorized - Edge collector cannot upload events (buffered locally)
- Edge collector cannot send heartbeat
- Revocation is immediate (no grace period)
Monitoring¶
Attempts to use revoked keys are: - Logged with full context (key prefix, collector ID, source IP) - Can trigger security alerts - Tracked for incident investigation
Implementation Details: See API Key PRP for revocation functions and monitoring patterns.
Compromise Recovery¶
Detection Indicators¶
| Indicator | Severity |
|---|---|
| Key used from unexpected IP | Medium |
| Key used from unexpected geography | High |
| Unusual request volume | Medium |
| Requests outside normal hours | Low |
| Multiple collectors using same key | Critical |
Response Procedure¶
Immediate (< 15 minutes):
- Revoke compromised key
- Alert security team
- Note timestamp of revocation
Short-term (< 1 hour):
- Generate new key for collector
- Provision new key to edge (if device not compromised)
- Verify new key works
Investigation (< 24 hours):
- Identify compromise window (first suspicious activity → revocation)
- Audit all requests during window:
- Check for data exfiltration or manipulation
- Review how key was compromised
Documentation:
- Create incident report
- Update procedures if gap identified
- Notify stakeholders if required
Incident Report Template¶
## API Key Compromise Incident
**Date:** 2025-01-15
**Key ID:** key_xyz (prefix: alpr_7Kx9)
**Collector:** col_abc123 (Main Street Camera)
### Timeline
- HH:MM - First suspicious activity detected
- HH:MM - Key revoked
- HH:MM - New key provisioned
- HH:MM - Normal operations restored
### Impact
- Events during window: X
- Requests from unauthorized IP: Y
- Data integrity: [Verified/Compromised]
### Root Cause
[How was the key compromised?]
### Remediation
[What was done to fix and prevent recurrence?]
Monitoring¶
Metrics¶
| Metric | Description |
|---|---|
api_key_requests_total |
Requests per key |
api_key_auth_failures_total |
Failed auth attempts |
api_key_revoked_usage_total |
Attempts with revoked keys |
api_key_last_used_age_seconds |
Time since key last used |
Alerts¶
| Alert | Condition | Severity |
|---|---|---|
| Revoked key usage | Any attempt | High |
| Unknown IP for key | New IP not in allowlist | Medium |
| Key unused 7+ days | Active collector silent | Medium |
| Auth failure spike | >10 failures in 5 min | High |
| Key expires soon | <7 days until expiration | Low |
Dashboard Panels¶
- Active keys by collector
- Key age distribution
- Last used times
- Failed authentication attempts
- Revoked key usage attempts
Admin UI Features¶
Key Management Page¶
Collector: Main Street Camera (col_abc123)
API Keys:
┌─────────────────┬────────────┬──────────────────┬──────────┐
│ Key Prefix │ Status │ Last Used │ Actions │
├─────────────────┼────────────┼──────────────────┼──────────┤
│ alpr_7Kx9... │ Active │ 2 minutes ago │ [Revoke] │
│ alpr_mNop... │ Revoked │ 3 days ago │ [View] │
└─────────────────┴────────────┴──────────────────┴──────────┘
[+ Generate New Key] [Rotate Key]
Key Generation Modal¶
┌────────────────────────────────────────────────┐
│ New API Key Generated │
├────────────────────────────────────────────────┤
│ │
│ alpr_7Kx9mPqR2vNwYzA1bCdEfGhIjKlMnOpQ3rStUvWx │
│ │
│ ⚠️ This key will not be shown again. │
│ Copy it now and store securely. │
│ │
│ [Copy to Clipboard] │
│ │
│ [ ] I have copied and saved this key │
│ │
│ [Close] │
└────────────────────────────────────────────────┘
Security Checklist¶
Before deploying:
- [ ] Keys generated with cryptographically secure random
- [ ] Keys stored as SHA-256 hash only
- [ ] Keys never logged in plaintext
- [ ] Keys transmitted only over TLS
- [ ] Revocation takes effect immediately
- [ ] Failed auth attempts are logged
- [ ] Revoked key usage triggers alert
- [ ] Key rotation procedure tested
- [ ] Compromise response procedure documented
- [ ] Admin UI requires MFA for key operations
Decision Date: 2025-12-29 Status: Approved Rationale: Comprehensive key lifecycle management essential for edge-to-central security