Files
nick-doc/01 - Architecture/Security Architecture.md
Siavash Sameni 4cf5c49274 docs(audit): align documentation with post-remediation backend reality
- Update data model enums to match backend models
- Update API reference auth requirements
- Add dispute module references and warning blocks
- Add 2026-05-24 audit remediation callout to Overview
- Generate task breakdowns and audit artifacts
- Add doc alignment report (.taskmaster/reports/)
2026-05-24 11:16:29 +04:00

9.9 KiB

title, tags, created
title tags created
Security Architecture
architecture
security
authentication
rbac
2026-05-23

Security Architecture

How identity, authorization, transport, and integrity are handled across the platform.

Important

Read alongside Authentication Flow (user-facing), Passkey (WebAuthn) Flow, and Payment Flow - SHKeeper (webhook HMAC).


1. Threat model — at a glance

Threat Mitigation
Credential stuffing bcrypt 12-round hashing + account lockout + rate-limit (when enabled)
Session hijacking Short-lived JWTs (7d), opaque refresh tokens (30d), token rotation
CSRF JWT in Authorization header (not cookie), CORS allow-list
XSS Helmet CSP, React auto-escaping, sanitize HTML before storage
SQL/NoSQL injection Mongoose parameterized queries, no $where strings, schema validation
Webhook spoofing HMAC SHA-256 over body + secret, constant-time compare
File upload abuse Multer MIME validation, 5 MB cap, non-executable storage, served by Nginx not Node
Replay attacks Per-payment idempotency on providerPaymentId, per-request X-Request-Id
Account takeover Email verification required, password reset code expiry (1h), passkey support
Phishing Passkey origin binding (NEXT_PUBLIC_PASSKEY_ORIGIN), email domain pinning
Data leakage Role-gated endpoints, field-level projection (select: false on password), redacted logs

2. Authentication layers

2.1 Email + password (primary)

sequenceDiagram
    actor U as User
    participant FE as Frontend
    participant BE as Backend
    participant DB as MongoDB
    U->>FE: enters credentials
    FE->>BE: POST /api/auth/login { email, password }
    BE->>DB: User.findOne({ email })
    DB-->>BE: user doc (incl. hashed password)
    BE->>BE: bcrypt.compare(password, hash)
    alt invalid
        BE->>DB: increment loginAttempts
        BE->>DB: lock account at N
        BE-->>FE: 401 / 423 locked
    else valid
        BE->>BE: sign JWT (7d), refresh (30d)
        BE->>DB: store refresh-token id
        BE->>DB: clear attempts
        BE-->>FE: 200 { user, token, refreshToken }
    end
  • Password rules enforced by authValidation.ts: ≥8 chars, mixed case + digit recommended (cite the validator for exact rules).
  • bcrypt rounds = 12 (authService.ts).
  • Lockout: after N failed attempts within window, account locked for cooldown — see authService.ts:113-145.
  • Reset code emailed on passwordResetCode request; valid 1h.

2.2 Google OAuth 2.0

  • Frontend uses NEXT_PUBLIC_GOOGLE_CLIENT_ID for the Sign-In with Google button.
  • ID token sent to backend → googleOAuthService.ts verifies via Google's public keys → either links to existing User by email or creates a new one.
  • See Google OAuth Flow.

2.3 WebAuthn / Passkey

  • Standards-based passwordless.
  • Backend: passkeyService.ts orchestrates registration and assertion challenges.
  • Frontend env: NEXT_PUBLIC_PASSKEY_RP_NAME=Amn, NEXT_PUBLIC_PASSKEY_RP_ID=<domain>, NEXT_PUBLIC_PASSKEY_ORIGIN=<origin>.
  • See Passkey (WebAuthn) Flow.

Warning

Dev env files ship NEXT_PUBLIC_PASSKEY_RP_ID=localhost. In production this MUST be the actual eTLD+1 domain (e.g., amn.gg) — passkeys are scoped to the RP ID and can't be transferred.

2.4 Refresh-token rotation

  • On POST /api/auth/refresh, the backend:
    • Verifies the supplied refresh token.
    • Issues a NEW access token + a NEW refresh token.
    • Invalidates the old refresh token id in MongoDB.
  • If the same refresh token is presented twice → all sessions for that user are invalidated (token reuse detection).

3. Authorization (RBAC)

3.1 Roles

Role Source Capabilities
buyer (user) default on signup Create requests, pay, chat, dispute, rate
seller (owner) chosen at signup OR upgraded Make offers, build templates, run a shop, withdraw
admin seed / manual Moderate, mediate disputes, manage users/blogs/levels
support seed / manual Read-only on most data, can reset passwords, escalate

A single User may be buyer and seller simultaneously (combined role).

3.2 Enforcement points

  • MiddlewareauthMiddleware (verifies JWT) followed by roleGuard(role) on every route that requires elevation.
  • Service layer — defensive assertRole(ctx, 'admin') calls inside critical service methods so even mis-mounted routes can't bypass.
  • UIAuthGuard + EmailVerificationGuard + role-aware nav (components/nav-section) hide admin/seller menus for users without permission. This is convenience only — never the security boundary.

