Authentication & Authorization
Comprehensive guide to Prism's API key authentication, rate limiting, and quota management.
Table of Contents
Overview
Prism provides a complete authentication and authorization system with:
API Key Authentication: SHA-256 hashed keys with SQLite storage
Rate Limiting: Token bucket algorithm with configurable limits
Method Permissions: Restrict access to specific RPC methods
Daily Quotas: Request count limits with automatic reset
In-Memory Caching: 60-second cache for authentication checks
Architecture
┌──────────────┐
│ HTTP │
│ Request │
└──────┬───────┘
│
▼
┌────────────────────────────────────────┐
│ API Key Middleware │
│ │
│ 1. Extract API key (header/query) │
│ 2. Check in-memory cache (60s TTL) │
│ 3. Validate against SQLite DB │
│ 4. Check quotas & expiration │
│ 5. Verify method permissions │
└──────┬─────────────────────────────────┘
│
├─ Valid ──────────► Process Request
│
└─ Invalid ────────► 401 UnauthorizedAPI Key Management
Enabling Authentication
[auth]
enabled = true
database_url = "sqlite://db/auth.db"When enabled, all requests require a valid API key.
Creating API Keys
Use the CLI to create and manage keys:
cargo run --bin cli -- auth create \
--name "production-api" \
--description "Main production service" \
--rate-limit 100 \
--refill-rate 10 \
--daily-limit 100000 \
--expires-in-days 365 \
--methods "eth_getLogs,eth_getBlockByNumber,eth_getTransactionReceipt"Parameters:
--name
Unique identifier for the key
production-api
--description
Human-readable description
Main production service
--rate-limit
Max tokens in bucket
100
--refill-rate
Tokens added per second
10
--daily-limit
Max requests per day
100000
--expires-in-days
Days until expiration
365
--methods
Comma-separated allowed methods
eth_getLogs,eth_blockNumber
Output:
API key created successfully!
Name: production-api
API Key: rpc_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6
Description: Main production service
Rate Limit: 100 requests/sec (refill: 10/sec)
Daily Limit: 100,000 requests
Expires: 2025-12-03
Allowed Methods:
- eth_getLogs
- eth_getBlockByNumber
- eth_getTransactionReceipt
IMPORTANT: Store this API key securely. It will not be shown again.Listing API Keys
cargo run --bin cli -- auth listOutput:
API Keys:
1. production-api
Created: 2024-12-03
Expires: 2025-12-03
Status: Active
Rate Limit: 100/sec
Daily Limit: 100,000
Methods: eth_getLogs, eth_getBlockByNumber, eth_getTransactionReceipt
2. development-key
Created: 2024-12-01
Expires: 2024-12-31
Status: Active
Rate Limit: 10/sec
Daily Limit: 10,000
Methods: All
3. expired-key
Created: 2024-01-01
Expires: 2024-06-01
Status: Expired
Rate Limit: 50/sec
Daily Limit: 50,000Revoking API Keys
# Revoke by name
cargo run --bin cli -- auth revoke --name "production-api"
# Revoke by ID
cargo run --bin cli -- auth revoke --id 123Using API Keys
Include the API key in requests:
Method 1: Header (Recommended)
curl -X POST http://localhost:3030/ \
-H "Content-Type: application/json" \
-H "X-API-Key: rpc_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'Method 2: Query Parameter
curl -X POST "http://localhost:3030/?api_key=rpc_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'API Key Format
API keys follow the format: rpc_<32-alphanumeric-characters>
Prefix:
rpc_Total Length: 36 characters
Random Portion: 32 alphanumeric characters (A-Z, a-z, 0-9)
Example: rpc_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6
Storage: Keys are hashed using Argon2id before storage. A SHA-256 blind index enables O(1) database lookups. The original key is never stored.
Rate Limiting
Prism implements a token bucket rate limiter per API key.
How It Works
Token Bucket Algorithm:
Each API key has a bucket with
max_tokensBucket refills at
refill_ratetokens/secondEach request consumes 1 token
Request allowed if tokens ≥ 1
Request rejected if tokens < 1
Configuration
cargo run --bin cli -- auth create \
--name "rate-limited-key" \
--rate-limit 100 \ # Bucket capacity: 100 tokens
--refill-rate 10 # Refill: 10 tokens/secondExample:
Bucket size: 100 tokens
Refill rate: 10 tokens/second
Sustainable rate: 10 requests/second
Burst capacity: 100 requests (then throttled to 10/sec)
Rate Limit Behavior
Scenario: Burst then sustain
Time 0s: 100 tokens available
→ Send 100 requests (burst)
→ 0 tokens remaining
Time 1s: 10 tokens refilled
→ Send 10 requests
→ 0 tokens remaining
Time 2s: 10 tokens refilled
→ Send 10 requests
→ 0 tokens remainingResult:
First second: 100 requests accepted
Subsequent seconds: 10 requests/second sustained
Rate Limit Exceeded Response
{
"jsonrpc": "2.0",
"error": {
"code": -32053,
"message": "Rate limit exceeded",
"data": "Retry after 1 second"
},
"id": 1
}HTTP Status: 429 Too Many Requests
Rate Limit Headers
Prism includes rate limit information in response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 85
X-RateLimit-Reset: 1638360000X-RateLimit-Limit
Maximum requests per window
X-RateLimit-Remaining
Tokens remaining
X-RateLimit-Reset
Unix timestamp when limit resets
Method-Level Permissions
Restrict API keys to specific RPC methods.
Configuration
Allow specific methods:
cargo run --bin cli -- auth create \
--name "logs-only-key" \
--methods "eth_getLogs"Allow multiple methods:
cargo run --bin cli -- auth create \
--name "read-only-key" \
--methods "eth_getLogs,eth_getBlockByNumber,eth_getTransactionReceipt,eth_blockNumber"Allow all methods (omit --methods or use "all"):
cargo run --bin cli -- auth create \
--name "admin-key" \
--methods "all"Permission Check
When a request arrives:
Extract
methodfrom requestCheck if key has permission
Allow or deny
Example:
# API key only has permission for eth_getLogs
X-API-Key: prism_logs_only_key
# This succeeds
{"method": "eth_getLogs", ...}
# This fails with 403 Forbidden
{"method": "eth_getBlockByNumber", ...}Permission Denied Response
{
"jsonrpc": "2.0",
"error": {
"code": -32055,
"message": "Method not allowed",
"data": "API key does not have permission for method: eth_getBlockByNumber"
},
"id": 1
}HTTP Status: 403 Forbidden
Use Cases
Read-only access:
--methods "eth_getLogs,eth_getBlockByNumber,eth_getTransactionReceipt,eth_blockNumber,eth_getBalance"Logs-only access (for event indexers):
--methods "eth_getLogs,eth_blockNumber"Admin access (all methods):
--methods "all"Quota Management
Daily request quotas with automatic midnight reset.
Configuration
cargo run --bin cli -- auth create \
--name "quota-limited-key" \
--daily-limit 100000 # 100,000 requests per dayQuota Tracking
Quota State:
requests_made_today: Current request countquota_reset_at: Midnight UTC when quota resetsdaily_request_limit: Maximum allowed requests
Quota Check:
Is current time >
quota_reset_at?YES: Reset
requests_made_todayto 0, updatequota_reset_atto next midnight
Is
requests_made_today<daily_request_limit?YES: Allow request, increment counter
NO: Deny request
Quota Exceeded Response
{
"jsonrpc": "2.0",
"error": {
"code": -32056,
"message": "Quota exceeded",
"data": "Daily limit of 100000 requests exceeded. Quota resets at 2024-12-04T00:00:00Z"
},
"id": 1
}HTTP Status: 429 Too Many Requests
Quota Headers
X-Quota-Limit: 100000
X-Quota-Remaining: 45230
X-Quota-Reset: 2024-12-04T00:00:00ZSecurity Best Practices
1. Secure API Key Storage
DON'T:
Store keys in version control
Hardcode keys in source code
Share keys between environments
DO:
Use environment variables
Use secret management systems (AWS Secrets Manager, HashiCorp Vault)
Rotate keys regularly
Example (Node.js):
// DON'T
const apiKey = "rpc_A1b2C3d4E5f6...";
// DO
const apiKey = process.env.PRISM_API_KEY;2. Use Method Restrictions
Principle of least privilege: grant only necessary methods.
# Good: Restrict to needed methods
cargo run --bin cli -- auth create \
--name "frontend-app" \
--methods "eth_getLogs,eth_getBlockByNumber"
# Bad: All methods when not needed
cargo run --bin cli -- auth create \
--name "frontend-app" \
--methods "all"3. Set Appropriate Rate Limits
Balance performance and cost:
# Conservative (low traffic)
--rate-limit 10 --refill-rate 1
# Moderate (typical app)
--rate-limit 100 --refill-rate 10
# Aggressive (high traffic)
--rate-limit 1000 --refill-rate 1004. Monitor Usage
Regularly review API key usage:
# Check metrics
curl http://localhost:3030/metrics | grep auth
# Example metrics:
# rpc_auth_success_total{key_id="production-api"} 125000
# rpc_auth_failure_total{key_id="unknown"} 45
# rpc_auth_quota_exceeded_total{key_id="production-api"} 35. Rotate Keys Regularly
# Create new key
cargo run --bin cli -- auth create --name "production-api-v2" ...
# Update applications to use new key
# Revoke old key after transition
cargo run --bin cli -- auth revoke --name "production-api"6. Use HTTPS in Production
DON'T:
# Sends API key in plaintext
http://prism.example.com/?api_key=prism_...DO:
# Encrypted connection
https://prism.example.com/
Header: X-API-Key: prism_...7. Implement Retry Logic
Handle rate limit and quota errors gracefully:
async function makeRequest(payload, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch('https://prism.example.com/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.PRISM_API_KEY
},
body: JSON.stringify(payload)
});
if (response.status === 429) {
// Rate limited, exponential backoff
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return await response.json();
}
throw new Error('Max retries exceeded');
}Database Schema
API keys are stored in SQLite with the following schema:
CREATE TABLE api_keys (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key_hash TEXT NOT NULL, -- Argon2id hash (PHC format)
blind_index TEXT NOT NULL UNIQUE, -- SHA-256 hash for O(1) lookup
name TEXT NOT NULL UNIQUE,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT 1,
created_at TEXT NOT NULL, -- ISO 8601 datetime
updated_at TEXT NOT NULL, -- ISO 8601 datetime
last_used_at TEXT, -- ISO 8601 datetime
expires_at TEXT, -- ISO 8601 datetime
rate_limit_max_tokens INTEGER NOT NULL,
rate_limit_refill_rate INTEGER NOT NULL,
daily_request_limit INTEGER,
daily_requests_used INTEGER DEFAULT 0,
quota_reset_at TEXT -- ISO 8601 datetime
);
CREATE TABLE method_permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
api_key_id INTEGER NOT NULL,
method_name TEXT NOT NULL,
max_requests_per_day INTEGER, -- Per-method daily limit (NULL = unlimited)
requests_today INTEGER DEFAULT 0,
FOREIGN KEY (api_key_id) REFERENCES api_keys(id)
);
CREATE INDEX idx_blind_index ON api_keys(blind_index);
CREATE INDEX idx_active_keys ON api_keys(is_active);
CREATE INDEX idx_method_permissions_api_key ON method_permissions(api_key_id);Security Notes:
key_hash: Argon2id with memory=64MB, iterations=3, parallelism=4blind_index: SHA-256 for timing-attack resistant O(1) lookupsThe original API key is never stored
Metrics
Authentication metrics are exposed at /metrics:
# Authentication attempts
rpc_auth_success_total{key_id="production-api"} 125000
rpc_auth_failure_total{key_id="unknown"} 45
# Cache performance
rpc_auth_cache_hits_total 120000
rpc_auth_cache_misses_total 5045
# Rate limiting
rpc_rate_limit_allowed_total{key="production-api"} 124500
rpc_rate_limit_rejected_total{key="production-api"} 500
# Quotas
rpc_auth_quota_exceeded_total{key_id="production-api"} 3
# Method permissions
rpc_auth_method_denied_total{key_id="logs-only",method="eth_getBlockByNumber"} 12Next: Learn about Monitoring for comprehensive observability.
Last updated