API Key Authentication
Overview
| Component | Description |
|---|---|
| Supabase | Cloud database for storing hashed API keys, revocation status and metadata |
| Crypto (Node.js) | Generates and hashes API keys securely using SHA-256 |
| LRU Cache | Stores API key validation states in memory for a specified time to reduce database lookups |
| Rate Limiter | Restricts API usage per key or IP to prevent abuse |
| Express Middleware | Authenticates requests using API keys via the abair-api-key header |
Key Generation
Purpose
Generate a unique, secure, and hashed API key for each user and store it in Supabase.
Flow
- Generate a random 32-byte API key
- Hash it using SHA-256
- Ensure the hash is unique (no collision in Supabase)
- Insert the hashed key and metadata (e.g. description, is_revoked) into the api_authentication table in Supabase
- Return the plain, unhashed API key to the user (only once)
Security Notes
- The plain api key is never stored in the database.
- Only the SHA-256 hash is stored, ensuring that even if the database is compromised, the keys remain secure.
- Key uniqueness is guarunteed by comparing hashed values in Supabase before insertion.
Middleware
Purpose
To authenticate incoming requests by verifying API keys against Supabase (with caching)
Header Example
Clients must send their key in the request header,
abair-api-key: 1e4c54e1c32a7e9bba3c4d78d71f4f35d7a4b812e73c...
Flow
- Extract API key from the
abair-api-keyheader. - Hash the key using SHA-256.
- Check the LRU cache for the hash state:
valid--> allow request.revoked--> reject immediately.
- If not cached, query Supabase for:
- Existence of the hashed key.
- Revocation status.
- Update cache accordingly and proceed or reject.
Example Responses
| Status | Message |
|---|---|
| 200 | Request proceeds normally |
| 401 | { "error": "API key missing" } |
| 403 | { "error": "Invalid API key" } or { "error": "API key revoked" } |
| 500 | { "error": "Error verifying API key" } |
Performance Optimisation - Caching
Purpose
Reduce frequent lookups to Supabase by storing recent key validation results.
Mechanism
Uses LRUCache (Least Recently Used Cache)
Default TTL: 15 Minutes (Configurable via CACHE_MINUTES in .env)
Stores either "valid" or "revoked" status per hashed key
Rate Limiting
Purpose
Protect the API services from abuse by enforcing per-key or per-IP limiting.
const apiKeyRateLimiter = rateLimit({
windowMs: rateLimitMinutes * 60 * 1000,
max: process.env.RATE_LIMIT_REQUESTS || 100,
keyGenerator: req => req.headers["abair-api-key"] || ipKeyGenerator(req),
});
Behaviour
Limits the number of requests per minute (default: 100/minute)
Tracks rate limit hits in Prometheus.
Returns 429 Too Many Requests with a retry timer.
Example Response
{
"error": "Rate limit exceeded. Please wait 2 minute(s)."
}
Max number of requests and Rate limit wait time can be toggled in .env
Revocation Endpoints
Purpose
Allow administrators or users to revoke keys securely and immediately.
Single Key Revocation
Endpoint
DELETE /apikey/revoke
Body Example
{
"apiKey": "1e4c54e1c32a7e9bba3c4d78d71f4f35d7a4b812e73c..."
}
Response Examples
| Status | Message |
|---|---|
| 200 | { "success": true, "message": "API key revoked successfully" } |
| 404 | { "error": "API key not found" } |
| 400 | { "error": "Missing apiKey in body" } |
| 500 | { error: "Database error fetching API key" } |
Revoked keys are marked in Supabase and removed from Cache.
Revoke All API-Keys (Admin-Only)
Endpoint
DELETE /apikey/revoke-all
Headers
x-admin-secret: <ADMIN_SECRET>
Response
{ "success": true, "message": "All active API keys revoked successfully" }
Security
- Requires
admin-secretmatchingprocess.env.ADMIN-SECRET. - Immediately invalidates all keys in both Supabase and Cache.
Supabase Schema
Table
api_authentication
| Column | Type | Description |
|---|---|---|
| id | INT | Unique identifer |
| hashed_key | TEXT | SHA-256 hashed API key |
| created_at | TIMESTAMPTZ | Creation date |
| is_revoked | BOOLEAN | Whether key is revoked |
| revoked_at | TIMESTAMPTZ | Revocation date |
| description | TEXT | Optional user-created identifer for key |
| last_used_at | TIMESTAMPTZ | Date key was last used |
| usage_count | INT | Number of times key has been used |
| owner | UUID | User account associated with key |
Security
-
Only hashed keys are stored.
-
Rate limiting prevents brute force attacks.
-
Cache invalidation ensures revoked keys are immediately recognised.
-
API key visibility is one-time at creation.
-
Admin-routes are protected via env-based secret.