API authentication as a service. Issue, verify, and track API keys and service identities — without building any of it yourself.
# Verify an external API key
POST /v1/verify
Authorization: Bearer sk_live_xxxxx
→ { "valid": true, "projectId": "...", "rateLimitRemaining": 98 }
# Verify a service-to-service call
POST /v1/verify/service
Authorization: Bearer svc_live_xxxxx
→ { "valid": true, "projectId": "...", "service": { "id": "...", "name": "billing-service" } }
Every backend eventually needs the same boring stuff: key generation, secure storage, revocation, rate limiting, usage logs. Most teams build it from scratch — and most teams build it wrong.
Elyzor handles all of it so you don't have to.
| Feature | Roll your own | Elyzor |
|---|---|---|
| Key generation & hashing | Manual | ✅ Built-in |
| Rate limiting | Redis setup required | ✅ Built-in |
| Key revocation | Custom logic | ✅ One API call |
| Usage tracking | Build from scratch | ✅ Automatic |
| Verification latency | Varies | ✅ <5ms target |
| Service-to-service auth | Roll your own | ✅ Built-in |
Alternatives like Unkey exist in this space — Elyzor's differentiator is its open-source, self-hostable architecture designed for teams who don't want their auth layer locked behind a third-party SaaS.
Elyzor manages two distinct credential types that never cross:
For external clients authenticating against your API.
Client → Your API → POST /v1/verify (sk_live_...) → Elyzor
For internal microservices authenticating with each other.
billing-service → order-service → POST /v1/verify/service (svc_live_...) → Elyzor
Elyzor answers only "is this credential valid?" — it never sits in the request path.
git clone https://github.com/your-username/elyzor
cd elyzor
npm installcp .env.example .envEdit .env:
PORT=3000
MONGO_URI=mongodb://localhost:27017/elyzor
REDIS_URL=redis://localhost:6379
JWT_SECRET=change_me_in_productiondocker compose up -d # starts MongoDB + Redis
npm run dev # starts Elyzor on :3000curl -X POST http://localhost:3000/v1/auth/register \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com", "password": "yourpassword" }'
curl -X POST http://localhost:3000/v1/auth/login \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com", "password": "yourpassword" }'
curl -X POST http://localhost:3000/v1/projects \
-H "Authorization: Bearer <your_jwt>" \
-H "Content-Type: application/json" \
-d '{ "name": "my-api" }'# Issue an API key (for external clients)
curl -X POST http://localhost:3000/v1/projects/<projectId>/keys \
-H "Authorization: Bearer <your_jwt>" \
-H "Content-Type: application/json"
# → { "key": "sk_live_xxxxx", ... }
# Register a service identity (for internal microservices)
curl -X POST http://localhost:3000/v1/projects/<projectId>/services \
-H "Authorization: Bearer <your_jwt>" \
-H "Content-Type: application/json" \
-d '{ "name": "billing-service" }'
# → { "key": "svc_live_xxxxx", ... }
# Save both keys — they won't be shown again.# Verify an API key (from your protected API)
curl -X POST http://localhost:3000/v1/verify \
-H "Authorization: Bearer sk_live_xxxxx"
# → { "valid": true, "projectId": "...", "rateLimitRemaining": 98 }
# Verify a service identity (from your internal service)
curl -X POST http://localhost:3000/v1/verify/service \
-H "Authorization: Bearer svc_live_xxxxx"
# → { "valid": true, "projectId": "...", "service": { "id": "...", "name": "billing-service" } }Success (200)
{ "valid": true, "projectId": "64f1a...", "rateLimitRemaining": 98 }Invalid key (401) { "valid": false, "error": "invalid_key" }
Revoked key (403) { "valid": false, "error": "key_revoked" }
Rate limit (429) { "valid": false, "error": "rate_limit_exceeded", "retryAfter": 42 }
Success (200)
{
"valid": true,
"projectId": "64f1a...",
"service": { "id": "...", "name": "billing-service" },
"rateLimitRemaining": 98
}Invalid key (401) { "valid": false, "error": "invalid_key" }
Revoked service (403) { "valid": false, "error": "service_revoked" }
Rate limit (429) { "valid": false, "error": "rate_limit_exceeded", "retryAfter": 42 }
# Auth
POST /v1/auth/register
POST /v1/auth/login
POST /v1/auth/refresh
POST /v1/auth/logout
POST /v1/auth/logout-all
GET /v1/auth/me
# Projects
GET /v1/projects
POST /v1/projects
DELETE /v1/projects/:id # cascade: deletes keys, services, usage logs
# API Keys
GET /v1/projects/:projectId/keys
POST /v1/projects/:projectId/keys
DELETE /v1/projects/:projectId/keys/:keyId
POST /v1/projects/:projectId/keys/:keyId/rotate
# Services
GET /v1/projects/:projectId/services
POST /v1/projects/:projectId/services
DELETE /v1/projects/:projectId/services/:serviceId
POST /v1/projects/:projectId/services/:serviceId/rotate
# Stats
GET /v1/projects/:projectId/stats?range=7d
All healthy (200)
{ "status": "ok", "mongo": "ok", "redis": "ok" }Degraded (503)
{ "status": "degraded", "mongo": "ok", "redis": "error" }Success (200)
{
"totalRequests": 1204,
"successRate": 0.97,
"topKeys": [{ "keyId": "...", "requests": 842 }],
"requestsByDay": [{ "date": "2026-03-01", "count": 310, "errors": 9 }],
"rateLimitHits": 12,
"avgLatencyMs": 3.2
}Supports range query param: 1d, 7d (default), 30d.
┌──────────────┐
Client ───────▶ │ Elyzor API │
└──────┬───────┘
│
┌──────────────┴──────────────┐
│ │
MongoDB Redis Cache
(metadata & logs) (verification cache,
rate limiting)
Stack: Node.js · Express · MongoDB · Redis · Docker
- Credentials are never stored in plaintext — only SHA-256 hashes
- Constant-time comparison during verification (prevents timing attacks)
sk_live_andsvc_live_prefixes are mutually exclusive — a service key cannot verify as an API key and vice versa- Immediate revocation — revoked credentials fail on next request
- Multi-layer rate limiting — IP-based (on
/register,/login,/verify) + per-key/service - JWT algorithm pinned to HS256 — algorithm confusion attacks prevented
- Refresh token rotation — each
/refreshissues a new token and invalidates the old one - Token theft detection — using a revoked refresh token triggers full session wipe
- Production startup validation — missing
JWT_SECRET,MONGO_URI, orREDIS_URLcrashes the process before accepting traffic - Request payload capped at 16kb;
Authorizationheader capped at 200 characters - Request ID tracing — every request carries an
X-Request-Idheader (generated or propagated) for log correlation
elyzor/
├── src/
│ ├── auth/
│ ├── users/
│ ├── projects/
│ ├── apikeys/ # sk_live_ credentials (external clients)
│ ├── services/ # svc_live_ credentials (internal microservices)
│ ├── verification/ # POST /v1/verify
│ ├── verify-service/ # POST /v1/verify/service
│ ├── stats/ # GET /v1/projects/:id/stats
│ ├── usage/
│ ├── middleware/
│ └── config/
├── docs/
├── tests/
│ ├── unit/
│ └── integration/
├── docker-compose.yml
├── .env.example
└── README.md
V1 (current)
- API key lifecycle (create, list, revoke)
- Service identity lifecycle (create, list, revoke)
- API key verification (
POST /v1/verify) - Service key verification (
POST /v1/verify/service) - Usage logging (per key, per service)
- JWT auth with access + refresh tokens
- Refresh token rotation with theft detection
- Multi-layer rate limiting (IP + key-based)
- Token blacklisting on logout
- Usage statistics (
GET /v1/projects/:id/stats) - Deep health check (MongoDB + Redis probe)
- Graceful shutdown (SIGTERM/SIGINT)
- Key rotation (API keys + service identities)
- Cascade deletion (project delete removes all associated data)
- Current user profile (
GET /v1/auth/me) - Request ID tracing (
X-Request-Idheader) - Structured JSON logging (pino)
V2
- Web dashboard
- Project roles & team access
- Webhook events
- SDK support (Node, Python, Go)
# Unit tests (no Docker required)
npm run test:unit
# Unit tests with coverage report
npm run test:unit -- --coverage
# Integration tests (requires Docker)
docker compose up -d
npm run test:integrationUnit tests mock Redis and MongoDB — no external dependencies. Coverage is enforced at push time via Husky: 80% statements/lines, 75% branches/functions.
Integration tests run against real services and must run serially (--runInBand) to avoid database state conflicts between suites. Suites: auth, projects, apikeys, verification, services, verify-service, stats, health.
Contributions are welcome! Here's how to get started:
- Fork the repo and create a branch:
git checkout -b feature/your-feature - Make your changes and write tests if applicable
- Run the test suite:
npm test - Open a pull request with a clear description of what you changed and why
For larger changes, open an issue first so we can discuss the approach.
Found a security issue? Please don't open a public issue — email us directly instead.
- Node.js 18+
- Docker
- MongoDB
- Redis
MIT — do whatever you want with it.