Skip to content

feat(auth): add API key authentication with rate limiting (#594)#2

Open
fytgian wants to merge 132 commits into
mainfrom
feature/594-api-key-auth
Open

feat(auth): add API key authentication with rate limiting (#594)#2
fytgian wants to merge 132 commits into
mainfrom
feature/594-api-key-auth

Conversation

@fytgian

@fytgian fytgian commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

Closes Vera3289#594

Full API key lifecycle: generation, validation, per-key rate limiting, rotation, revocation, and usage tracking — integrated into the existing auth middleware.

Changes

New files

  • api/services/apiKeyService.js — Core service: generates psk_ prefixed keys (32-byte CSPRNG), stores SHA-256 hashes (never raw keys), validates, enforces per-key sliding-window rate limits, supports rotation (revoke + re-issue) and revocation by id, tracks last-used timestamp and request counts
  • api/routes/apiKeys.js — REST interface for key management with swagger docs

Modified files

  • api/middleware/auth.js — X-API-Key path now goes through apiKeyService.validateKey() + checkRateLimit(); returns 429 on rate limit breach; JWT path unchanged
  • api/server.js — Mounts /v1/api/api-keys router

API Endpoints

Method Path Description
POST /v1/api/api-keys Generate new key (raw key shown once)
GET /v1/api/api-keys List owner's keys (metadata only)
POST /v1/api/api-keys/:id/rotate Rotate key (revoke old, issue new)
DELETE /v1/api/api-keys/:id Revoke key
GET /v1/api/api-keys/:id/usage Usage stats for key

Acceptance Criteria

Criterion Status
API key generation psk_ prefix + 32-byte random, CSPRNG
API key validation ✅ Hash-based lookup in validateKey()
Rate limiting per key ✅ Sliding window, configurable windowMs + max per key
Key rotation rotateKey() — revokes old, creates new with same owner/name
Key revocation revokeById() + DELETE /api-keys/:id
Usage tracking ✅ Per-window request count + lastUsedAt timestamp
Admin interface ✅ All endpoints behind authMiddleware; ownerId scopes listing

Security notes

  • Raw keys are never stored; only SHA-256 hashes
  • Keys are shown exactly once at creation/rotation
  • Invalid/revoked keys return 401; rate-limited keys return 429

fytgian and others added 30 commits June 26, 2026 16:08
- apiKeyService: key generation (psk_ prefix, 32-byte random), SHA-256
  hashing, validation, per-key sliding-window rate limiting, rotation
  (revoke + re-issue), revocation by id, usage tracking, owner listing
- apiKeys router: POST /api-keys (create), GET /api-keys (list),
  POST /api-keys/:id/rotate, DELETE /api-keys/:id (revoke),
  GET /api-keys/:id/usage — all with swagger JSDoc
- auth middleware: replaced env-var key list with apiKeyService validation
  + per-key rate limit check (429 on breach); JWT path unchanged
- server.js: mount /v1/api/api-keys router
- POST /v1/api/streams/:stream_id/top-up accepts employer + amount
- Validates stream exists via get_stream contract call
- Rejects top-up if stream status is not Active
- Calls top_up(employer, stream_id, amount) on Soroban contract
- Invalidates stream cache after successful top-up
- Full input validation (stream_id, employer address, amount > 0)
- Swagger JSDoc documented
- templateService: in-memory CRUD, createFromStream (strips employer/employee),
  share toggle, owner-scoped list that includes shared+default templates,
  seeded default templates (Monthly Salary USDC, Weekly Contractor USDC)
- streamTemplates router: POST /stream-templates (create), GET (list),
  GET /:id, PATCH /:id (update), DELETE /:id, POST /:id/share,
  POST /:id/create-stream (instantiate with optional overrides)
- server.js: mount /v1/api/stream-templates behind authMiddleware
- Add services/balance/balance.js with calculateBalance() and calculateBalanceByDuration()
- Account for pause periods via calcPausedSeconds()
- Support daily, weekly, monthly, and custom duration types
- Handle leap years and DST via UTC Date arithmetic
- Handle edge cases: cliff, stop_time, cancelled/exhausted streams, partial periods
- Add 16 unit tests covering all acceptance criteria

Closes Vera3289#498
- Add MigrationRunner class with versioned up/down migrations
- Before/after validation hooks per migration step
- Rollback support (roll back newest-first to target version)
- Append-only audit trail (file + DB migration_history table)
- User notification hooks (migration_started/success/failed/rollback_*)
- Zero-downtime design: migrations operate on data arrays, no table locks
- Example migration: 0002_add_cliff_delegate.js
- 7 unit tests covering all acceptance criteria

Closes Vera3289#505
- Add scripts/deploy-rollback.sh: automated blue-green rollback
  - Health check (configurable retries/interval) before traffic switch
  - Automatic slot calculation (blue↔green)
  - Deployment history tracking (deployment-history.json)
  - Rollback notification webhook support
  - Dry-run mode for testing
- Add .github/workflows/deploy-rollback.yml: manual + automated trigger
  - Callable from other workflows on health-check failure
  - Uploads deployment history as artifact
- Add services/deployment/rollback.test.js: 10 unit tests
  - Slot calculation, health check retry logic, history recording

Closes Vera3289#513
- Add SnapshotStore class with configurable interval and retention
- checkpoint(): periodic snapshot (skips if interval not elapsed)
- forceCheckpoint(): on-demand snapshot for withdrawals/status changes
- query(): historical query with optional time range filter
- queryAt(): point-in-time recovery lookup (most recent snapshot ≤ ts)
- cleanup(): prune snapshots older than a given timestamp
- MemoryStorage backend (swap for DB/file backend in production)
- Compact snapshot format: only 7 essential fields (minimal overhead)
- 11 unit tests covering all acceptance criteria

Closes Vera3289#503
- Emit dedicated paused/resumed/cancelled events with employer address
  for off-chain indexing
- Remove generic stream_status_changed in favour of typed event fns
- Add edge-case tests: double-pause, resume-active, wrong-employer auth,
  cancel-while-paused payout
- All events carry complete state for off-chain indexing:
  stream_created includes token, deposit, rate, stop_time
  withdrawn includes total_withdrawn cumulative
  topped_up includes new total deposit
  stream_cancelled includes employee_payout and employer_refund
- Dedicated typed events per operation (no generic status event)
- Consistent topic convention: (event_name, stream_id)
- Add per-user and global stream creation rate limits with sliding window
- Configurable limits and window duration via admin functions
- Admin can exempt trusted addresses from rate limits
- Add DataKey variants: UserRateCount, UserRateWindow, GlobalRateCount,
  GlobalRateWindow, RateLimitUser, RateLimitGlobal, RateLimitWindow,
  RateLimitExempt
- Add ERR_RATE_LIMIT (E005) error code
- Fix pre-existing: define StreamParams, add Vec import, fix claimable_at,
  add locked field to batch stream creation, add MinDeposit DataKey
- Tests: user limit exceeded, window reset, exempt bypass
- Multi-stage Dockerfile: base, builder (WASM), test, dev
- Dev stage installs cargo-watch for hot-reload on source changes
- docker-compose: build, test, and dev services
- dev service mounts source + two cache volumes (registry, target)
- .dockerignore excludes target/, .git/, *.wasm, docs to minimise
  build context sent to the daemon
- Add balance.rs with BalanceSnapshot contracttype
- Expose balance_snapshot() on StreamContract
- Account for paused state, stop_time cap, vested/unvested split
- Add remaining_duration(), daily_rate(), monthly_rate() helpers
- Fix duplicate MinDeposit variant in DataKey enum
- Fix missing pending-admin imports in lib.rs
- Unit tests for all calculation paths
- Add services/cache/ crate with CacheClient backed by ConnectionManager
- TTL config per data type: stream=30s, user_stats=60s, token_info=300s
- Cache key strategy: ps:stream:{id}, ps:user:{addr}:stats, ps:token:{addr}
- Cache-aside get_or_load() helper with transparent fallback on Redis errors
- Cache invalidation via invalidate() on updates
- Hit/miss metrics via atomic counters
- Cache warming on startup via warm_streams()
- Unit tests for key helpers and metrics
- Add services/search/ crate (paystream-search)
- StreamFilter: recipient substring, token, status, amount range, date range
- Combined filters, pagination (page/page_size), sorting (id/deposit/start_time/rate)
- search() returns PagedResult with total, page, total_pages
- Case-insensitive recipient matching
- Tests: each filter type, combined filters, sorting, pagination
- Add services/pool/ crate (paystream-pool)
- build_pg_pool(): deadpool-postgres with configurable max_size, wait/create/recycle timeouts
- build_redis_pool(): deadpool-redis with same PoolConfig
- PoolConfig struct with sensible defaults (max=16, timeouts 5-10s)
- pg_metrics() / redis_metrics() for monitoring pool size/available/waiting
- Pools are lazy (no connection until first get()) — safe to build at startup
- Unit tests for config and pool construction
Vera3289 added 30 commits June 27, 2026 12:10
…ance-calculation-service

feat: implement stream balance calculation service
…-docs

Write Database Schema Documentation
…ncellation-endpoint

Create Stream Cancellation Endpoint
…recovery

Implement Disaster Recovery Plan
…ats-service

Build Stream Stats/Aggregation Service
…-forum

Create Community Forum/Discussion Board
…-dashboard

Add Analytics and Reporting Dashboard
…tion-sanitization

Add Data Validation and Sanitization
…trics-monitoring

Add Contract Metrics/Monitoring
…eckpoint

feat: implement snapshot/checkpoint system (Vera3289#503)
…rollback

feat: create deployment rollback strategy (Vera3289#513)
…culation

feat: implement balance calculation logic (Vera3289#498)
…ates

feat(templates): implement stream template system (Vera3289#596)
feat(streams): implement stream top-up endpoint (Vera3289#595)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add API Key Authentication