# QODY

White-label QR menu ordering platform (SnowIT BiH market) — architecture, security, operations.

# Authentication Architecture

# Authentication Architecture

## Overview

QODY implements multi-role authentication with separate flows for:

- **Staff** — venue-scoped (email + password + venueSlug)
- **SuperAdmin** — cross-venue global admin (email + password)
- **Guest** — anonymous/session-based (no credential auth required)

## Security: Rate-Limiting &amp; Brute-Force Protection

### Critical Vulnerability Fixed — 2026-07-03 (MC #104585)

#### Vulnerability

**Impact:** CRITICAL — Unlimited brute-force / password-spray against owner/admin/SUPERADMIN accounts.

The login rate limiter trusted the client-supplied `X-Forwarded-For` header (first value) to key rate-limit buckets. An attacker could rotate a forged `X-Forwarded-For` value on each request to obtain a fresh rate-limit bucket every time, bypassing all protections.

**Affected Endpoints:**

- `POST /staff/auth/login`
- `POST /superadmin/auth/login`
- `POST /auth/forgot-password`

**Live Exploitation Verified:** 2026-07-01 (Securion security review)

> 7 consecutive requests, same email, different spoofed X-Forwarded-For on every request:  
> All attempts → HTTP 401 (unlimited, rate limiter never triggered)  
> *Evidence: `~/system/evidence/104554/qody-ratelimit-securion-review-20260701.md`*

#### Fix (Deployed 2026-07-03)

**Strategy:** Rate-limit on NON-SPOOFABLE dimensions instead of client IP.

**New Design:**

- **Email-only bucket:** 5 failures per email per 15 minutes (keyed on `email.trim().lowercase()` — no IP component)
- **Scope bucket:** 20 failures per scope per 15 minutes 
    - Staff login: keyed on `venueSlug` (per-venue spray protection)
    - SuperAdmin login: keyed on constant `"superadmin-crossvenue"` (global spray protection)
- **Forgot Password:** Email-only bucket (3 requests per 15 minutes, privacy-preserving — always returns HTTP 200 even when rate-limited)

**ClientIpResolver.kt:** A helper for trusted-last-hop IP resolution was added (`TRUSTED_PROXY_HOPS=1`) but is used ONLY for non-critical post-auth `ApiRateLimiter` IP dimension + audit logs — NOT the login security key. Login security does NOT depend on IP trust.

**Implementation:**

- `LoginRateLimiter.kt` — complete rewrite
- `StaffRoutes.kt`, `SuperAdminRoutes.kt` — scope-based rate-limit keys
- `PasswordResetService.kt` — email-only key for forgot-password
- `ClientIpResolver.kt` — NEW (non-critical uses only)

#### Deployment

- **Commit:** `f176357` (security(api): fix XFF-bypass login rate-limiter — MC #104585)
- **Image:** `qodyprodacr.azurecr.io/qody-api:f176357`
- **Revision:** qody-api-prod--0000012 @ 100% traffic
- **Environment:** rg-qody-prod (swedencentral), api.qody.ba
- **Deploy Date:** 2026-07-03

#### Verification (Live Production Replay)

**Test:** Replayed XFF-rotation exploit on production `api.qody.ba` with fresh email + rotating forged `X-Forwarded-For` values (same attack that succeeded unlimited pre-fix).

**Result (Staff Login):**

<table id="bkmrk-attemptforged-xffres"><thead><tr><th>Attempt</th><th>Forged XFF</th><th>Result</th></tr></thead><tbody><tr><td>1</td><td>203.0.113.1</td><td>HTTP 401</td></tr><tr><td>2</td><td>203.0.113.2</td><td>HTTP 401</td></tr><tr><td>3</td><td>203.0.113.3</td><td>HTTP 401</td></tr><tr><td>4</td><td>203.0.113.4</td><td>HTTP 401</td></tr><tr><td>5</td><td>203.0.113.5</td><td>HTTP 401</td></tr><tr><td>6</td><td>203.0.113.6</td><td>**HTTP 429** — Rate limited ✅</td></tr></tbody></table>

**Outcome:** Email-based limiter triggered at threshold despite XFF rotation. Fix confirmed live.

**Unit Tests:** 261 total, 0 failures, including a regression test replaying the exact exploit.

*Evidence: `~/system/evidence/104585/qody-xff-fix-deploy-verify-20260703.md`*

#### Follow-Up (Non-Blocking, L-Priority)

- Empirically verify ACA Envoy XFF hop-count to validate/tune the `ClientIpResolver` `TRUSTED_PROXY_HOPS` default (does not affect login security, which is now email+scope keyed).

---

## JWT &amp; Refresh Tokens

*(Planned section — JWT structure, rotation policy, HttpOnly cookies, etc.)*

## Password Hashing

*(Planned section — Argon2id parameters, rotation, etc.)*

## Session Management

*(Planned section — Staff/SuperAdmin session lifecycle, logout, etc.)*