4. Transport security

  • HTTPS terminated upstream (CloudFlare / external Nginx). Internal cluster is HTTP.
  • HSTS header set by upstream proxy (recommended max-age=31536000; includeSubDomains; preload).
  • CORS — exactly one origin allowed: config.frontendUrl. credentials: true.
  • CSP — Helmet default, currently permissive for Web3 popup compatibility (see frontend/next.config.ts setting COOP=same-origin-allow-popups, COEP=unsafe-none).

5. Webhook integrity (SHKeeper)

sequenceDiagram
    participant SHK
    participant BE
    SHK->>BE: POST /api/payment/shkeeper/webhook<br/>X-Signature: sha256=<hmac>
    BE->>BE: hmac = HMAC_SHA256(SHKEEPER_WEBHOOK_SECRET, body)
    BE->>BE: crypto.timingSafeEqual(hmac, providedSig)
    alt mismatch
        BE-->>SHK: 401 Unauthorized
    else match
        BE->>BE: process payment update
        BE-->>SHK: 200 OK
    end
  • Body must be the raw bytes used for HMAC — apply express.raw({ type: 'application/json' }) on the webhook route ONLY (the rest of the app uses parsed JSON).
  • In dev (NODE_ENV === 'development') signature verification can be bypassed for local testing — confirm this is gated and never reachable in prod.
  • Idempotency: identical webhook delivered twice should be no-op. Check by (providerPaymentId, status) tuple before mutating.

See Payment Flow - SHKeeper for the full flow.


6. Input validation

  • Backendexpress-validator per route (e.g., authValidation.ts), centralised validate middleware that 422s on failure with { details: [...] }.
  • Frontendzod schemas via @hookform/resolvers/zod. Same schema can be re-exported to a shared/ package for true single-source-of-truth (not yet wired).
  • Mongoose — schema-level type, required, enum, min/max, custom validate functions as a last line of defence.

7. File upload safety

  • Stored under uploads/{avatars|documents|products|temp}/ — non-executable, served by Nginx (no Node interpretation).
  • MIME allow-list in fileService.ts: images for avatars/products, PDFs/docs for evidence.
  • 5 MB hard cap (MAX_FILE_SIZE=5242880).
  • Original filenames hashed → no path traversal, no clobber.
  • Recommended: virus scan via ClamAV before exposing to other users (dispute evidence, chat attachments).

8. Secrets management

  • Production secrets injected via host .env, mounted into compose env_file.
  • Never log secrets — logger redaction recommended (winston/pino formatter).
  • .env* files in .gitignore. Repo includes only .env.development / .env.production templates with public values (NEXT_PUBLIC_*).
  • Rotate JWT_SECRET invalidates all existing JWTs — schedule a maintenance window.
  • Rotate SHKEEPER_WEBHOOK_SECRET coordinated with SHKeeper dashboard (set new → verify → remove old).

See Environment Variables for the catalog.


9. Rate limiting & abuse

  • Rate limiting is enabled as of 2026-05-24 (app.ts).
  • Active tiers:
    • /api/auth/* — 10 req / 15 min / IP
    • /api/payment/* — 30 req / 15 min / IP
    • /api/ai/* — 20 req / 15 min / IP
    • global API — 100 req / 15 min / IP (skips /health and Request-Network webhooks)
  • Counters are in-memory (Redis adapter planned for distributed deploys).
  • For chat and notifications, debounce at the client to avoid spamming legitimate emits.

10. Audit logging

The codebase currently uses morgan (HTTP access logs) and ad-hoc logger.info/warn/error. For PCI-adjacent operations (payments) consider:

  • Append-only audit log of every payment / payout / refund / role change.
  • Include actor (userId), target, action, before/after diff, request id.
  • Persist in a separate Mongo collection or external log sink with retention ≥1y.

11. Frontend session storage

  • JWT and refresh token stored in localStorage (per current implementation — cite to verify in frontend/src/lib/).
  • Risk: XSS = total takeover. Mitigations: strict CSP, no dangerouslySetInnerHTML on untrusted content, audit dependencies (yarn audit).
  • Alternative: store refresh token in httpOnly cookie and keep only short-lived access token in memory — recommended for production hardening.

12. Hardening checklist (pre-launch)

  • Enable rate-limit middleware (done 2026-05-24)
  • Enforce Socket.IO JWT authentication (done 2026-05-24)
  • Promote refresh tokens to httpOnly cookies
  • Replace localhost passkey RP ID with production domain
  • Disable NEXT_PUBLIC_IS_DEVELOPMENT=true and ENABLE_DEBUG=true in prod build
  • Verify NODE_ENV=production in backend prod env
  • Pin production Watchtower to versioned tag (not latest)
  • Add backend Sentry SDK + source maps
  • Rotate all dev-seeded credentials before public launch
  • Run yarn audit / npm audit and triage CVEs
  • Pentest the payment + dispute flows specifically
  • Review every > [!warning] callout in this vault