Security Model
Zero-Dependency Security
Kyqu implements all security primitives using Node.js built-in modules — no external security libraries.
| Feature | Implementation | Module |
|---|---|---|
| Password hashing | scrypt with 16-byte salt, 64-byte key | node:crypto |
| Token hashing | SHA-256 | node:crypto |
| TOTP 2FA | HMAC-SHA1, 30-second window, RFC 6238 | node:crypto |
| CSRF protection | Double-submit cookie pattern | node:crypto timingSafeEqual |
| Session tokens | 32-byte random, URL-safe base64 | node:crypto randomBytes |
| SMTP email | Raw TCP/TLS sockets, EHLO/STARTTLS/AUTH LOGIN | node:net node:tls |
Password Storage
Format: scrypt:<base64url-salt>:<base64url-derived-key>
Example: scrypt:abc123...:def456...
- Salt: 16 random bytes per password
- Key: 64 bytes scrypt output
- Comparison:
timingSafeEqual(constant-time)
Session Security
Admin Sessions
- Stored as HttpOnly cookies with
SameSite=Strict - Token is 32 bytes of randomness (256 bits entropy)
- Only SHA-256 hash stored in database
- Raw token never logged or stored
Project User Sessions
- Returned as bearer tokens to the integrating application
- Same 256-bit entropy token
- Only SHA-256 hash stored in database
CSRF Protection
Uses the double-submit cookie pattern:
- Server sets a non-HttpOnly
kyqu_csrfcookie - Client reads the cookie and includes it as
x-kyqu-csrf-tokenheader - Server compares cookie vs header using
timingSafeEqual - Applies to all
POST/PATCH/PUT/DELETErequests to admin routes - Cookie rotates on each login/signup
Rate Limiting
Route families are rate-limited independently:
| Route Family | Examples |
|---|---|
admin-login | /api/admin/login |
admin-signup | /api/admin/signup |
project-login | /api/projects/*/auth/login |
project-signup | /api/projects/*/auth/signup |
password-reset | /api/projects/*/auth/password-reset/* |
magic-link | /api/projects/*/auth/magic-link/* |
api | Everything else |
Storage Backends
- Memory (
Map): Single-process development - PostgreSQL (
api_rate_limitstable): Multi-process production
Account Lockout
Per-project configurable lockout policy:
- Tracks consecutive failed login attempts (password and failed TOTP/backup-code after correct password)
- Locks account after
lockout_max_attempts(default: 5) - Lockout window:
lockout_window_minutes(default: 15) - Emits
user.login_failedanduser.lockedwebhooks - Admin can unlock individual user accounts
- Superadmin can suspend entire projects — suspended projects reject all auth routes
CORS
- Admin routes: CORS restricted to
APP_ORIGINvalue(s) - Project routes: CORS resolved per-project from
allowed_originssetting - Empty
allowed_origins= unrestricted — echoes requestOriginonly (never*with credentials) - Restricted list: only matching origins are echoed back
API Key Authentication
Projects authenticate API calls using:
x-kyqu-public-keyheader — identifies the project (public key)publicKeyfield in request body — alternative for non-browser clients
The public key is stored in plaintext; the paired secret key is stored only as a hash.
Monitoring & Logging
- Structured JSON logs to stdout/stderr
- Sensitive fields (password, token, secret, key, session) automatically redacted as
[redacted] - Optional external monitoring webhook for critical events
- Rate limit exceeded events trigger monitoring alerts
Webhook URL Policy
Outbound webhook URLs are validated on create/update. Targets on localhost, .local hostnames, and private/link-local IP literals are rejected to reduce SSRF risk.
Email Link Base URL
Email verification, password reset, and magic-link messages embed URLs pointing to the Kyqu API. The base URL is determined by your Kyqu instance configuration.
Integration Security Checklist
- HTTPS for all API communication
- Session tokens stored securely (keychain, HttpOnly cookies, or env vars)
- Webhook signatures verified on your endpoints
- CORS origins restricted in project settings
- 401 responses handled with proper redirects
-
withAutoRefreshused for long-lived sessions