# New Authentication & Onboarding API (DEPRECATED)

## Overview

**Hybrid Auth Strategy:**
- Signup: Phone/Email + OTP (passwordless initially)
- After onboarding: Optional password setup
- Login: Password (if set) OR OTP OR Google/Apple
- Sensitive actions: Require OTP or password confirmation

**Response Format Standard:**
All responses follow `GlobeSuccessResponseBuilder` or `GlobeFailureResponseBuilder` format:
```json
{
  "success": true/false,
  "httpStatus": "OK/BAD_REQUEST/etc",
  "message": "Human readable message",
  "action_time": "2025-01-11T15:20:00",
  "data": { ... }
}
```

---

## DEVICE SECURITY ARCHITECTURE

### The Problem We're Solving

```
Without hardware-bound keys:
─────────────────────────────────────────────────────────
Attacker steals victim's password
    ↓
Attacker generates fake deviceId: "fake-device-123"
    ↓
Attacker logs in → Server asks for OTP
    ↓
Attacker does SIM swap / social engineering → gets OTP
    ↓
Attacker is now "trusted" forever 😱
    ↓
Victim can't kick attacker out (attacker has valid device)


With hardware-bound keys:
─────────────────────────────────────────────────────────
Attacker steals victim's password
    ↓
Attacker tries to login with fake deviceId
    ↓
Server: "Sign this challenge with your private key"
    ↓
Attacker: "I don't have the private key..." 😤
    ↓
Private key is locked inside victim's phone hardware
    ↓
Attack FAILS ✅
```

---

### Asymmetric Cryptography (Key Pair Concept)

```
┌─────────────────────────────────────────────────────────────┐
│                    ASYMMETRIC CRYPTOGRAPHY                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Private Key                      Public Key               │
│   ───────────                      ──────────               │
│   • Secret                         • Shareable              │
│   • Never leaves device            • Stored on server       │
│   • Used to SIGN                   • Used to VERIFY         │
│   • Locked in hardware             • Anyone can have it     │
│                                                             │
│   ┌─────────────┐                 ┌─────────────┐          │
│   │ Private Key │──── generates ──▶│ Public Key  │          │
│   │    🔐       │                  │    🔓       │          │
│   └─────────────┘                  └─────────────┘          │
│         │                                │                  │
│         ▼                                ▼                  │
│   Sign("hello")                    Verify(signature)        │
│         │                                │                  │
│         ▼                                ▼                  │
│   "MEUCIQC7..."                    true / false             │
│   (signature)                                               │
│                                                             │
│   KEY POINT: You CANNOT derive private key from public key  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

### Platform Security Overview

```
┌─────────────────────────────────────────────────────────────┐
│                         iOS DEVICE                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────────────────────────────────────────────────┐  │
│   │                    Main Processor                    │  │
│   │   Your app lives here                                │  │
│   │   Can request signatures                             │  │
│   │   CANNOT access private key                          │  │
│   └─────────────────────────────────────────────────────┘  │
│                           │                                 │
│                           │ "Please sign this"              │
│                           ▼                                 │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              SECURE ENCLAVE (Separate Chip)          │  │
│   │   ┌─────────────────────────────────────────────┐   │  │
│   │   │   Private Key 🔐                            │   │  │
│   │   │   • Generated HERE                          │   │  │
│   │   │   • Stored HERE                             │   │  │
│   │   │   • NEVER leaves                            │   │  │
│   │   │   • Cannot be read by main processor        │   │  │
│   │   │   • Cannot be extracted even if jailbroken  │   │  │
│   │   └─────────────────────────────────────────────┘   │  │
│   │   Returns: signature (NOT the key)                   │  │
│   └─────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                       ANDROID DEVICE                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Option A: StrongBox (Dedicated Security Chip)             │
│   • Separate hardware chip (like iOS Secure Enclave)        │
│   • Best security                                           │
│   • Available on Pixel 3+, Samsung S10+, etc.               │
│                                                             │
│   Option B: TEE (Trusted Execution Environment)             │
│   • Isolated area within main processor                     │
│   • Very good security                                      │
│   • Available on most Android 7+ devices                    │
│                                                             │
│   Both use Android Keystore API                             │
│   Same result: Private key cannot be extracted              │
│                                                             │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                       WEB BROWSER                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Problem: No dedicated hardware security                   │
│   Solution: PKCE-style ephemeral keys (explained below)     │
│                                                             │
│   • Generate keypair in MEMORY (not stored)                 │
│   • Session-bound (dies when tab closes)                    │
│   • Combined with fingerprint + token binding               │
│   • NEVER fully trusted (always require OTP for sensitive)  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

### Platform Security Comparison

| Aspect | iOS | Android | Web |
|--------|-----|---------|-----|
| **Key Storage** | Secure Enclave | StrongBox / TEE | Memory only |
| **Hardware Protected** | ✅ Yes | ✅ Yes | ❌ No |
| **Key Extractable** | ❌ Never | ❌ Never | N/A (ephemeral) |
| **Forgery Possible** | ❌ No | ❌ No | ⚠️ Harder |
| **Trust Level** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| **Trust Duration** | 30 days | 30 days | 7 days |
| **Sensitive Actions** | Some without OTP | Some without OTP | ALWAYS OTP |

---

### Why Attacker Cannot Forge (Mobile)

```
What attacker has access to:
──────────────────────────────────────────────────────────────
✅ Victim's email/username (public or leaked)
✅ Victim's password (phishing, data breach, etc.)
✅ Victim's deviceId (could intercept network traffic)
✅ Victim's publicKey (stored on server, not secret)
✅ Can request fresh nonce anytime

What attacker CANNOT get:
──────────────────────────────────────────────────────────────
❌ Victim's privateKey (locked in hardware)
   • Cannot extract from Secure Enclave
   • Cannot extract even with physical device access
   • Cannot extract even if device is jailbroken

Without privateKey:
──────────────────────────────────────────────────────────────
❌ Cannot create valid signature
❌ Server rejects login
❌ Attack fails
```

---

### Challenge/Nonce Purpose

```
Without nonce:
──────────────────────────────────────────────────────────────
1. Victim logs in legitimately
2. Attacker intercepts: { deviceId, signature }
3. Attacker replays exact same request
4. Server: "Signature valid!" ✅
5. Attacker is in 😱

Problem: Signature is always the same for same deviceId


With nonce:
──────────────────────────────────────────────────────────────
1. Victim logs in legitimately
   - Gets nonce "ch_abc123"
   - Signs "ch_abc123|timestamp|deviceId"
   
2. Attacker intercepts the request
   
3. Attacker replays exact same request 1 minute later
   
4. Server: "Nonce ch_abc123 already used/expired!" ❌

5. Attacker tries to get new nonce and replay
   - Gets nonce "ch_xyz789"
   - But old signature was for "ch_abc123"
   - Signature doesn't match new nonce ❌
   
6. Attacker cannot create new signature
   - Needs privateKey to sign "ch_xyz789|..."
   - privateKey is in victim's phone ❌

7. Attack fails ✅


Key insight: 
──────────────────────────────────────────────────────────────
Nonce makes each signature UNIQUE and TIME-LIMITED
Even captured valid signatures become useless after ~60 seconds
```

---

## WEB SECURITY MODEL (PKCE-Style)

### The Problem: Web Cannot Keep Secrets

```
Mobile App:
────────────────────────────────────────────────────────
✅ Compiled binary (hard to reverse engineer)
✅ Secure storage (Keychain, Keystore)
✅ Hardware protection (Secure Enclave, TEE)
✅ Can store secrets safely


Web Browser:
────────────────────────────────────────────────────────
❌ JavaScript is readable (View Source)
❌ localStorage/IndexedDB accessible via DevTools
❌ No hardware-protected storage
❌ Any "secret" can be extracted

If we store a private key in browser:
→ Open DevTools → Application → IndexedDB → Copy the key → Use anywhere 😱
```

### The Solution: PKCE-Style Ephemeral Keys

OAuth2 had the same problem. Their solution: **Don't store a secret. Generate a temporary one-time proof.**

```
┌─────────────────────────────────────────────────────────────┐
│              WEB DEVICE AUTH (PKCE-Style)                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Instead of:                                                │
│  Store private key → Sign challenges                        │
│  (Private key can be stolen from IndexedDB)                 │
│                                                             │
│  We do:                                                     │
│  Each session: Generate fresh keypair in MEMORY             │
│  Register public key with server for THIS SESSION           │
│  Private key lives only in JavaScript memory                │
│  When tab closes → key is gone → nothing to steal           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

### Web Security Layers

```
┌─────────────────────────────────────────────────────────────┐
│                    WEB SECURITY MODEL                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  LAYER 1: Session-Bound Cryptographic Proof                 │
│  • Generate keypair in memory (not stored)                  │
│  • Proves: "Same browser tab that started auth"             │
│  • Prevents: Request interception/replay                    │
│                                                             │
│  LAYER 2: Browser Fingerprint                               │
│  • Collect browser characteristics                          │
│  • Proves: "Likely same browser/device"                     │
│  • Prevents: Token theft to different browser               │
│                                                             │
│  LAYER 3: Bound Tokens                                      │
│  • Tokens bound to fingerprint + IP range                   │
│  • Server validates on each request                         │
│  • Prevents: Token theft/export                             │
│                                                             │
│  LAYER 4: Never Fully Trust                                 │
│  • Web devices NEVER get "trusted" status                   │
│  • Sensitive actions ALWAYS require OTP                     │
│  • Shorter token lifetime than mobile                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

### Web vs Mobile Trust Levels

```
┌─────────────────────────────────────────────────────────────┐
│                  TRUST LEVEL COMPARISON                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  MOBILE (iOS/Android):                                      │
│  After OTP verification:                                    │
│  • Device becomes "TRUSTED"                                 │
│  • Trust lasts 30 days                                      │
│  • Password-only login allowed                              │
│  • Most actions without re-auth                             │
│  • Hardware key proves device identity                      │
│                                                             │
│  WEB (Browser):                                             │
│  After OTP verification:                                    │
│  • Device is "RECOGNIZED" (not trusted)                     │
│  • Recognition lasts 7 days (shorter)                       │
│  • Password-only login allowed for basic actions            │
│  • Sensitive actions ALWAYS need OTP:                       │
│    • Change password                                        │
│    • Change email/phone                                     │
│    • View payment methods                                   │
│    • Delete account                                         │
│    • Large purchases                                        │
│  • Session-bound keys (weaker than hardware)                │
│                                                             │
│  WHY THE DIFFERENCE:                                        │
│  Mobile: Hardware guarantees "this is THE device"           │
│  Web: Software only guarantees "probably same browser"      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

## AUTHENTICATION FLOWS

### Device Registration (First App Launch - Mobile)

```
┌──────────────┐                              ┌──────────────┐
│    Device    │                              │    Server    │
└──────┬───────┘                              └──────┬───────┘
       │                                             │
       │  1. Generate key pair in hardware           │
       │     Private key → Secure Enclave            │
       │     Public key → exportable                 │
       │                                             │
       │  2. GET /auth/challenge                     │
       │────────────────────────────────────────────▶│
       │                                             │
       │  3. { nonce: "ch_abc123", expiresIn: 60 }   │
       │◀────────────────────────────────────────────│
       │                                             │
       │  4. Sign nonce with private key             │
       │     message = "ch_abc123|timestamp|deviceId"│
       │     signature = sign(message, privateKey)   │
       │                                             │
       │  5. POST /auth/device/register              │
       │     {                                       │
       │       deviceId: "ios_xyz...",               │
       │       publicKey: "MFkw...",                 │
       │       nonce: "ch_abc123",                   │
       │       timestamp: 1736611200000,             │
       │       signature: "MEUC...",                 │
       │       platform: "IOS"                       │
       │     }                                       │
       │────────────────────────────────────────────▶│
       │                                             │
       │                      6. Verify signature    │
       │                         using publicKey     │
       │                                             │
       │                      7. Store:              │
       │                         deviceId → publicKey│
       │                                             │
       │  8. { success: true, deviceId: "ios_xyz" }  │
       │◀────────────────────────────────────────────│
       │                                             │
```

### Login Flow (Mobile - With Device Signature)

```
┌──────────────┐                              ┌──────────────┐
│    Device    │                              │    Server    │
└──────┬───────┘                              └──────┬───────┘
       │                                             │
       │  1. GET /auth/challenge                     │
       │────────────────────────────────────────────▶│
       │                                             │
       │  2. { nonce: "ch_xyz789", expiresIn: 60 }   │
       │◀────────────────────────────────────────────│
       │                                             │
       │  3. Sign: signature = sign(                 │
       │       "ch_xyz789|1736611200000|ios_xyz...", │
       │       privateKey                            │
       │     )                                       │
       │                                             │
       │  4. POST /auth/login                        │
       │     {                                       │
       │       identifier: "alex@email.com",         │
       │       password: "***",                      │
       │       deviceAuth: {                         │
       │         deviceId: "ios_xyz...",             │
       │         nonce: "ch_xyz789",                 │
       │         timestamp: 1736611200000,           │
       │         signature: "MEUC...",               │
       │         platform: "IOS"                     │
       │       }                                     │
       │     }                                       │
       │────────────────────────────────────────────▶│
       │                                             │
       │                      5. Validate nonce      │
       │                         (exists in Redis?)  │
       │                                             │
       │                      6. Delete nonce        │
       │                         (one-time use)      │
       │                                             │
       │                      7. Get publicKey       │
       │                         for deviceId        │
       │                                             │
       │                      8. Verify signature    │
       │                                             │
       │                      9. Check password      │
       │                                             │
       │                     10. Check device trust  │
       │                         status              │
       │                                             │
       │  11. { accessToken, refreshToken, ... }     │
       │◀────────────────────────────────────────────│
       │                                             │
```

### Web Session Flow (PKCE-Style)

```
┌──────────────┐                              ┌──────────────┐
│   Browser    │                              │    Server    │
└──────┬───────┘                              └──────┬───────┘
       │                                             │
       │  User opens login page                      │
       │                                             │
       │  1. Generate keypair IN MEMORY              │
       │     const keyPair = await crypto.subtle    │
       │       .generateKey(ECDSA, P-256)            │
       │     ⚠️ NOT stored anywhere                  │
       │     ⚠️ Lives only in JS variable            │
       │                                             │
       │  2. Generate browser fingerprint            │
       │     • Screen size, timezone, language       │
       │     • Canvas hash, WebGL renderer           │
       │     • → Hash all into single ID             │
       │                                             │
       │  3. POST /auth/web/session                  │
       │     {                                       │
       │       publicKey: "MFkw...",                 │
       │       fingerprint: "fp_abc123..."           │
       │     }                                       │
       │────────────────────────────────────────────▶│
       │                                             │
       │                      4. Generate sessionId  │
       │                      5. Store in Redis:     │
       │                         sessionId →         │
       │                           publicKey,        │
       │                           fingerprint,      │
       │                           ipAddress         │
       │                         TTL: 10 minutes     │
       │                                             │
       │  6. { sessionId: "ws_xyz...",               │
       │       nonce: "ch_abc..." }                  │
       │◀────────────────────────────────────────────│
       │                                             │
       │  User enters email + password               │
       │                                             │
       │  7. Sign the nonce                          │
       │     signature = sign(                       │
       │       nonce + timestamp + sessionId,        │
       │       privateKey  ← still in memory         │
       │     )                                       │
       │                                             │
       │  8. POST /auth/login                        │
       │     {                                       │
       │       identifier: "alex@email.com",         │
       │       password: "***",                      │
       │       webAuth: {                            │
       │         sessionId: "ws_xyz...",             │
       │         nonce: "ch_abc...",                 │
       │         timestamp: 1736611200000,           │
       │         signature: "MEUC...",               │
       │         fingerprint: "fp_abc123..."         │
       │       }                                     │
       │     }                                       │
       │────────────────────────────────────────────▶│
       │                                             │
       │                      9. Get session from    │
       │                         Redis               │
       │                                             │
       │                     10. Verify:             │
       │                         • Session exists    │
       │                         • Not expired       │
       │                         • Signature valid   │
       │                         • Fingerprint match │
       │                         • IP in range       │
       │                                             │
       │                     11. Delete session      │
       │                         (one-time use)      │
       │                                             │
       │                     12. Create BOUND tokens │
       │                         (bound to fp + IP)  │
       │                                             │
       │  13. {                                      │
       │        accessToken: "...",                  │
       │        refreshToken: "...",                 │
       │        device: { trusted: false }           │
       │      }                                      │
       │◀────────────────────────────────────────────│
       │                                             │
```

---

## SCALABLE ARCHITECTURE

```
┌─────────────────────────────────────────────────────────────────────────────────┐
│                          NEXTGATE SECURITY ARCHITECTURE                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                  │
│  ┌───────────────────────────────────────────────────────────────────────────┐  │
│  │                         API GATEWAY / LOAD BALANCER                        │  │
│  │                    (Rate Limiting, DDoS Protection)                        │  │
│  └───────────────────────────────────────────────────────────────────────────┘  │
│                                  │                                               │
│                                  ▼                                               │
│  ┌───────────────────────────────────────────────────────────────────────────┐  │
│  │                        AUTH SERVICE (Stateless)                            │  │
│  │  ┌─────────────────────────────────────────────────────────────────────┐  │  │
│  │  │                    Challenge/Nonce Service                           │  │  │
│  │  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐                 │  │  │
│  │  │  │ Node 1  │  │ Node 2  │  │ Node 3  │  │ Node N  │  (Stateless)    │  │  │
│  │  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘                 │  │  │
│  │  │       │            │            │            │                       │  │  │
│  │  │       └────────────┴─────┬──────┴────────────┘                       │  │  │
│  │  │                          │                                           │  │  │
│  │  │                          ▼                                           │  │  │
│  │  │              ┌───────────────────────┐                               │  │  │
│  │  │              │   Redis Cluster       │  (Nonce storage, 60s TTL)     │  │  │
│  │  │              │   (High Availability) │                               │  │  │
│  │  │              └───────────────────────┘                               │  │  │
│  │  └─────────────────────────────────────────────────────────────────────┘  │  │
│  │                                                                            │  │
│  │  ┌─────────────────────────────────────────────────────────────────────┐  │  │
│  │  │                  Signature Verification Service                      │  │  │
│  │  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐                 │  │  │
│  │  │  │ Node 1  │  │ Node 2  │  │ Node 3  │  │ Node N  │  (Stateless)    │  │  │
│  │  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘                 │  │  │
│  │  │       │            │            │            │                       │  │  │
│  │  │       └────────────┴─────┬──────┴────────────┘                       │  │  │
│  │  │                          │                                           │  │  │
│  │  │                          ▼                                           │  │  │
│  │  │     ┌───────────────────────────────────────────────────────┐       │  │  │
│  │  │     │              PostgreSQL (Primary-Replica)              │       │  │  │
│  │  │     │  device_keys table (deviceId → publicKey)              │       │  │  │
│  │  │     │  Cached in Redis for fast lookups                      │       │  │  │
│  │  │     └───────────────────────────────────────────────────────┘       │  │  │
│  │  └─────────────────────────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────────────────────┘  │
│                                                                                  │
│  SCALABILITY:                                                                    │
│  • Challenge generation: < 5ms (any node can generate)                           │
│  • Signature verification: < 10ms (pure computation)                             │
│  • Public keys cached in Redis (1-hour TTL)                                      │
│  • Can handle 10,000+ logins/second per node                                     │
│  • Horizontal scaling: just add more nodes                                       │
│                                                                                  │
└──────────────────────────────────────────────────────────────────────────────────┘
```

---

## TOKEN BINDING (Web Extra Protection)

```
┌─────────────────────────────────────────────────────────────┐
│                     TOKEN BINDING                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Traditional Token:                                         │
│  {                                                          │
│    "sub": "user_123",                                       │
│    "exp": 1736614800                                        │
│  }                                                          │
│  Problem: Anyone with this token can use it                 │
│                                                             │
│                                                             │
│  Bound Token (What we do for web):                          │
│  {                                                          │
│    "sub": "user_123",                                       │
│    "exp": 1736614800,                                       │
│    "device_fp": "fp_abc123...",     ← Must match           │
│    "ip_hash": "a1b2c3...",          ← Must be in range     │
│    "platform": "WEB"                 ← Affects trust level  │
│  }                                                          │
│                                                             │
│  On every request, server checks:                           │
│  • Current fingerprint ≈ token's device_fp                  │
│  • Current IP in same /24 range as token's IP               │
│  • If mismatch → reject OR require re-auth                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

## BROWSER FINGERPRINT

```
┌─────────────────────────────────────────────────────────────┐
│                  BROWSER FINGERPRINT                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Components collected:                                      │
│  1. Screen: "1920x1080"                                     │
│  2. Color Depth: 24                                         │
│  3. Timezone: "Africa/Dar_es_Salaam"                        │
│  4. Language: "en-US"                                       │
│  5. Platform: "MacIntel"                                    │
│  6. CPU Cores: 8                                            │
│  7. Memory: 8 (GB, approximate)                             │
│  8. Canvas Hash: "a1b2c3..." (drawn image fingerprint)      │
│  9. WebGL Renderer: "Apple M1"                              │
│  10. Audio Context fingerprint                              │
│                                                             │
│  All combined → SHA256 → "fp_7f9a2b3c..."                   │
│                                                             │
│  Stability:                                                 │
│  • ~90% of users have unique fingerprint                    │
│  • Changes if: browser update, OS update, new monitor       │
│  • We allow ~15% variance (fuzzy matching)                  │
│                                                             │
│  Privacy Note:                                              │
│  • NOT used for tracking users                              │
│  • Only to verify "same browser" for security               │
│  • Stored hashed, associated with user session              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

## ATTACK SCENARIO ANALYSIS

```
ATTACK 1: Steal the private key (Mobile)
────────────────────────────────────────────────────────────────
Attacker: Tries to extract key from device
Result: Key is in Secure Enclave / StrongBox
        Cannot be extracted even with physical access
Verdict: ❌ FAILED


ATTACK 2: Steal the private key (Web)
────────────────────────────────────────────────────────────────
Attacker: Opens DevTools, looks for private key
Result: Key is in JavaScript memory, not in storage
        When attacker opens DevTools, they're in THEIR session
        with THEIR keypair, not victim's
Verdict: ❌ FAILED


ATTACK 3: Intercept and replay request
────────────────────────────────────────────────────────────────
Attacker: Captures { sessionId, nonce, signature }
Attacker: Replays the exact request
Server: "Nonce already used/expired"
Verdict: ❌ FAILED (nonce is one-time use)


ATTACK 4: Get new nonce, use old signature
────────────────────────────────────────────────────────────────
Attacker: Gets new nonce "ch_NEW..."
Attacker: Uses old signature (signed for "ch_OLD...")
Server: Signature doesn't match nonce "ch_NEW..."
Verdict: ❌ FAILED (signature bound to specific nonce)


ATTACK 5: Steal tokens from victim's browser
────────────────────────────────────────────────────────────────
Attacker: Uses XSS to steal accessToken
Attacker: Uses token from different browser/IP
Server: Fingerprint mismatch! IP range mismatch!
Verdict: ❌ FAILED (tokens bound to browser + IP)


ATTACK 6: Create fake session from different browser
────────────────────────────────────────────────────────────────
Attacker: Knows victim's email + password
Attacker: Creates own session (own keypair, own fingerprint)
Attacker: Logs in successfully... BUT
Server: "New device detected, OTP required"
Attacker: Doesn't have victim's phone
Verdict: ❌ FAILED (OTP still required for new device)
```

---

## SUMMARY: WHAT WE STORE WHERE

```
┌─────────────────────────────────────────────────────────────┐
│              WHAT WE STORE WHERE (MOBILE)                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ON DEVICE (Secure Storage):                                │
│  ✅ Private key (in Secure Enclave / Keystore)              │
│  ✅ deviceId (identifier)                                   │
│  ✅ accessToken (short-lived)                               │
│  ✅ refreshToken (long-lived)                               │
│                                                             │
│  ON SERVER:                                                 │
│  ✅ Public key (for signature verification)                 │
│  ✅ deviceId → userId mapping                               │
│  ✅ Trust status and expiry                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│              WHAT WE STORE WHERE (WEB)                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  IN BROWSER:                                                │
│  ✅ accessToken (short-lived, bound)                        │
│  ✅ refreshToken (medium-lived, bound)                      │
│  ✅ deviceId (just an identifier, not secret)               │
│  ❌ NO private keys stored                                  │
│  ❌ NO long-term secrets                                    │
│                                                             │
│  IN MEMORY ONLY (during session):                           │
│  ✅ Ephemeral keypair (dies when tab closes)                │
│                                                             │
│  ON SERVER:                                                 │
│  ✅ Session public key (Redis, 10-min TTL)                  │
│  ✅ Browser fingerprint hash                                │
│  ✅ IP range for token binding                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

## DEVICE SECURITY API ENDPOINTS

### 1. Get Challenge (All Platforms)

**GET** `/api/v1/auth/challenge`

Called before any authenticated action (login, register device, refresh token).

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Challenge generated",
  "action_time": "2025-01-11T16:00:00Z",
  "data": {
    "nonce": "ch_dGhpcyBpcyBhIHNlY3VyZSByYW5kb20gbm9uY2U",
    "expiresIn": 60,
    "expiresAt": "2025-01-11T16:01:00Z"
  }
}
```

---

### 2. Register Device (Mobile - First Launch)

**POST** `/api/v1/auth/device/register`

Called on first app launch to register the device's public key.

**Request:**
```json
{
  "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...",
  "nonce": "ch_dGhpcyBpcyBhIHNlY3VyZSByYW5kb20gbm9uY2U",
  "timestamp": 1736611200000,
  "signature": "MEUCIQC7...",
  "platform": "IOS"
}
```

| Field | Description |
|-------|-------------|
| `deviceId` | SHA256 hash of public key, prefixed with platform |
| `publicKey` | Base64-encoded ECDSA P-256 public key |
| `nonce` | Challenge from `/auth/challenge` |
| `timestamp` | Current time in milliseconds |
| `signature` | Sign(`nonce\|timestamp\|deviceId`, privateKey) |
| `platform` | `IOS`, `ANDROID`, or `WEB` |

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device registered successfully",
  "action_time": "2025-01-11T16:00:05Z",
  "data": {
    "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "registered": true,
    "securityLevel": "SECURE_ENCLAVE",
    "note": "Device will be linked to user account on login/signup"
  }
}
```

**Response (Invalid Signature):**
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Invalid device signature",
  "action_time": "2025-01-11T16:00:05Z",
  "data": {
    "code": "INVALID_SIGNATURE",
    "suggestion": "Ensure keys are generated correctly"
  }
}
```

---

### 3. Initialize Web Session (Web Only)

**POST** `/api/v1/auth/web/session`

Called when user opens login page in browser.

**Request:**
```json
{
  "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...",
  "fingerprint": "fp_7f9a2b3c4d5e6f7a8b9c0d1e2f3a4b5c"
}
```

| Field | Description |
|-------|-------------|
| `publicKey` | Ephemeral public key (generated in memory) |
| `fingerprint` | Browser fingerprint hash |

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Web session initialized",
  "action_time": "2025-01-11T16:00:00Z",
  "data": {
    "sessionId": "ws_abc123xyz789",
    "nonce": "ch_dGhpcyBpcyBhIHNlY3VyZSByYW5kb20gbm9uY2U",
    "expiresIn": 600,
    "expiresAt": "2025-01-11T16:10:00Z"
  }
}
```

---

### 4. Login with Device Auth (Mobile)

**POST** `/api/v1/auth/login`

**Request:**
```json
{
  "identifier": "alex@example.com",
  "password": "MySecurePass123!",
  "deviceAuth": {
    "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "nonce": "ch_dGhpcyBpcyBhIHNlY3VyZSByYW5kb20gbm9uY2U",
    "timestamp": 1736611200000,
    "signature": "MEUCIQC7...",
    "platform": "IOS"
  }
}
```

**Response (Success - Trusted Device):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful",
  "action_time": "2025-01-11T16:00:10Z",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "requiresOtp": false,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/..."
    },
    "device": {
      "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
      "trusted": true,
      "securityLevel": "SECURE_ENCLAVE",
      "trustExpiresAt": "2025-02-10T16:00:10Z"
    }
  }
}
```

**Response (New Device - OTP Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "New device detected. Please verify with OTP.",
  "action_time": "2025-01-11T16:00:10Z",
  "data": {
    "requiresOtp": true,
    "otpReason": "NEW_DEVICE",
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "otpSentTo": "+255*****678",
    "otpMethod": "SMS",
    "expiresAt": "2025-01-11T16:10:00Z",
    "device": {
      "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
      "isNew": true,
      "securityLevel": "SECURE_ENCLAVE"
    }
  }
}
```

**Response (Invalid Signature):**
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Invalid device signature",
  "action_time": "2025-01-11T16:00:10Z",
  "data": {
    "code": "INVALID_SIGNATURE",
    "suggestion": "Please ensure your app is up to date"
  }
}
```

**Response (Nonce Expired/Used):**
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Challenge expired or already used",
  "action_time": "2025-01-11T16:00:10Z",
  "data": {
    "code": "INVALID_NONCE",
    "suggestion": "Request a new challenge and try again"
  }
}
```

---

### 5. Login with Web Auth (Web Browser)

**POST** `/api/v1/auth/login`

**Request:**
```json
{
  "identifier": "alex@example.com",
  "password": "MySecurePass123!",
  "webAuth": {
    "sessionId": "ws_abc123xyz789",
    "nonce": "ch_dGhpcyBpcyBhIHNlY3VyZSByYW5kb20gbm9uY2U",
    "timestamp": 1736611200000,
    "signature": "MEUCIQC7...",
    "fingerprint": "fp_7f9a2b3c4d5e6f7a8b9c0d1e2f3a4b5c"
  }
}
```

**Response (Success - Web is NEVER fully trusted):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful",
  "action_time": "2025-01-11T16:00:10Z",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "requiresOtp": false,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson"
    },
    "device": {
      "trusted": false,
      "recognized": true,
      "platform": "WEB",
      "recognitionExpiresAt": "2025-01-18T16:00:10Z",
      "restrictions": [
        "OTP required for password change",
        "OTP required for email change",
        "OTP required for phone change",
        "OTP required for payment actions",
        "OTP required for account deletion"
      ]
    },
    "tokenBinding": {
      "boundToFingerprint": true,
      "boundToIpRange": true,
      "note": "Token will be invalidated if used from different browser/network"
    }
  }
}
```

---

### 6. Verify Device OTP (After New Device Detected)

**POST** `/api/v1/auth/device/verify-otp`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456",
  "trustDevice": true,
  "deviceAuth": {
    "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "nonce": "ch_newNonceForVerification",
    "timestamp": 1736611260000,
    "signature": "MEUCIQD8...",
    "platform": "IOS"
  }
}
```

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device verified and trusted",
  "action_time": "2025-01-11T16:01:00Z",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson"
    },
    "device": {
      "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
      "trusted": true,
      "securityLevel": "SECURE_ENCLAVE",
      "trustExpiresAt": "2025-02-10T16:01:00Z"
    }
  }
}
```

---

### 7. Refresh Token (With Device Signature)

**POST** `/api/v1/auth/token/refresh`

Refresh tokens also require device signature to prevent stolen refresh token usage.

**Request (Mobile):**
```json
{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "deviceAuth": {
    "deviceId": "ios_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "nonce": "ch_refreshNonce123",
    "timestamp": 1736614800000,
    "signature": "MEUCIQDx...",
    "platform": "IOS"
  }
}
```

**Request (Web):**
```json
{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "webAuth": {
    "sessionId": "ws_abc123xyz789",
    "nonce": "ch_refreshNonce123",
    "timestamp": 1736614800000,
    "signature": "MEUCIQDx...",
    "fingerprint": "fp_7f9a2b3c4d5e6f7a8b9c0d1e2f3a4b5c"
  }
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Token refreshed",
  "action_time": "2025-01-11T17:00:00Z",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600
  }
}
```

**Response (Device Mismatch):**
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Token does not belong to this device",
  "action_time": "2025-01-11T17:00:00Z",
  "data": {
    "code": "DEVICE_MISMATCH",
    "suggestion": "Please login again"
  }
}
```

---

## DEVICE KEYS DATABASE SCHEMA

```sql
-- Device Keys Table (stores public keys for verification)
CREATE TABLE device_keys (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES accounts(id) ON DELETE CASCADE,
    device_id VARCHAR(64) NOT NULL UNIQUE,
    
    -- Cryptographic identity
    public_key TEXT NOT NULL,
    key_algorithm VARCHAR(20) DEFAULT 'EC_P256',
    
    -- Device metadata (server-derived from User-Agent)
    platform VARCHAR(20) NOT NULL,  -- IOS, ANDROID, WEB
    device_name VARCHAR(255),
    security_level VARCHAR(20),     -- SECURE_ENCLAVE, STRONGBOX, TEE, SOFTWARE
    
    -- Trust status
    is_trusted BOOLEAN DEFAULT FALSE,
    trust_expires_at TIMESTAMP,
    
    -- Activity tracking
    last_active_at TIMESTAMP DEFAULT NOW(),
    last_ip_address VARCHAR(45),
    last_location VARCHAR(255),
    
    -- Audit
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    
    CONSTRAINT unique_user_device UNIQUE (user_id, device_id)
);

CREATE INDEX idx_device_keys_user_id ON device_keys(user_id);
CREATE INDEX idx_device_keys_device_id ON device_keys(device_id);
CREATE INDEX idx_device_keys_last_active ON device_keys(last_active_at);


-- Web Sessions Table (Redis is preferred, but DB fallback)
CREATE TABLE web_sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id VARCHAR(64) NOT NULL UNIQUE,
    
    -- Session data
    public_key TEXT NOT NULL,
    fingerprint_hash VARCHAR(64) NOT NULL,
    ip_address VARCHAR(45),
    
    -- Expiry
    expires_at TIMESTAMP NOT NULL,
    used_at TIMESTAMP,  -- NULL = not used yet
    
    -- Audit
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_web_sessions_session_id ON web_sessions(session_id);
CREATE INDEX idx_web_sessions_expires ON web_sessions(expires_at);
```

---

## REDIS KEYS STRUCTURE

```
# Nonce/Challenge storage (60 second TTL)
nonce:{nonce_value} = {ip_address}|{created_timestamp}

# Web session storage (10 minute TTL)
websession:{session_id} = {
  "publicKey": "MFkw...",
  "fingerprint": "fp_abc123",
  "ipAddress": "196.41.xxx.xxx",
  "createdAt": 1736611200000
}

# Public key cache (1 hour TTL)
pubkey:{device_id} = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE..."

# Device trust cache (matches trust expiry)
devicetrust:{device_id} = {
  "userId": "550e8400-e29b-41d4-a716-446655440000",
  "trusted": true,
  "expiresAt": 1739203200000
}
```

---

**Problem:** If username is used in JWT tokens, changing username requires logout (bad UX).

**Solution:** Separate system identifier from display username.

| Field | Type | Purpose | Can Change? | Used In |
|-------|------|---------|-------------|---------|
| `id` | UUID | Primary key | ❌ Never | DB relations |
| `systemUsername` | String | Internal identifier | ❌ Never | JWT tokens, internal APIs |
| `userName` | String | Public @handle | ✅ Yes | Profile URL, mentions, search, display |

**How it works:**
- `systemUsername` is auto-generated at signup (e.g., `usr_550e8400e29b41d4`)
- `userName` is user-chosen during onboarding (e.g., `alexvibes`)
- JWT tokens contain `systemUsername` → user can change `userName` without logout
- Profile URLs use `userName`: `app.com/@alexvibes`
- Mentions use `userName`: `@alexvibes`

**Username Change Flow:**
```
User changes userName from "alex" to "alexnew"
       │
       ▼
┌─────────────────────────────────┐
│ 1. Validate new userName        │
│ 2. Check availability           │
│ 3. Update userName in DB        │
│ 4. Return success               │
│                                 │
│ JWT stays valid (uses           │
│ systemUsername, unchanged)      │
│                                 │
│ NO LOGOUT REQUIRED ✅           │
└─────────────────────────────────┘
```

**Database:**
```sql
account_table:
  id              UUID PRIMARY KEY
  system_username VARCHAR(50) UNIQUE NOT NULL  -- "usr_550e8400e29b41d4"
  user_name       VARCHAR(30) UNIQUE NOT NULL  -- "alexvibes"
  ...
```

---

## Flow Diagram

```
┌─────────────────────────────────────────────────────────────────┐
│                    SIGNUP FLOW (5 Screens)                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Screen 1: Sign Up Method                                       │
│  ┌─────────────────┐                                            │
│  │ Phone/Email/    │──► OTP Sent ──► Verify OTP ──► Account     │
│  │ Google/Apple    │                              Created       │
│  └─────────────────┘                                            │
│           │                                                     │
│           ▼                                                     │
│  Screen 2: Name & Birthdate                                     │
│  ┌─────────────────┐                                            │
│  │ Display Name    │──► Validate Age ──► Save                   │
│  │ Birthdate       │                                            │
│  └─────────────────┘                                            │
│           │                                                     │
│           ▼                                                     │
│  Screen 3: Profile Setup                                        │
│  ┌─────────────────┐                                            │
│  │ Profile Pic     │──► Username Check ──► Save                 │
│  │ Username        │                                            │
│  │ Bio             │                                            │
│  └─────────────────┘                                            │
│           │                                                     │
│           ▼                                                     │
│  Screen 4: Interests                                            │
│  ┌─────────────────┐                                            │
│  │ Select 5-10     │──► Save Preferences                        │
│  │ Categories      │                                            │
│  └─────────────────┘                                            │
│           │                                                     │
│           ▼                                                     │
│  Screen 5: Complete! ──► Home Feed                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                       LOGIN FLOW                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    DEVICE CHECK FIRST                    │   │
│  │  ┌─────────────┐                                         │   │
│  │  │ Device Info │──► Known & Active? ──► YES ──► Continue │   │
│  │  └─────────────┘         │                               │   │
│  │                          NO                              │   │
│  │                          │                               │   │
│  │                          ▼                               │   │
│  │                    OTP Required First                    │   │
│  │                    (New/Inactive Device)                 │   │
│  └─────────────────────────────────────────────────────────┘   │
│                             │                                   │
│                             ▼                                   │
│  Option A: Password Login (if password set & device trusted)    │
│  ┌─────────────────┐                                            │
│  │ Identifier +    │──► Validate ──► Access Token               │
│  │ Password        │                                            │
│  └─────────────────┘                                            │
│        │                                                        │
│        └──► "Forgot Password?" ──► Password Reset Flow          │
│        └──► "Login with OTP instead" ──► Option B               │
│                                                                 │
│  Option B: Passwordless/OTP Login (always available)            │
│  ┌─────────────────┐                                            │
│  │ Phone/Email     │──► OTP Sent ──► Verify ──► Access Token    │
│  └─────────────────┘                                            │
│        │                                                        │
│        └──► "Lost access to phone/email?" ──► Account Recovery  │
│                                                                 │
│  Option C: Social Login (Google/Apple)                          │
│  ┌─────────────────┐                                            │
│  │ Google/Apple    │──► OAuth ──► Check Existing ──► Link/Create│
│  └─────────────────┘                      │                     │
│                                           ▼                     │
│                              ┌─────────────────────────┐        │
│                              │ Email matches existing? │        │
│                              │ YES → Link Account Flow │        │
│                              │ NO  → Create New        │        │
│                              └─────────────────────────┘        │
│                                                                 │
│  Account Recovery (Lost Access)                                 │
│  ┌─────────────────┐                                            │
│  │ Verify Identity │──► Support Ticket ──► Manual Review        │
│  │ (ID upload,     │                                            │
│  │  selfie, etc.)  │                                            │
│  └─────────────────┘                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

---

## SECURITY ARCHITECTURE

### Why We Need Extra Protection for OAuth Users

**The Problem:**
```
Attacker hacks victim's Google account
           │
           ▼
Attacker can access ALL apps linked to that Google
           │
           ▼
😱 If we only rely on Google, attacker owns the account
```

**Our Solution: Multi-Layer Security**

```
┌─────────────────────────────────────────────────────────────────┐
│                    NEXTGATE SECURITY LAYERS                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Layer 1: Authentication (Who are you?)                         │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ Password / OTP / Google / Apple                          │   │
│  │ (Any of these can authenticate)                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  Layer 2: Device Trust (Is this your device?)                   │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ New device? → OTP to PHONE required                      │   │
│  │ Inactive 30+ days? → OTP to PHONE required               │   │
│  │ Trusted device? → Pass through                           │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  Layer 3: Sensitive Actions (Extra verification)                │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ Change password     → OTP to PHONE                       │   │
│  │ Change email        → OTP to PHONE                       │   │
│  │ Change phone        → OTP to OLD phone + NEW phone       │   │
│  │ Link/Unlink OAuth   → OTP to PHONE                       │   │
│  │ Delete account      → OTP to PHONE + Password (if set)   │   │
│  │ Large purchases     → OTP to PHONE (configurable)        │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  🔑 KEY INSIGHT: Phone is the ULTIMATE trust anchor             │
│     Even if Google/Apple/Email is hacked, phone protects you    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

### Phone as Ultimate Recovery Method

| Auth Method Compromised | Can Attacker Access Account? |
|------------------------|------------------------------|
| Password leaked | ❌ No - needs device OTP or phone OTP |
| Email hacked | ❌ No - sensitive actions need phone OTP |
| Google hacked | ❌ No - new device needs phone OTP |
| Apple hacked | ❌ No - new device needs phone OTP |
| Phone stolen (unlocked) | ⚠️ Partial - but needs password for sensitive actions |
| Phone + Password both | ✅ Yes - full access (this is expected) |

### Mandatory Phone Verification

During onboarding, we STRONGLY encourage phone verification:

```
┌─────────────────────────────────────────────────────────────────┐
│                   ONBOARDING PHONE PROMPT                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  "Add your phone number for account security"                   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ 🔒 Recover your account if you lose access               │   │
│  │ 🔒 Get alerts about suspicious activity                  │   │
│  │ 🔒 Verify sensitive actions                              │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  [+255] [___________] [Verify]                                  │
│                                                                 │
│  [Skip for now] ← Show warning:                                 │
│  "Without a verified phone, you may lose access to your         │
│   account if you forget your password or lose access to         │
│   your Google/Apple account."                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

---

## ACCOUNT LINKING & MERGING

### When OAuth Email Matches Existing Account

**Scenario Matrix:**

| Existing Account State | OAuth Provider | Action |
|------------------------|----------------|--------|
| Email verified + password | Google (same email) | Prompt to link |
| Email verified + no password | Google (same email) | Prompt to link |
| Email unverified | Google (same email) | Auto-link (Google verified it) |
| Phone only (no email set) | Google (new email) | Prompt to add email or create new |
| Email verified but different | Google (different email) | Create new account |

### Link Account Flow

**POST** `/api/v1/auth/oauth/google`

**Request:**
```json
{
  "idToken": "eyJhbGciOiJSUzI1NiIs...",
  "deviceInfo": {
    "deviceId": "abc123-device-fingerprint",
    "deviceName": "iPhone 15 Pro",
    "deviceType": "MOBILE_IOS"
  }
}
```

**Response (Email Matches Existing - Link Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Account found with this email. Please verify to link.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "LINK_REQUIRED",
    "existingAccount": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "maskedEmail": "a***@example.com",
      "maskedPhone": "+255*****678",
      "hasPassword": true,
      "authProviders": ["PHONE", "EMAIL"],
      "createdAt": "2024-06-15T10:00:00"
    },
    "linkOptions": [
      {
        "method": "PASSWORD",
        "available": true,
        "description": "Enter your password to link"
      },
      {
        "method": "OTP_PHONE",
        "available": true,
        "description": "Get OTP on +255*****678"
      },
      {
        "method": "OTP_EMAIL",
        "available": true,
        "description": "Get OTP on a***@example.com"
      }
    ],
    "oauthProvider": "GOOGLE",
    "oauthEmail": "alex@example.com",
    "tempToken": "eyJhbGciOiJIUzI1NiIs..."
  }
}
```

---

### Confirm Account Link (with Password)

**POST** `/api/v1/auth/account/link/confirm`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "method": "PASSWORD",
  "password": "MySecurePass123!"
}
```

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Google account linked successfully",
  "action_time": "2025-01-11T16:01:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "email": "alex@example.com",
      "authProviders": ["PHONE", "EMAIL", "GOOGLE"],
      "onboardingComplete": true
    },
    "linkedProvider": {
      "provider": "GOOGLE",
      "email": "alex@example.com",
      "linkedAt": "2025-01-11T16:01:00"
    }
  }
}
```

---

### Confirm Account Link (with OTP)

**POST** `/api/v1/auth/account/link/request-otp`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "method": "OTP_PHONE"
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "OTP sent to your phone",
  "action_time": "2025-01-11T16:01:00",
  "data": {
    "otpToken": "eyJhbGciOiJIUzI1NiIs...",
    "sentTo": "+255*****678",
    "method": "SMS",
    "expiresAt": "2025-01-11T16:11:00"
  }
}
```

**POST** `/api/v1/auth/account/link/confirm`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "method": "OTP_PHONE",
  "otpCode": "123456"
}
```

---

### Auto-Link (Unverified Email)

When existing account has unverified email that matches Google email:

**Response (Auto-Linked):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Account linked and email verified",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "AUTO_LINKED",
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "email": "alex@example.com",
      "isEmailVerified": true,
      "authProviders": ["PHONE", "GOOGLE"]
    },
    "note": "Your Google account has been linked and email verified automatically"
  }
}
```

---

### Create New Account (No Match)

**Response (New Account):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Account created successfully",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "CREATED",
    "isNewUser": true,
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "systemUsername": "usr_660e8400e29b41d4",
      "userName": null,
      "email": "alex@example.com",
      "firstName": "Alex",
      "lastName": "Johnson",
      "profilePictureUrl": "https://lh3.googleusercontent.com/...",
      "isEmailVerified": true,
      "hasPassword": false,
      "authProviders": ["GOOGLE"],
      "onboardingStep": "NAME_BIRTHDATE",
      "onboardingComplete": false
    },
    "promptAddPhone": true,
    "phonePromptMessage": "Add your phone number to secure your account and enable recovery options"
  }
}
```

---

## MANAGE LINKED ACCOUNTS

### Get Linked Providers

**GET** `/api/v1/auth/providers`

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Linked providers retrieved",
  "action_time": "2025-01-11T18:00:00",
  "data": {
    "providers": [
      {
        "provider": "PHONE",
        "identifier": "+255*****678",
        "verified": true,
        "linkedAt": "2025-01-01T10:00:00",
        "isPrimary": true,
        "canUnlink": false,
        "unlinkBlockedReason": "Phone is your primary recovery method"
      },
      {
        "provider": "EMAIL",
        "identifier": "a***@example.com",
        "verified": true,
        "linkedAt": "2025-01-01T10:00:00",
        "isPrimary": false,
        "canUnlink": true
      },
      {
        "provider": "GOOGLE",
        "identifier": "a***@gmail.com",
        "verified": true,
        "linkedAt": "2025-01-11T16:01:00",
        "isPrimary": false,
        "canUnlink": true
      }
    ],
    "availableToLink": [
      {
        "provider": "APPLE",
        "description": "Sign in with Apple"
      }
    ],
    "hasPassword": true,
    "securityNote": "You have 3 ways to access your account"
  }
}
```

---

### Unlink Provider

**DELETE** `/api/v1/auth/providers/{provider}`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{
  "verificationMethod": "OTP_PHONE",
  "otpCode": "123456"
}
```

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Google account unlinked",
  "action_time": "2025-01-11T18:10:00",
  "data": {
    "unlinkedProvider": "GOOGLE",
    "remainingProviders": ["PHONE", "EMAIL"],
    "securityNote": "You can no longer sign in with Google"
  }
}
```

**Response (Cannot Unlink - Only Provider):**
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Cannot unlink your only sign-in method",
  "action_time": "2025-01-11T18:10:00",
  "data": {
    "code": "CANNOT_UNLINK_ONLY_PROVIDER",
    "suggestion": "Add another sign-in method before unlinking this one"
  }
}
```

---

### Link New Provider

**POST** `/api/v1/auth/providers/link`

**Request:**
```json
{
  "provider": "APPLE",
  "identityToken": "eyJraWQiOiJXNldjT0...",
  "authorizationCode": "c7e8a3f1b2d4..."
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Apple account linked successfully",
  "action_time": "2025-01-11T18:15:00",
  "data": {
    "linkedProvider": {
      "provider": "APPLE",
      "identifier": "a***@privaterelay.appleid.com",
      "linkedAt": "2025-01-11T18:15:00"
    },
    "totalProviders": 4
  }
}
```

---

## ACCOUNT RECOVERY (Lost Access)

### When User Can't Access Any Auth Method

**POST** `/api/v1/auth/recovery/request`

**Request:**
```json
{
  "identifier": "alexvibes",
  "recoveryReason": "LOST_PHONE",
  "contactEmail": "backup@anotheremail.com"
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Recovery request submitted",
  "action_time": "2025-01-11T20:00:00",
  "data": {
    "ticketId": "REC-2025-001234",
    "status": "PENDING_REVIEW",
    "estimatedResponseTime": "24-48 hours",
    "nextSteps": [
      "Check your backup email for instructions",
      "Prepare identity verification documents",
      "Our team will contact you within 48 hours"
    ],
    "requiredDocuments": [
      "Government-issued ID (passport, national ID)",
      "Selfie holding ID",
      "Proof of account ownership (screenshots, transaction history)"
    ]
  }
}
```

---

## SENSITIVE ACTIONS - OTP VERIFICATION

All sensitive actions require OTP to phone (regardless of how user logged in):

### Sensitive Actions List

| Action | OTP Required? | Additional Verification |
|--------|---------------|-------------------------|
| Change password | ✅ Phone OTP | Current password (if set) |
| Change email | ✅ Phone OTP | - |
| Change phone | ✅ Old phone OTP + New phone OTP | - |
| Link OAuth provider | ✅ Phone OTP | - |
| Unlink OAuth provider | ✅ Phone OTP | - |
| Delete account | ✅ Phone OTP | Password (if set) |
| View full payment methods | ✅ Phone OTP | - |
| Add payment method | ❌ No | - |
| Remove payment method | ✅ Phone OTP | - |
| Large purchase (>$100) | ⚙️ Configurable | - |
| Export account data | ✅ Phone OTP | - |
| Change security settings | ✅ Phone OTP | Password (if set) |

### Request OTP for Sensitive Action

**POST** `/api/v1/auth/sensitive-action/request-otp`

**Request:**
```json
{
  "action": "CHANGE_EMAIL",
  "newEmail": "newemail@example.com"
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "OTP sent to your phone",
  "action_time": "2025-01-11T19:00:00",
  "data": {
    "actionToken": "eyJhbGciOiJIUzI1NiIs...",
    "action": "CHANGE_EMAIL",
    "sentTo": "+255*****678",
    "method": "SMS",
    "expiresAt": "2025-01-11T19:10:00"
  }
}
```

### Confirm Sensitive Action

**POST** `/api/v1/auth/sensitive-action/confirm`

**Request:**
```json
{
  "actionToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456"
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Email updated successfully",
  "action_time": "2025-01-11T19:01:00",
  "data": {
    "action": "CHANGE_EMAIL",
    "completed": true,
    "changes": {
      "previousEmail": "a***@example.com",
      "newEmail": "n***@example.com",
      "emailVerified": false
    },
    "note": "Please verify your new email address"
  }
}
```

---

## NO PHONE? FALLBACK OPTIONS

If user didn't add phone during onboarding:

### Prompt to Add Phone (Shown on sensitive actions)

```json
{
  "success": false,
  "httpStatus": "FORBIDDEN",
  "message": "Phone verification required for this action",
  "action_time": "2025-01-11T19:00:00",
  "data": {
    "code": "PHONE_REQUIRED",
    "action": "CHANGE_EMAIL",
    "options": [
      {
        "option": "ADD_PHONE",
        "description": "Add and verify your phone number first",
        "endpoint": "/api/v1/profile/add-phone"
      },
      {
        "option": "USE_PASSWORD",
        "available": true,
        "description": "Use your password instead (less secure)"
      },
      {
        "option": "CONTACT_SUPPORT",
        "description": "Contact support for manual verification"
      }
    ]
  }
}
```

---

## SUMMARY: AUTH METHODS & SECURITY

### Complete Auth Provider Matrix

| Provider | Can Signup? | Can Login? | Provides Email? | Provides Phone? | Trust Level |
|----------|-------------|------------|-----------------|-----------------|-------------|
| Phone + OTP | ✅ | ✅ | ❌ | ✅ | HIGH |
| Email + OTP | ✅ | ✅ | ✅ | ❌ | MEDIUM |
| Email + Password | ❌ (need OTP first) | ✅ | ✅ | ❌ | MEDIUM |
| Google OAuth | ✅ | ✅ | ✅ | ❌ | MEDIUM |
| Apple OAuth | ✅ | ✅ | ✅ (may be relay) | ❌ | MEDIUM |
| Password only | ❌ | ✅ (trusted device) | ❌ | ❌ | LOW |

### Security Recommendations for Users

```json
{
  "securityScore": 75,
  "level": "GOOD",
  "recommendations": [
    {
      "priority": "HIGH",
      "action": "ADD_PHONE",
      "title": "Verify your phone number",
      "description": "Enables account recovery and secures sensitive actions",
      "completed": false
    },
    {
      "priority": "MEDIUM",
      "action": "SET_PASSWORD",
      "title": "Set a password",
      "description": "Faster login on trusted devices",
      "completed": true
    },
    {
      "priority": "LOW",
      "action": "LINK_BACKUP_EMAIL",
      "title": "Add backup email",
      "description": "Alternative recovery option",
      "completed": false
    }
  ]
}

---

## SCREEN 1: Sign Up

### 1.1 Initiate Signup (Phone)

**POST** `/api/v1/auth/signup/initiate`

**Request:**
```json
{
  "method": "PHONE",
  "phoneNumber": "+255712345678"
}
```

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "OTP sent to your phone",
  "action_time": "2025-01-11T15:20:00",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "method": "PHONE",
    "maskedIdentifier": "+255*****678",
    "expiresAt": "2025-01-11T15:30:00",
    "resendAllowedAt": "2025-01-11T15:22:00",
    "attemptsRemaining": 3
  }
}
```

**Response (Phone Already Registered):**
```json
{
  "success": false,
  "httpStatus": "CONFLICT",
  "message": "This phone number is already registered",
  "action_time": "2025-01-11T15:20:00",
  "data": {
    "code": "ACCOUNT_EXISTS",
    "field": "phoneNumber",
    "suggestion": "Please login instead"
  }
}
```

---

### 1.2 Initiate Signup (Email)

**POST** `/api/v1/auth/signup/initiate`

**Request:**
```json
{
  "method": "EMAIL",
  "email": "alex@example.com"
}
```

**Response (Success):**
```json
{
  "success": true,
  "message": "OTP sent to your email",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "method": "EMAIL",
    "maskedIdentifier": "a***@example.com",
    "expiresAt": "2025-01-11T15:30:00Z",
    "resendAllowedAt": "2025-01-11T15:22:00Z",
    "attemptsRemaining": 3
  },
  "timestamp": "2025-01-11T15:20:00Z"
}
```

---

### 1.3 Verify Signup OTP

**POST** `/api/v1/auth/signup/verify`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456"
}
```

**Response (Success - Account Created):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Account created successfully",
  "action_time": "2025-01-11T15:21:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": null,
      "phoneNumber": "+255712345678",
      "email": null,
      "isPhoneVerified": true,
      "isEmailVerified": false,
      "hasPassword": false,
      "onboardingStep": "NAME_BIRTHDATE",
      "onboardingComplete": false,
      "createdAt": "2025-01-11T15:20:00"
    }
  }
}
```

**Response (Invalid OTP):**
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Invalid OTP code",
  "action_time": "2025-01-11T15:21:00",
  "data": {
    "code": "INVALID_OTP",
    "attemptsRemaining": 2
  }
}
```

**Response (OTP Expired):**
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "OTP has expired",
  "action_time": "2025-01-11T15:35:00",
  "data": {
    "code": "OTP_EXPIRED",
    "suggestion": "Please request a new OTP"
  }
}
```

---

### 1.4 Resend OTP

**POST** `/api/v1/auth/otp/resend`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs..."
}
```

**Response (Success):**
```json
{
  "success": true,
  "message": "OTP resent successfully",
  "data": {
    "newTempToken": "eyJhbGciOiJIUzI1NiIs...",
    "maskedIdentifier": "+255*****678",
    "expiresAt": "2025-01-11T15:40:00Z",
    "resendAllowedAt": "2025-01-11T15:32:00Z",
    "attemptsRemaining": 2
  },
  "timestamp": "2025-01-11T15:30:00Z"
}
```

**Response (Too Soon):**
```json
{
  "success": false,
  "message": "Please wait before requesting another OTP",
  "error": {
    "code": "RESEND_COOLDOWN",
    "resendAllowedAt": "2025-01-11T15:32:00Z",
    "waitSeconds": 90
  },
  "timestamp": "2025-01-11T15:30:30Z"
}
```

---

### 1.5 Social Signup (Google)

**POST** `/api/v1/auth/signup/google`

**Request:**
```json
{
  "idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..."
}
```

**Response (Success - New User):**
```json
{
  "success": true,
  "message": "Account created successfully",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "isNewUser": true,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "email": "alex@gmail.com",
      "firstName": "Alex",
      "lastName": "Johnson",
      "profilePictureUrl": "https://lh3.googleusercontent.com/...",
      "isEmailVerified": true,
      "hasPassword": false,
      "authProvider": "GOOGLE",
      "onboardingStep": "NAME_BIRTHDATE",
      "onboardingComplete": false,
      "createdAt": "2025-01-11T15:20:00Z"
    }
  },
  "timestamp": "2025-01-11T15:20:00Z"
}
```

**Response (Existing User - Login):**
```json
{
  "success": true,
  "message": "Welcome back!",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "isNewUser": false,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "userName": "alexj",
      "email": "alex@gmail.com",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/...",
      "isEmailVerified": true,
      "hasPassword": true,
      "authProvider": "GOOGLE",
      "onboardingComplete": true
    }
  },
  "timestamp": "2025-01-11T15:20:00Z"
}
```

---

### 1.6 Social Signup (Apple)

**POST** `/api/v1/auth/signup/apple`

**Request:**
```json
{
  "identityToken": "eyJraWQiOiJXNldjT0...",
  "authorizationCode": "c7e8a3f1b2d4...",
  "firstName": "Alex",
  "lastName": "Johnson"
}
```

*Note: Apple only provides name on first authorization*

**Response:** Same structure as Google response

---

## SCREEN 2: Name & Birthdate

### 2.1 Save Name & Birthdate

**PUT** `/api/v1/onboarding/name-birthdate`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{
  "displayName": "Alex Johnson",
  "firstName": "Alex",
  "lastName": "Johnson",
  "birthDate": "1995-06-15"
}
```

**Response (Success):**
```json
{
  "success": true,
  "message": "Profile updated successfully",
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "displayName": "Alex Johnson",
      "firstName": "Alex",
      "lastName": "Johnson",
      "birthDate": "1995-06-15",
      "age": 29,
      "onboardingStep": "PROFILE_SETUP",
      "onboardingComplete": false
    }
  },
  "timestamp": "2025-01-11T15:22:00Z"
}
```

**Response (Underage - Below 13):**
```json
{
  "success": false,
  "message": "You must be at least 13 years old to use this app",
  "error": {
    "code": "UNDERAGE",
    "field": "birthDate",
    "minimumAge": 13
  },
  "timestamp": "2025-01-11T15:22:00Z"
}
```

**Response (Invalid Date):**
```json
{
  "success": false,
  "message": "Invalid birth date",
  "error": {
    "code": "INVALID_DATE",
    "field": "birthDate",
    "details": "Birth date cannot be in the future"
  },
  "timestamp": "2025-01-11T15:22:00Z"
}
```

---

## SCREEN 3: Profile Setup

### 3.1 Check Username Availability

**GET** `/api/v1/onboarding/username/check?username=alexvibes`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

*Note: Always returns suggestions (even when available) so user can pick alternatives*

**Response (Available):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Username is available",
  "action_time": "2025-01-11T15:23:00",
  "data": {
    "username": "alexvibes",
    "available": true,
    "valid": true,
    "suggestions": [
      "alexvibes_",
      "alexvibes1",
      "thealexvibes",
      "alexvibes_official",
      "real_alexvibes"
    ]
  }
}
```

**Response (Taken):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Username is already taken",
  "action_time": "2025-01-11T15:23:00",
  "data": {
    "username": "alex",
    "available": false,
    "valid": true,
    "suggestions": [
      "alex123",
      "alex_vibes",
      "alexcool",
      "alex2025",
      "thealex"
    ]
  }
}
```

**Response (Invalid Format):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Invalid username format",
  "action_time": "2025-01-11T15:23:00",
  "data": {
    "username": "123alex",
    "available": false,
    "valid": false,
    "validationError": "Username must start with a letter",
    "suggestions": [
      "alex123",
      "alex_user",
      "alexnew",
      "user_alex",
      "the_alex"
    ]
  }
}
```

---

### 3.2 Upload Profile Picture

**POST** `/api/v1/onboarding/profile-picture`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: multipart/form-data
```

**Request (Form Data):**
```
file: [binary image data]
```

**Response (Success):**
```json
{
  "success": true,
  "message": "Profile picture uploaded successfully",
  "data": {
    "profilePictureUrl": "https://storage.example.com/users/550e8400.../profile/pic_1736605380.jpg",
    "thumbnailUrl": "https://storage.example.com/users/550e8400.../profile/pic_1736605380_thumb.jpg"
  },
  "timestamp": "2025-01-11T15:23:00Z"
}
```

**Response (Invalid File):**
```json
{
  "success": false,
  "message": "Invalid file type",
  "error": {
    "code": "INVALID_FILE_TYPE",
    "allowedTypes": ["image/jpeg", "image/png", "image/webp"],
    "maxSizeMB": 5
  },
  "timestamp": "2025-01-11T15:23:00Z"
}
```

---

### 3.3 Save Profile Setup

**PUT** `/api/v1/onboarding/profile-setup`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{
  "userName": "alexvibes",
  "bio": "Fashion lover | Shopaholic | Event creator 🔥",
  "profilePictureUrl": "https://storage.example.com/users/550e8400.../profile/pic_1736605380.jpg"
}
```

**Response (Success):**
```json
{
  "success": true,
  "message": "Profile setup completed",
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "bio": "Fashion lover | Shopaholic | Event creator 🔥",
      "profilePictureUrl": "https://storage.example.com/users/550e8400.../profile/pic_1736605380.jpg",
      "onboardingStep": "INTERESTS",
      "onboardingComplete": false
    }
  },
  "timestamp": "2025-01-11T15:24:00Z"
}
```

**Response (Username Taken - Race Condition):**
```json
{
  "success": false,
  "message": "Username was just taken by another user",
  "error": {
    "code": "USERNAME_TAKEN",
    "field": "userName",
    "suggestions": [
      "alexvibes1",
      "alexvibes_",
      "thealexvibes"
    ]
  },
  "timestamp": "2025-01-11T15:24:00Z"
}
```

---

## SCREEN 4: Interests

### 4.1 Get Available Interest Categories

**GET** `/api/v1/onboarding/interests/categories`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Response:**
```json
{
  "success": true,
  "message": "Categories retrieved successfully",
  "data": {
    "categories": [
      {
        "id": "cat_001",
        "name": "Fashion",
        "icon": "👗",
        "iconUrl": "https://cdn.example.com/icons/fashion.png",
        "color": "#FF6B6B"
      },
      {
        "id": "cat_002",
        "name": "Electronics",
        "icon": "📱",
        "iconUrl": "https://cdn.example.com/icons/electronics.png",
        "color": "#4ECDC4"
      },
      {
        "id": "cat_003",
        "name": "Beauty & Cosmetics",
        "icon": "💄",
        "iconUrl": "https://cdn.example.com/icons/beauty.png",
        "color": "#FF69B4"
      },
      {
        "id": "cat_004",
        "name": "Food & Drinks",
        "icon": "🍔",
        "iconUrl": "https://cdn.example.com/icons/food.png",
        "color": "#F39C12"
      },
      {
        "id": "cat_005",
        "name": "Sports & Fitness",
        "icon": "⚽",
        "iconUrl": "https://cdn.example.com/icons/sports.png",
        "color": "#2ECC71"
      },
      {
        "id": "cat_006",
        "name": "Music & Dance",
        "icon": "🎵",
        "iconUrl": "https://cdn.example.com/icons/music.png",
        "color": "#9B59B6"
      },
      {
        "id": "cat_007",
        "name": "Home & Decor",
        "icon": "🏠",
        "iconUrl": "https://cdn.example.com/icons/home.png",
        "color": "#E67E22"
      },
      {
        "id": "cat_008",
        "name": "Tech & Gadgets",
        "icon": "💻",
        "iconUrl": "https://cdn.example.com/icons/tech.png",
        "color": "#3498DB"
      },
      {
        "id": "cat_009",
        "name": "Travel",
        "icon": "✈️",
        "iconUrl": "https://cdn.example.com/icons/travel.png",
        "color": "#1ABC9C"
      },
      {
        "id": "cat_010",
        "name": "Gaming",
        "icon": "🎮",
        "iconUrl": "https://cdn.example.com/icons/gaming.png",
        "color": "#8E44AD"
      },
      {
        "id": "cat_011",
        "name": "Books & Reading",
        "icon": "📚",
        "iconUrl": "https://cdn.example.com/icons/books.png",
        "color": "#D35400"
      },
      {
        "id": "cat_012",
        "name": "Art & Design",
        "icon": "🎨",
        "iconUrl": "https://cdn.example.com/icons/art.png",
        "color": "#E74C3C"
      },
      {
        "id": "cat_013",
        "name": "Health & Wellness",
        "icon": "🧘",
        "iconUrl": "https://cdn.example.com/icons/wellness.png",
        "color": "#27AE60"
      },
      {
        "id": "cat_014",
        "name": "Automotive",
        "icon": "🚗",
        "iconUrl": "https://cdn.example.com/icons/auto.png",
        "color": "#34495E"
      },
      {
        "id": "cat_015",
        "name": "Pets & Animals",
        "icon": "🐾",
        "iconUrl": "https://cdn.example.com/icons/pets.png",
        "color": "#F1C40F"
      },
      {
        "id": "cat_016",
        "name": "Photography",
        "icon": "📷",
        "iconUrl": "https://cdn.example.com/icons/photo.png",
        "color": "#7F8C8D"
      },
      {
        "id": "cat_017",
        "name": "Kids & Baby",
        "icon": "👶",
        "iconUrl": "https://cdn.example.com/icons/kids.png",
        "color": "#FFB6C1"
      },
      {
        "id": "cat_018",
        "name": "Business & Finance",
        "icon": "💼",
        "iconUrl": "https://cdn.example.com/icons/business.png",
        "color": "#2C3E50"
      },
      {
        "id": "cat_019",
        "name": "Entertainment",
        "icon": "🎬",
        "iconUrl": "https://cdn.example.com/icons/entertainment.png",
        "color": "#C0392B"
      },
      {
        "id": "cat_020",
        "name": "DIY & Crafts",
        "icon": "🛠️",
        "iconUrl": "https://cdn.example.com/icons/diy.png",
        "color": "#16A085"
      }
    ],
    "minimumSelection": 3,
    "recommendedSelection": 5,
    "maximumSelection": 15
  },
  "timestamp": "2025-01-11T15:25:00Z"
}
```

---

### 4.2 Save User Interests

**POST** `/api/v1/onboarding/interests`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{
  "categoryIds": [
    "cat_001",
    "cat_003",
    "cat_006",
    "cat_009",
    "cat_012"
  ]
}
```

**Response (Success):**
```json
{
  "success": true,
  "message": "Interests saved successfully",
  "data": {
    "selectedCount": 5,
    "interests": [
      { "id": "cat_001", "name": "Fashion" },
      { "id": "cat_003", "name": "Beauty & Cosmetics" },
      { "id": "cat_006", "name": "Music & Dance" },
      { "id": "cat_009", "name": "Travel" },
      { "id": "cat_012", "name": "Art & Design" }
    ],
    "onboardingStep": "COMPLETE",
    "onboardingComplete": true
  },
  "timestamp": "2025-01-11T15:26:00Z"
}
```

**Response (Too Few Selected):**
```json
{
  "success": false,
  "message": "Please select at least 3 interests",
  "error": {
    "code": "MINIMUM_NOT_MET",
    "selectedCount": 1,
    "minimumRequired": 3
  },
  "timestamp": "2025-01-11T15:26:00Z"
}
```

---

### 4.3 Skip Interests (Optional)

**POST** `/api/v1/onboarding/interests/skip`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{}
```

**Response:**
```json
{
  "success": true,
  "message": "Interests skipped. You can update them later in settings.",
  "data": {
    "onboardingStep": "COMPLETE",
    "onboardingComplete": true,
    "reminder": "We'll show you general content. Update your interests anytime for a personalized feed!"
  },
  "timestamp": "2025-01-11T15:26:00Z"
}
```

---

## SCREEN 5: Complete Onboarding

### 5.1 Get Onboarding Summary & Suggestions

**GET** `/api/v1/onboarding/complete`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Response:**
```json
{
  "success": true,
  "message": "Welcome to the app! 🎉",
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/users/550e8400.../profile/pic.jpg",
      "bio": "Fashion lover | Shopaholic | Event creator 🔥",
      "onboardingComplete": true,
      "hasPassword": false
    },
    "suggestions": {
      "accountsToFollow": [
        {
          "id": "user_001",
          "userName": "fashionista",
          "displayName": "Fashion Hub",
          "profilePictureUrl": "https://storage.example.com/...",
          "bio": "Latest fashion trends",
          "isVerified": true,
          "followerCount": 12500,
          "matchReason": "Based on your interest in Fashion"
        },
        {
          "id": "user_002",
          "userName": "beautyguru",
          "displayName": "Beauty Tips Daily",
          "profilePictureUrl": "https://storage.example.com/...",
          "bio": "Makeup tutorials & reviews",
          "isVerified": true,
          "followerCount": 8300,
          "matchReason": "Based on your interest in Beauty & Cosmetics"
        }
      ],
      "shopsToFollow": [
        {
          "id": "shop_001",
          "name": "Style Studio",
          "logoUrl": "https://storage.example.com/...",
          "category": "Fashion",
          "rating": 4.8,
          "productCount": 156,
          "matchReason": "Top rated in Fashion"
        }
      ],
      "upcomingEvents": [
        {
          "id": "event_001",
          "title": "Summer Fashion Show",
          "coverImageUrl": "https://storage.example.com/...",
          "startDate": "2025-01-20T18:00:00Z",
          "attendeeCount": 234,
          "matchReason": "Fashion event near you"
        }
      ]
    },
    "nextSteps": [
      {
        "action": "FOLLOW_ACCOUNTS",
        "title": "Follow 5 accounts",
        "description": "Get started by following accounts you like",
        "reward": null
      },
      {
        "action": "SET_PASSWORD",
        "title": "Set a password",
        "description": "For faster login next time",
        "reward": null
      },
      {
        "action": "FIRST_POST",
        "title": "Create your first post",
        "description": "Share something with the community",
        "reward": null
      }
    ]
  },
  "timestamp": "2025-01-11T15:27:00Z"
}
```

---

## LOGIN FLOWS (With Device Trust)

### Login Option A: Password Login

**POST** `/api/v1/auth/login/password`

**Request:**
```json
{
  "identifier": "alexvibes",
  "password": "MySecurePass123!",
  "deviceInfo": {
    "deviceId": "abc123-device-fingerprint",
    "deviceName": "iPhone 15 Pro",
    "deviceType": "MOBILE_IOS",
    "appVersion": "1.2.0"
  }
}
```

*identifier can be: username, email, or phone number*

**Response (Success - Trusted Device):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "requiresOtp": false,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/...",
      "email": "alex@example.com",
      "phoneNumber": "+255712345678",
      "isEmailVerified": true,
      "isPhoneVerified": true,
      "hasPassword": true,
      "onboardingComplete": true
    },
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "trusted": true,
      "trustExpiresAt": "2025-02-10T16:00:00"
    }
  }
}
```

**Response (New Device - OTP Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "New device detected. Please verify with OTP.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "requiresOtp": true,
    "otpReason": "NEW_DEVICE",
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "otpSentTo": "+255*****678",
    "otpMethod": "SMS",
    "expiresAt": "2025-01-11T16:10:00",
    "device": {
      "deviceId": "xyz789-new-device",
      "deviceName": "Chrome on Windows",
      "isNew": true
    },
    "securityNote": "We detected a login from a new device. For your security, please verify with OTP."
  }
}
```

**Response (Inactive Device 30+ days - OTP Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device inactive for 30+ days. Please verify with OTP.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "requiresOtp": true,
    "otpReason": "INACTIVE_DEVICE",
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "otpSentTo": "+255*****678",
    "otpMethod": "SMS",
    "expiresAt": "2025-01-11T16:10:00",
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "lastActiveAt": "2024-12-01T10:00:00",
      "inactiveDays": 41
    },
    "securityNote": "This device hasn't been used in 41 days. Please verify it's you."
  }
}
```

**Response (Wrong Password):**
```json
{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Invalid credentials",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "code": "INVALID_CREDENTIALS",
    "attemptsRemaining": 4,
    "suggestion": "Forgot password? Use OTP login instead"
  }
}
```

**Response (Account Locked):**
```json
{
  "success": false,
  "httpStatus": "LOCKED",
  "message": "Account temporarily locked due to multiple failed attempts",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "code": "ACCOUNT_LOCKED",
    "unlockAt": "2025-01-11T16:30:00",
    "suggestion": "Try OTP login or wait 30 minutes"
  }
}
```

**Response (No Password Set):**
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "No password set for this account",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "code": "NO_PASSWORD",
    "suggestion": "Use OTP login or social login"
  }
}
```

---

### Login Option B: OTP Login (Passwordless)

#### B.1 Request OTP

**POST** `/api/v1/auth/login/otp/request`

**Request:**
```json
{
  "identifier": "+255712345678",
  "deviceInfo": {
    "deviceId": "abc123-device-fingerprint",
    "deviceName": "iPhone 15 Pro",
    "deviceType": "MOBILE_IOS",
    "appVersion": "1.2.0"
  }
}
```

*identifier can be: email or phone number*

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "OTP sent to your phone",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "method": "SMS",
    "maskedIdentifier": "+255*****678",
    "expiresAt": "2025-01-11T16:10:00",
    "resendAllowedAt": "2025-01-11T16:02:00",
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "isNew": false,
      "lastActiveAt": "2025-01-10T14:30:00"
    }
  }
}
```

**Response (User Not Found):**
```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "No account found with this phone number",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "code": "USER_NOT_FOUND",
    "suggestion": "Would you like to create an account?",
    "createAccountUrl": "/api/v1/auth/signup/initiate"
  }
}
```

#### B.2 Verify OTP Login

**POST** `/api/v1/auth/login/otp/verify`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456",
  "trustDevice": true,
  "deviceInfo": {
    "deviceId": "abc123-device-fingerprint",
    "deviceName": "iPhone 15 Pro",
    "deviceType": "MOBILE_IOS",
    "appVersion": "1.2.0"
  }
}
```

**Response (Success - Device Trusted):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful",
  "action_time": "2025-01-11T16:01:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/...",
      "hasPassword": false,
      "onboardingComplete": true
    },
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "trusted": true,
      "trustExpiresAt": "2025-02-10T16:01:00"
    },
    "promptSetPassword": true,
    "passwordPromptMessage": "Set a password for faster login on trusted devices"
  }
}
```

**Response (Success - Device NOT Trusted by user choice):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful (device not saved)",
  "action_time": "2025-01-11T16:01:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/...",
      "hasPassword": false,
      "onboardingComplete": true
    },
    "device": {
      "trusted": false,
      "note": "This device will require OTP on next login"
    }
  }
}
```

---

### Login Option C: Social Login (Google/Apple)

**POST** `/api/v1/auth/oauth/google`

**Request:**
```json
{
  "idToken": "eyJhbGciOiJSUzI1NiIs...",
  "deviceInfo": {
    "deviceId": "abc123-device-fingerprint",
    "deviceName": "iPhone 15 Pro",
    "deviceType": "MOBILE_IOS",
    "appVersion": "1.2.0"
  }
}
```

**Response (Existing User - Trusted Device):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Welcome back!",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "LOGIN",
    "isNewUser": false,
    "requiresOtp": false,
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "email": "alex@gmail.com",
      "profilePictureUrl": "https://storage.example.com/...",
      "isEmailVerified": true,
      "hasPassword": true,
      "authProviders": ["PHONE", "EMAIL", "GOOGLE"],
      "onboardingComplete": true
    },
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "trusted": true,
      "trustExpiresAt": "2025-02-10T16:00:00"
    }
  }
}
```

**Response (Existing User - New Device - OTP Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "New device detected. Please verify with OTP.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "DEVICE_VERIFICATION_REQUIRED",
    "isNewUser": false,
    "requiresOtp": true,
    "otpReason": "NEW_DEVICE",
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "otpSentTo": "+255*****678",
    "otpMethod": "SMS",
    "expiresAt": "2025-01-11T16:10:00",
    "user": {
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/..."
    },
    "device": {
      "deviceId": "xyz789-new-device",
      "deviceName": "Chrome on Windows",
      "isNew": true
    },
    "securityNote": "Even though you signed in with Google, we need to verify this new device."
  }
}
```

**Response (Existing User - No Phone - Fallback Options):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "New device detected. Please verify to continue.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "VERIFICATION_REQUIRED_NO_PHONE",
    "isNewUser": false,
    "requiresVerification": true,
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "userName": "alexvibes",
      "displayName": "Alex Johnson"
    },
    "device": {
      "deviceId": "xyz789-new-device",
      "deviceName": "Chrome on Windows",
      "isNew": true
    },
    "verificationOptions": [
      {
        "method": "ADD_PHONE",
        "description": "Add phone number to receive OTP",
        "recommended": true
      },
      {
        "method": "OTP_EMAIL",
        "description": "Send OTP to a***@gmail.com",
        "available": true,
        "lessSecure": true,
        "note": "Email is less secure than phone"
      },
      {
        "method": "PASSWORD",
        "available": true,
        "description": "Enter your password"
      }
    ],
    "securityNote": "For better security, we recommend adding a phone number."
  }
}
```

**Response (New User - Account Created):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Account created successfully",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "CREATED",
    "isNewUser": true,
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "systemUsername": "usr_660e8400e29b41d4",
      "userName": null,
      "email": "alex@gmail.com",
      "firstName": "Alex",
      "lastName": "Johnson",
      "profilePictureUrl": "https://lh3.googleusercontent.com/...",
      "isEmailVerified": true,
      "hasPassword": false,
      "authProviders": ["GOOGLE"],
      "onboardingStep": "NAME_BIRTHDATE",
      "onboardingComplete": false
    },
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "trusted": true,
      "trustExpiresAt": "2025-02-10T16:00:00",
      "note": "First device is automatically trusted"
    },
    "promptAddPhone": true,
    "phonePromptMessage": "Add your phone number to secure your account"
  }
}
```

**Response (Email Matches Existing - Link Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Account found with this email. Please verify to link.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "action": "LINK_REQUIRED",
    "isNewUser": false,
    "existingAccount": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "maskedEmail": "a***@example.com",
      "maskedPhone": "+255*****678",
      "hasPassword": true,
      "authProviders": ["PHONE", "EMAIL"],
      "createdAt": "2024-06-15T10:00:00"
    },
    "linkOptions": [
      {
        "method": "PASSWORD",
        "available": true,
        "description": "Enter your password to link"
      },
      {
        "method": "OTP_PHONE",
        "available": true,
        "description": "Get OTP on +255*****678"
      },
      {
        "method": "OTP_EMAIL",
        "available": true,
        "description": "Get OTP on a***@example.com"
      }
    ],
    "oauthProvider": "GOOGLE",
    "oauthEmail": "alex@gmail.com",
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "note": "Device will be trusted after account link"
    }
  }
}
```

---

### Apple Sign In

**POST** `/api/v1/auth/oauth/apple`

**Request:**
```json
{
  "identityToken": "eyJraWQiOiJXNldjT0...",
  "authorizationCode": "c7e8a3f1b2d4...",
  "firstName": "Alex",
  "lastName": "Johnson",
  "deviceInfo": {
    "deviceId": "abc123-device-fingerprint",
    "deviceName": "iPhone 15 Pro",
    "deviceType": "MOBILE_IOS",
    "appVersion": "1.2.0"
  }
}
```

*Note: Apple only provides name on first authorization. Store it!*

**Responses:** Same structure as Google OAuth responses above.

---

### Verify Device OTP (After any login triggers device verification)

**POST** `/api/v1/auth/login/verify-device`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456",
  "trustDevice": true,
  "deviceInfo": {
    "deviceId": "xyz789-new-device",
    "deviceName": "Chrome on Windows",
    "deviceType": "WEB_BROWSER"
  }
}
```

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device verified and trusted",
  "action_time": "2025-01-11T16:02:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/...",
      "onboardingComplete": true
    },
    "device": {
      "deviceId": "xyz789-new-device",
      "deviceName": "Chrome on Windows",
      "trusted": true,
      "trustExpiresAt": "2025-02-10T16:02:00"
    }
  }
}
```

---

## POST-ONBOARDING: Set Password (Optional)

**POST** `/api/v1/auth/password/set`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{
  "newPassword": "MySecurePass123!",
  "confirmPassword": "MySecurePass123!"
}
```

**Response (Success):**
```json
{
  "success": true,
  "message": "Password set successfully. You can now use password login.",
  "data": {
    "hasPassword": true,
    "passwordStrength": {
      "score": 85,
      "level": "STRONG",
      "feedback": "Great password!"
    }
  },
  "timestamp": "2025-01-11T16:05:00Z"
}
```

**Response (Weak Password):**
```json
{
  "success": false,
  "message": "Password is too weak",
  "error": {
    "code": "WEAK_PASSWORD",
    "passwordStrength": {
      "score": 40,
      "level": "WEAK",
      "feedback": "Add uppercase letters and special characters"
    },
    "requirements": [
      "At least 8 characters",
      "At least one uppercase letter",
      "At least one lowercase letter",
      "At least one number",
      "At least one special character (@$!%*?&#)"
    ]
  },
  "timestamp": "2025-01-11T16:05:00Z"
}
```

---

## TOKEN MANAGEMENT

### Refresh Token

**POST** `/api/v1/auth/token/refresh`

**Request:**
```json
{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
```

**Response (Success):**
```json
{
  "success": true,
  "message": "Token refreshed successfully",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600
  },
  "timestamp": "2025-01-11T17:00:00Z"
}
```

**Response (Invalid/Expired Refresh Token):**
```json
{
  "success": false,
  "message": "Session expired. Please login again.",
  "error": {
    "code": "REFRESH_TOKEN_EXPIRED"
  },
  "timestamp": "2025-01-11T17:00:00Z"
}
```

---

### Logout

**POST** `/api/v1/auth/logout`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
  "logoutAllDevices": false
}
```

**Response:**
```json
{
  "success": true,
  "message": "Logged out successfully",
  "data": null,
  "timestamp": "2025-01-11T18:00:00Z"
}
```

---

## PASSWORD RESET

### Request Password Reset

**POST** `/api/v1/auth/password/reset/request`

**Request:**
```json
{
  "identifier": "alex@example.com"
}
```

**Response:**
```json
{
  "success": true,
  "message": "Password reset OTP sent to your email",
  "data": {
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "maskedIdentifier": "a***@example.com",
    "expiresAt": "2025-01-11T16:10:00Z"
  },
  "timestamp": "2025-01-11T16:00:00Z"
}
```

### Verify Reset OTP & Set New Password

**POST** `/api/v1/auth/password/reset/confirm`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456",
  "newPassword": "MyNewSecurePass123!",
  "confirmPassword": "MyNewSecurePass123!"
}
```

**Response:**
```json
{
  "success": true,
  "message": "Password reset successfully. Please login with your new password.",
  "data": null,
  "timestamp": "2025-01-11T16:02:00Z"
}
```

---

## GET CURRENT USER (Check Auth Status)

**GET** `/api/v1/auth/me`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "User retrieved successfully",
  "action_time": "2025-01-11T18:00:00",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "systemUsername": "usr_550e8400e29b41d4",
    "userName": "alexvibes",
    "displayName": "Alex Johnson",
    "firstName": "Alex",
    "lastName": "Johnson",
    "email": "alex@example.com",
    "phoneNumber": "+255712345678",
    "profilePictureUrl": "https://storage.example.com/...",
    "bio": "Fashion lover | Shopaholic | Event creator 🔥",
    "birthDate": "1995-06-15",
    "isEmailVerified": true,
    "isPhoneVerified": true,
    "hasPassword": true,
    "authProviders": ["PHONE", "GOOGLE"],
    "onboardingComplete": true,
    "onboardingStep": "COMPLETE",
    "interests": [
      { "id": "cat_001", "name": "Fashion" },
      { "id": "cat_003", "name": "Beauty & Cosmetics" }
    ],
    "createdAt": "2025-01-11T15:20:00",
    "updatedAt": "2025-01-11T15:27:00"
  }
}
```

---

## ONBOARDING STATUS CHECK

**GET** `/api/v1/onboarding/status`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Response (Incomplete):**
```json
{
  "success": true,
  "message": "Onboarding in progress",
  "data": {
    "onboardingComplete": false,
    "currentStep": "PROFILE_SETUP",
    "completedSteps": ["SIGNUP", "NAME_BIRTHDATE"],
    "remainingSteps": ["PROFILE_SETUP", "INTERESTS"],
    "progress": 50
  },
  "timestamp": "2025-01-11T15:23:00Z"
}
```

**Response (Complete):**
```json
{
  "success": true,
  "message": "Onboarding complete",
  "data": {
    "onboardingComplete": true,
    "currentStep": "COMPLETE",
    "completedSteps": ["SIGNUP", "NAME_BIRTHDATE", "PROFILE_SETUP", "INTERESTS"],
    "remainingSteps": [],
    "progress": 100
  },
  "timestamp": "2025-01-11T15:27:00Z"
}
```

---

## ERROR RESPONSE FORMAT (Standard)

All error responses follow `GlobeFailureResponseBuilder` structure:

```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Human readable error message",
  "action_time": "2025-01-11T15:20:00",
  "data": {
    "code": "ERROR_CODE",
    "field": "fieldName",
    "details": "Additional details if any",
    "suggestion": "What user can do"
  }
}
```

### Common Error Codes

| Code | HTTP Status | Description |
|------|-------------|-------------|
| `VALIDATION_ERROR` | BAD_REQUEST | Request validation failed |
| `INVALID_OTP` | BAD_REQUEST | OTP code is incorrect |
| `OTP_EXPIRED` | BAD_REQUEST | OTP has expired |
| `INVALID_CREDENTIALS` | UNAUTHORIZED | Wrong password |
| `TOKEN_EXPIRED` | UNAUTHORIZED | Access token expired |
| `REFRESH_TOKEN_EXPIRED` | UNAUTHORIZED | Refresh token expired |
| `UNAUTHORIZED` | UNAUTHORIZED | Not authenticated |
| `FORBIDDEN` | FORBIDDEN | Not allowed |
| `USER_NOT_FOUND` | NOT_FOUND | User doesn't exist |
| `ACCOUNT_EXISTS` | CONFLICT | Account already exists |
| `USERNAME_TAKEN` | CONFLICT | Username is taken |
| `PHONE_ALREADY_REGISTERED` | CONFLICT | Phone belongs to another account |
| `PHONE_TEMPORARILY_RESERVED` | CONFLICT | Phone claimed but unverified |
| `ACCOUNT_LOCKED` | LOCKED | Too many failed attempts |
| `RESEND_COOLDOWN` | TOO_MANY_REQUESTS | Wait before resending OTP |
| `RATE_LIMITED` | TOO_MANY_REQUESTS | Too many requests |
| `WEAK_PASSWORD` | UNPROCESSABLE_ENTITY | Password doesn't meet requirements |
| `UNDERAGE` | UNPROCESSABLE_ENTITY | User is under minimum age |
| `MIN_INTERESTS_REQUIRED` | UNPROCESSABLE_ENTITY | Must have minimum interests |
| `MAX_INTERESTS_REACHED` | UNPROCESSABLE_ENTITY | Maximum interests reached |
| `SERVER_ERROR` | INTERNAL_SERVER_ERROR | Internal server error |

---

## ENUMS REFERENCE

### OnboardingStep
```
SIGNUP
NAME_BIRTHDATE
PROFILE_SETUP
INTERESTS
COMPLETE
```

### AuthProvider
```
PHONE
EMAIL
GOOGLE
APPLE
```

### OTP Purpose
```
SIGNUP_VERIFICATION
LOGIN_OTP
PASSWORD_RESET
EMAIL_VERIFICATION
PHONE_VERIFICATION
```

---

## SUMMARY: What's New vs Existing

### New Endpoints
- `POST /api/v1/auth/signup/initiate` (replaces `/register`)
- `POST /api/v1/auth/signup/verify` (replaces `/verify-otp`)
- `POST /api/v1/auth/signup/google`
- `POST /api/v1/auth/signup/apple`
- `PUT /api/v1/onboarding/name-birthdate`
- `GET /api/v1/onboarding/username/check`
- `POST /api/v1/onboarding/profile-picture`
- `PUT /api/v1/onboarding/profile-setup`
- `GET /api/v1/onboarding/interests/categories`
- `POST /api/v1/onboarding/interests`
- `POST /api/v1/onboarding/interests/skip`
- `GET /api/v1/onboarding/complete`
- `POST /api/v1/auth/login/password`
- `POST /api/v1/auth/login/otp/request`
- `POST /api/v1/auth/login/otp/verify`
- `POST /api/v1/auth/password/set`
- `GET /api/v1/auth/me`
- `GET /api/v1/onboarding/status`

### Modified Endpoints
- `POST /api/v1/auth/otp/resend` (updated response)
- `POST /api/v1/auth/token/refresh` (same, just standardized response)
- `POST /api/v1/auth/password/reset/request` (updated from `/psw-reset-otp`)
- `POST /api/v1/auth/password/reset/confirm` (updated from `/reset-password`)

### Deprecated/Removed
- `POST /api/v1/auth/register` (replaced by `/signup/initiate`)
- `POST /api/v1/auth/login` (replaced by password/OTP specific endpoints)

---

---

## INTEREST SYSTEM

The interest system tracks user preferences through two methods:
1. **Explicit**: User picks during onboarding (visible to user)
2. **Implicit**: Silent tracking based on user behavior (invisible to user)

Scores update silently in the background as users interact with content.

---

### Interest Scoring Logic

| Action | Score | Notes |
|--------|-------|-------|
| Explicitly selected (onboarding) | +100 | Base score for picked interests |
| View post | +1 | Quick glance |
| Like post | +3 | Shows interest |
| Comment on post | +5 | Higher engagement |
| Share post | +7 | Strong signal |
| Save/Bookmark | +5 | Wants to revisit |
| Follow user in category | +10 | Committed interest |
| View product | +2 | Shopping interest |
| Add to cart | +8 | Purchase intent |
| Purchase product | +15 | Strongest signal |
| View event | +2 | Curious |
| Attend event | +12 | Committed |
| Search term | +4 | Active seeking |
| Time spent (per 30sec) | +1 | Passive engagement |
| Scroll past quickly | -1 | Not interested |
| Hide/Not interested | -20 | Explicit dislike |
| Report content | -30 | Strong negative |

**Score Decay**: 10% reduction per week of no interaction (keeps recommendations fresh)

**Max Score**: 1000 per category

---

## ADMIN: Interest Category Management

### Create Category

**POST** `/api/v1/admin/interests/categories`

**Headers:**
```
Authorization: Bearer {admin_token}
```

**Request:**
```json
{
  "name": "Fashion",
  "icon": "👗",
  "iconUrl": "https://cdn.example.com/icons/fashion.png",
  "color": "#FF6B6B",
  "keywords": ["clothes", "style", "outfit", "wear", "dress"],
  "displayOrder": 1,
  "isActive": true
}
```

**Response:**
```json
{
  "success": true,
  "message": "Category created successfully",
  "data": {
    "id": "cat_001",
    "name": "Fashion",
    "icon": "👗",
    "iconUrl": "https://cdn.example.com/icons/fashion.png",
    "color": "#FF6B6B",
    "keywords": ["clothes", "style", "outfit", "wear", "dress"],
    "displayOrder": 1,
    "isActive": true,
    "createdAt": "2025-01-11T16:00:00Z",
    "updatedAt": "2025-01-11T16:00:00Z"
  },
  "timestamp": "2025-01-11T16:00:00Z"
}
```

---

### List All Categories (Admin View)

**GET** `/api/v1/admin/interests/categories?includeInactive=true`

**Response:**
```json
{
  "success": true,
  "message": "Categories retrieved successfully",
  "data": {
    "categories": [
      {
        "id": "cat_001",
        "name": "Fashion",
        "icon": "👗",
        "iconUrl": "https://cdn.example.com/icons/fashion.png",
        "color": "#FF6B6B",
        "keywords": ["clothes", "style", "outfit"],
        "displayOrder": 1,
        "isActive": true,
        "stats": {
          "usersExplicit": 15420,
          "usersImplicit": 28750,
          "totalEngagements": 89500,
          "postsTagged": 8920,
          "productsTagged": 3450,
          "shopsTagged": 234,
          "eventsTagged": 89
        },
        "createdAt": "2025-01-01T10:00:00Z",
        "updatedAt": "2025-01-10T14:30:00Z"
      },
      {
        "id": "cat_002",
        "name": "Electronics",
        "icon": "📱",
        "iconUrl": "https://cdn.example.com/icons/electronics.png",
        "color": "#4ECDC4",
        "keywords": ["phone", "laptop", "gadget", "tech"],
        "displayOrder": 2,
        "isActive": true,
        "stats": {
          "usersExplicit": 12300,
          "usersImplicit": 31000,
          "totalEngagements": 125000,
          "postsTagged": 5600,
          "productsTagged": 8900,
          "shopsTagged": 456,
          "eventsTagged": 23
        },
        "createdAt": "2025-01-01T10:00:00Z",
        "updatedAt": "2025-01-10T14:30:00Z"
      }
    ],
    "totalCategories": 20,
    "activeCategories": 18,
    "inactiveCategories": 2
  },
  "timestamp": "2025-01-11T16:00:00Z"
}
```

---

### Update Category

**PUT** `/api/v1/admin/interests/categories/{categoryId}`

**Request:**
```json
{
  "name": "Fashion & Style",
  "icon": "👗",
  "iconUrl": "https://cdn.example.com/icons/fashion-v2.png",
  "color": "#FF5252",
  "keywords": ["clothes", "style", "outfit", "wear", "dress", "apparel"],
  "isActive": true
}
```

**Response:**
```json
{
  "success": true,
  "message": "Category updated successfully",
  "data": {
    "id": "cat_001",
    "name": "Fashion & Style",
    "icon": "👗",
    "iconUrl": "https://cdn.example.com/icons/fashion-v2.png",
    "color": "#FF5252",
    "keywords": ["clothes", "style", "outfit", "wear", "dress", "apparel"],
    "displayOrder": 1,
    "isActive": true,
    "updatedAt": "2025-01-11T16:05:00Z"
  },
  "timestamp": "2025-01-11T16:05:00Z"
}
```

---

### Reorder Categories

**PUT** `/api/v1/admin/interests/categories/reorder`

**Request:**
```json
{
  "order": [
    { "categoryId": "cat_003", "displayOrder": 1 },
    { "categoryId": "cat_001", "displayOrder": 2 },
    { "categoryId": "cat_002", "displayOrder": 3 }
  ]
}
```

**Response:**
```json
{
  "success": true,
  "message": "Categories reordered successfully",
  "data": {
    "updated": 3
  },
  "timestamp": "2025-01-11T16:10:00Z"
}
```

---

### Deactivate Category (Soft Delete)

**DELETE** `/api/v1/admin/interests/categories/{categoryId}`

**Response:**
```json
{
  "success": true,
  "message": "Category deactivated successfully",
  "data": {
    "id": "cat_015",
    "name": "Outdated Category",
    "isActive": false,
    "deactivatedAt": "2025-01-11T16:15:00Z",
    "note": "Category hidden from users. Existing user data preserved."
  },
  "timestamp": "2025-01-11T16:15:00Z"
}
```

---

### Get Category Analytics

**GET** `/api/v1/admin/interests/categories/{categoryId}/analytics?period=30d`

**Response:**
```json
{
  "success": true,
  "message": "Analytics retrieved successfully",
  "data": {
    "categoryId": "cat_001",
    "categoryName": "Fashion",
    "period": "LAST_30_DAYS",
    "overview": {
      "totalUsers": 44170,
      "explicitUsers": 15420,
      "implicitUsers": 28750,
      "averageScore": 67.5,
      "totalEngagements": 89500
    },
    "trend": {
      "direction": "UP",
      "percentageChange": 12.5,
      "newUsersThisPeriod": 2340
    },
    "engagement": {
      "likes": 34000,
      "comments": 12500,
      "shares": 8900,
      "saves": 15600,
      "purchases": 3200
    },
    "topContent": {
      "topPosts": [
        { "postId": "post_123", "engagements": 4500 },
        { "postId": "post_456", "engagements": 3200 }
      ],
      "topProducts": [
        { "productId": "prod_789", "sales": 234 },
        { "productId": "prod_012", "sales": 189 }
      ]
    },
    "demographics": {
      "ageGroups": [
        { "range": "13-17", "percentage": 15 },
        { "range": "18-24", "percentage": 35 },
        { "range": "25-34", "percentage": 30 },
        { "range": "35-44", "percentage": 12 },
        { "range": "45+", "percentage": 8 }
      ]
    }
  },
  "timestamp": "2025-01-11T16:20:00Z"
}
```

---

## PUBLIC: Interest Categories

### Get Active Categories (Onboarding & Settings)

**GET** `/api/v1/interests/categories`

*No auth required for onboarding, shows only active categories*

**Response:**
```json
{
  "success": true,
  "message": "Categories retrieved successfully",
  "data": {
    "categories": [
      {
        "id": "cat_001",
        "name": "Fashion",
        "icon": "👗",
        "iconUrl": "https://cdn.example.com/icons/fashion.png",
        "color": "#FF6B6B"
      },
      {
        "id": "cat_002",
        "name": "Electronics",
        "icon": "📱",
        "iconUrl": "https://cdn.example.com/icons/electronics.png",
        "color": "#4ECDC4"
      },
      {
        "id": "cat_003",
        "name": "Beauty & Cosmetics",
        "icon": "💄",
        "iconUrl": "https://cdn.example.com/icons/beauty.png",
        "color": "#FF69B4"
      },
      {
        "id": "cat_004",
        "name": "Food & Drinks",
        "icon": "🍔",
        "iconUrl": "https://cdn.example.com/icons/food.png",
        "color": "#F39C12"
      },
      {
        "id": "cat_005",
        "name": "Sports & Fitness",
        "icon": "⚽",
        "iconUrl": "https://cdn.example.com/icons/sports.png",
        "color": "#2ECC71"
      },
      {
        "id": "cat_006",
        "name": "Music & Dance",
        "icon": "🎵",
        "iconUrl": "https://cdn.example.com/icons/music.png",
        "color": "#9B59B6"
      },
      {
        "id": "cat_007",
        "name": "Home & Decor",
        "icon": "🏠",
        "iconUrl": "https://cdn.example.com/icons/home.png",
        "color": "#E67E22"
      },
      {
        "id": "cat_008",
        "name": "Tech & Gadgets",
        "icon": "💻",
        "iconUrl": "https://cdn.example.com/icons/tech.png",
        "color": "#3498DB"
      },
      {
        "id": "cat_009",
        "name": "Travel",
        "icon": "✈️",
        "iconUrl": "https://cdn.example.com/icons/travel.png",
        "color": "#1ABC9C"
      },
      {
        "id": "cat_010",
        "name": "Gaming",
        "icon": "🎮",
        "iconUrl": "https://cdn.example.com/icons/gaming.png",
        "color": "#8E44AD"
      },
      {
        "id": "cat_011",
        "name": "Books & Reading",
        "icon": "📚",
        "iconUrl": "https://cdn.example.com/icons/books.png",
        "color": "#D35400"
      },
      {
        "id": "cat_012",
        "name": "Art & Design",
        "icon": "🎨",
        "iconUrl": "https://cdn.example.com/icons/art.png",
        "color": "#E74C3C"
      },
      {
        "id": "cat_013",
        "name": "Health & Wellness",
        "icon": "🧘",
        "iconUrl": "https://cdn.example.com/icons/wellness.png",
        "color": "#27AE60"
      },
      {
        "id": "cat_014",
        "name": "Automotive",
        "icon": "🚗",
        "iconUrl": "https://cdn.example.com/icons/auto.png",
        "color": "#34495E"
      },
      {
        "id": "cat_015",
        "name": "Pets & Animals",
        "icon": "🐾",
        "iconUrl": "https://cdn.example.com/icons/pets.png",
        "color": "#F1C40F"
      },
      {
        "id": "cat_016",
        "name": "Photography",
        "icon": "📷",
        "iconUrl": "https://cdn.example.com/icons/photo.png",
        "color": "#7F8C8D"
      },
      {
        "id": "cat_017",
        "name": "Kids & Baby",
        "icon": "👶",
        "iconUrl": "https://cdn.example.com/icons/kids.png",
        "color": "#FFB6C1"
      },
      {
        "id": "cat_018",
        "name": "Business & Finance",
        "icon": "💼",
        "iconUrl": "https://cdn.example.com/icons/business.png",
        "color": "#2C3E50"
      },
      {
        "id": "cat_019",
        "name": "Entertainment",
        "icon": "🎬",
        "iconUrl": "https://cdn.example.com/icons/entertainment.png",
        "color": "#C0392B"
      },
      {
        "id": "cat_020",
        "name": "DIY & Crafts",
        "icon": "🛠️",
        "iconUrl": "https://cdn.example.com/icons/diy.png",
        "color": "#16A085"
      }
    ],
    "selectionRules": {
      "minimum": 3,
      "recommended": 5,
      "maximum": 15,
      "canSkip": true
    }
  },
  "timestamp": "2025-01-11T15:25:00Z"
}
```

---

## USER: Interest Management

### Get My Interests

**GET** `/api/v1/interests/me`

**Headers:**
```
Authorization: Bearer {access_token}
```

**Response:**
```json
{
  "success": true,
  "message": "Interests retrieved successfully",
  "data": {
    "explicit": [
      {
        "categoryId": "cat_001",
        "categoryName": "Fashion",
        "icon": "👗",
        "color": "#FF6B6B",
        "source": "ONBOARDING",
        "addedAt": "2025-01-11T15:26:00Z"
      },
      {
        "categoryId": "cat_003",
        "categoryName": "Beauty & Cosmetics",
        "icon": "💄",
        "color": "#FF69B4",
        "source": "SETTINGS",
        "addedAt": "2025-01-12T10:00:00Z"
      }
    ],
    "topImplicit": [
      {
        "categoryId": "cat_002",
        "categoryName": "Electronics",
        "icon": "📱",
        "color": "#4ECDC4",
        "score": 87,
        "trend": "RISING"
      },
      {
        "categoryId": "cat_006",
        "categoryName": "Music & Dance",
        "icon": "🎵",
        "color": "#9B59B6",
        "score": 65,
        "trend": "STABLE"
      },
      {
        "categoryId": "cat_009",
        "categoryName": "Travel",
        "icon": "✈️",
        "color": "#1ABC9C",
        "score": 42,
        "trend": "FALLING"
      }
    ],
    "summary": {
      "explicitCount": 2,
      "implicitCount": 8,
      "topCategory": "Fashion",
      "feedPersonalization": "HIGH"
    }
  },
  "timestamp": "2025-01-11T18:00:00Z"
}
```

---

### Update My Explicit Interests

**PUT** `/api/v1/interests/me`

**Headers:**
```
Authorization: Bearer {access_token}
```

**Request:**
```json
{
  "categoryIds": ["cat_001", "cat_003", "cat_006", "cat_010", "cat_012"]
}
```

**Response:**
```json
{
  "success": true,
  "message": "Interests updated successfully",
  "data": {
    "explicit": [
      { "categoryId": "cat_001", "categoryName": "Fashion" },
      { "categoryId": "cat_003", "categoryName": "Beauty & Cosmetics" },
      { "categoryId": "cat_006", "categoryName": "Music & Dance" },
      { "categoryId": "cat_010", "categoryName": "Gaming" },
      { "categoryId": "cat_012", "categoryName": "Art & Design" }
    ],
    "changes": {
      "added": ["cat_006", "cat_010", "cat_012"],
      "removed": [],
      "unchanged": ["cat_001", "cat_003"]
    },
    "feedImpact": "Your feed will now show more Music, Gaming, and Art content"
  },
  "timestamp": "2025-01-11T18:05:00Z"
}
```

---

### Add Single Interest

**POST** `/api/v1/interests/me/{categoryId}`

**Headers:**
```
Authorization: Bearer {access_token}
```

**Response:**
```json
{
  "success": true,
  "message": "Interest added successfully",
  "data": {
    "categoryId": "cat_015",
    "categoryName": "Pets & Animals",
    "icon": "🐾",
    "addedAt": "2025-01-11T18:10:00Z",
    "totalExplicit": 6
  },
  "timestamp": "2025-01-11T18:10:00Z"
}
```

**Response (Max Reached):**
```json
{
  "success": false,
  "message": "Maximum interests reached",
  "error": {
    "code": "MAX_INTERESTS_REACHED",
    "current": 15,
    "maximum": 15,
    "suggestion": "Remove an interest before adding a new one"
  },
  "timestamp": "2025-01-11T18:10:00Z"
}
```

---

### Remove Single Interest

**DELETE** `/api/v1/interests/me/{categoryId}`

**Headers:**
```
Authorization: Bearer {access_token}
```

**Response:**
```json
{
  "success": true,
  "message": "Interest removed successfully",
  "data": {
    "categoryId": "cat_015",
    "categoryName": "Pets & Animals",
    "removedAt": "2025-01-11T18:15:00Z",
    "totalExplicit": 5,
    "note": "You may still see some Pets & Animals content based on your activity"
  },
  "timestamp": "2025-01-11T18:15:00Z"
}
```

**Response (Minimum Required):**
```json
{
  "success": false,
  "message": "Cannot remove interest",
  "error": {
    "code": "MIN_INTERESTS_REQUIRED",
    "current": 3,
    "minimum": 3,
    "suggestion": "Add another interest before removing this one"
  },
  "timestamp": "2025-01-11T18:15:00Z"
}
```

---

### Hide Content Category (Negative Signal)

**POST** `/api/v1/interests/me/{categoryId}/hide`

*User explicitly says "not interested" - strong negative signal*

**Headers:**
```
Authorization: Bearer {access_token}
```

**Response:**
```json
{
  "success": true,
  "message": "You'll see less of this content",
  "data": {
    "categoryId": "cat_018",
    "categoryName": "Business & Finance",
    "action": "HIDDEN",
    "feedImpact": "Business & Finance content will be significantly reduced in your feed",
    "canUndo": true,
    "undoExpiry": "2025-01-11T18:30:00Z"
  },
  "timestamp": "2025-01-11T18:20:00Z"
}
```

---

### Unhide Content Category

**DELETE** `/api/v1/interests/me/{categoryId}/hide`

**Response:**
```json
{
  "success": true,
  "message": "Category unhidden",
  "data": {
    "categoryId": "cat_018",
    "categoryName": "Business & Finance",
    "action": "UNHIDDEN",
    "feedImpact": "Business & Finance content will appear normally based on your activity"
  },
  "timestamp": "2025-01-11T18:25:00Z"
}
```

---

### Get Hidden Categories

**GET** `/api/v1/interests/me/hidden`

**Response:**
```json
{
  "success": true,
  "message": "Hidden categories retrieved",
  "data": {
    "hidden": [
      {
        "categoryId": "cat_018",
        "categoryName": "Business & Finance",
        "icon": "💼",
        "hiddenAt": "2025-01-11T18:20:00Z"
      },
      {
        "categoryId": "cat_014",
        "categoryName": "Automotive",
        "icon": "🚗",
        "hiddenAt": "2025-01-05T12:00:00Z"
      }
    ],
    "totalHidden": 2
  },
  "timestamp": "2025-01-11T18:30:00Z"
}
```

---

## INTERNAL: Silent Interest Tracking

*These are NOT public API endpoints. They are triggered internally when users interact with content.*

### How It Works

When user performs any action on content (post, product, shop, event), the system:

1. Gets the category tags from that content
2. Calculates score based on action type
3. Updates user's implicit interest scores silently
4. No API call from client needed

### Actions That Trigger Tracking

| User Action | System Tracks |
|-------------|---------------|
| Views post | `POST_VIEW` on post's categories |
| Likes post | `POST_LIKE` on post's categories |
| Comments on post | `POST_COMMENT` on post's categories |
| Shares post | `POST_SHARE` on post's categories |
| Saves post | `POST_SAVE` on post's categories |
| Views product | `PRODUCT_VIEW` on product's category |
| Adds to cart | `PRODUCT_CART` on product's category |
| Purchases | `PRODUCT_PURCHASE` on product's category |
| Views shop | `SHOP_VIEW` on shop's categories |
| Follows shop | `SHOP_FOLLOW` on shop's categories |
| Views event | `EVENT_VIEW` on event's categories |
| RSVPs to event | `EVENT_RSVP` on event's categories |
| Follows user | `USER_FOLLOW` on user's primary categories |
| Searches | `SEARCH` on matched categories |
| Scrolls past quickly | `SCROLL_PAST` (negative) |
| Clicks "Not interested" | `HIDE_CONTENT` (strong negative) |

### Content Must Have Category Tags

For tracking to work, all content must be tagged:

```json
// Post
{
  "id": "post_123",
  "content": "Check out my new outfit!",
  "categoryIds": ["cat_001"]  // Fashion
}

// Product
{
  "id": "prod_456", 
  "name": "Wireless Earbuds",
  "categoryId": "cat_002"  // Electronics
}

// Shop
{
  "id": "shop_789",
  "name": "StyleHub",
  "categoryIds": ["cat_001", "cat_003"]  // Fashion, Beauty
}

// Event
{
  "id": "event_012",
  "title": "Summer Music Festival",
  "categoryIds": ["cat_006", "cat_019"]  // Music, Entertainment
}
```

---

## FEED: Using Interests for Recommendations

### Get Personalized Feed

**GET** `/api/v1/feed?page=1&size=20`

*Feed algorithm uses interest scores to rank content*

**Response includes personalization info:**
```json
{
  "success": true,
  "data": {
    "posts": [
      {
        "id": "post_123",
        "content": "...",
        "author": { "...": "..." },
        "categoryIds": ["cat_001"],
        "relevanceScore": 0.95,
        "relevanceReason": "EXPLICIT_INTEREST"
      },
      {
        "id": "post_456",
        "content": "...",
        "author": { "...": "..." },
        "categoryIds": ["cat_002"],
        "relevanceScore": 0.87,
        "relevanceReason": "HIGH_IMPLICIT_SCORE"
      },
      {
        "id": "post_789",
        "content": "...",
        "author": { "...": "..." },
        "categoryIds": ["cat_015"],
        "relevanceScore": 0.45,
        "relevanceReason": "TRENDING"
      }
    ],
    "personalization": {
      "status": "ACTIVE",
      "basedOn": {
        "explicitInterests": 5,
        "implicitInterests": 8,
        "followedAccounts": 23
      }
    },
    "pagination": { "...": "..." }
  }
}
```

---

## SUMMARY: Interest System Endpoints

### Admin Endpoints
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/v1/admin/interests/categories` | Create category |
| GET | `/api/v1/admin/interests/categories` | List all (with stats) |
| PUT | `/api/v1/admin/interests/categories/{id}` | Update category |
| DELETE | `/api/v1/admin/interests/categories/{id}` | Deactivate category |
| PUT | `/api/v1/admin/interests/categories/reorder` | Reorder categories |
| GET | `/api/v1/admin/interests/categories/{id}/analytics` | Get analytics |

### Public Endpoints
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/interests/categories` | Get active categories |

### User Endpoints (Requires Auth)
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/interests/me` | Get my interests |
| PUT | `/api/v1/interests/me` | Update all explicit |
| POST | `/api/v1/interests/me/{id}` | Add single interest |
| DELETE | `/api/v1/interests/me/{id}` | Remove single interest |
| POST | `/api/v1/interests/me/{id}/hide` | Hide category |
| DELETE | `/api/v1/interests/me/{id}/hide` | Unhide category |
| GET | `/api/v1/interests/me/hidden` | Get hidden list |

### Internal (No Public API)
- Silent tracking triggered by user actions
- Score calculation and decay
- Feed personalization

---

## New Database Fields Needed

### AccountEntity
```java
// Existing fields to keep
private UUID id;
private String userName;              // Public @handle (changeable)
private String phoneNumber;
private String email;
private String password;
private String firstName;
private String lastName;
private String middleName;
private String bio;
private String location;
private Boolean isVerified;
private Boolean isEmailVerified;
private Boolean isPhoneVerified;
private boolean twoFactorEnabled;
private String twoFactorSecret;
private boolean locked;
private String lockedReason;
private LocalDateTime createdAt;
private LocalDateTime editedAt;
private Set<Roles> roles;
private List<String> profilePictureUrls;
private boolean isBucketCreated;

// NEW fields to add
private String systemUsername;        // Internal identifier (never changes) - "usr_550e8400e29b41d4"
private LocalDate birthDate;          // For age gating
private String displayName;           // Full display name "Alex Johnson"
private String authProvider;          // PHONE, EMAIL, GOOGLE, APPLE (primary signup method)
private String googleId;              // Google OAuth ID
private String appleId;               // Apple OAuth ID
private String onboardingStep;        // SIGNUP, NAME_BIRTHDATE, PROFILE_SETUP, INTERESTS, COMPLETE
private Boolean onboardingComplete;   // Quick check flag
private Boolean hasPassword;          // Whether user set a password
private LocalDateTime phoneClaimedAt; // When phone was first set (for claim expiry)
private LocalDateTime phoneVerifiedAt;// When phone was verified
private LocalDateTime emailClaimedAt; // When email was first set
private LocalDateTime emailVerifiedAt;// When email was verified
```

### New Entities

**InterestCategory** (Admin managed)
```
- id (UUID)
- name (String)
- icon (String) - emoji
- iconUrl (String) - image URL
- color (String) - hex color
- keywords (List<String>) - for auto-tagging
- displayOrder (Integer)
- isActive (Boolean)
- createdAt (LocalDateTime)
- updatedAt (LocalDateTime)
```

**UserInterest** (User's interests)
```
- id (UUID)
- userId (UUID)
- categoryId (UUID)
- source (Enum: ONBOARDING, SETTINGS, IMPLICIT)
- score (Integer) - 0 to 1000
- isExplicit (Boolean)
- isHidden (Boolean)
- lastInteractionAt (LocalDateTime)
- createdAt (LocalDateTime)
- updatedAt (LocalDateTime)
```

**InterestEvent** (Tracking log - optional, for analytics)
```
- id (UUID)
- userId (UUID)
- categoryId (UUID)
- actionType (Enum)
- weight (Integer)
- sourceType (Enum: POST, PRODUCT, SHOP, EVENT, USER, SEARCH)
- sourceId (UUID)
- createdAt (LocalDateTime)
```

---

## JWT Token Structure

**Access Token Payload:**
```json
{
  "sub": "usr_550e8400e29b41d4",
  "tokenType": "ACCESS",
  "iat": 1736605200,
  "exp": 1736608800
}
```

**Refresh Token Payload:**
```json
{
  "sub": "usr_550e8400e29b41d4",
  "tokenType": "REFRESH",
  "iat": 1736605200,
  "exp": 1768141200
}
```

*Note: `sub` (subject) uses `systemUsername`, NOT `userName`. This allows username changes without token invalidation.*

---

## Username Change Flow (No Logout Required)

**PUT** `/api/v1/profile/update-basic-info`

**Request:**
```json
{
  "userName": "newusername"
}
```

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Username updated successfully",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "systemUsername": "usr_550e8400e29b41d4",
    "userName": "newusername",
    "previousUserName": "oldusername",
    "note": "Your profile URL is now: app.com/@newusername"
  }
}
```

*JWT token remains valid because it uses `systemUsername` which hasn't changed.*

---

## System Username Generation

Generated automatically at account creation:

```java
public String generateSystemUsername(UUID userId) {
    // Take first 16 chars of UUID (without hyphens)
    String shortId = userId.toString().replace("-", "").substring(0, 16);
    return "usr_" + shortId;
}

// Example:
// UUID: 550e8400-e29b-41d4-a716-446655440000
// systemUsername: usr_550e8400e29b41d4
```

**Rules:**
- Prefix: `usr_`
- Length: 20 characters total
- Characters: lowercase alphanumeric only
- Unique: derived from UUID
- Never displayed to users
- Never changeable

---

## DEVICE TRUST & LOGIN SECURITY

### Overview

The system tracks user devices and applies a sliding trust window:
- **New device** → OTP required before password login
- **Inactive device (30+ days)** → OTP required (re-verification)
- **Trusted active device** → Password login allowed
- **Suspicious activity** → OTP required

### Trust Sliding Window

```
Device Activity Timeline:
─────────────────────────────────────────────────────────────────►
                                                              NOW
     │                    │                    │
     ▼                    ▼                    ▼
  Day 0               Day 25               Day 35
  (Login)            (Last use)           (Login attempt)
     │                    │                    │
     │◄────── TRUSTED ────►│                   │
     │                    │◄─── 30 DAY GAP ───►│
     │                                         │
     │                              Device now UNTRUSTED
     │                              OTP required to re-trust
```

### Login Decision Matrix

| Device Status | Last Activity | Password Set? | Action |
|---------------|---------------|---------------|--------|
| New (unknown) | Never | Yes | OTP first → then password → trust device |
| New (unknown) | Never | No | OTP only → trust device |
| Known trusted | < 30 days | Yes | Password only ✅ |
| Known trusted | < 30 days | No | Auto-login or OTP |
| Known trusted | > 30 days | Yes | OTP first → then password → re-trust |
| Known trusted | > 30 days | No | OTP → re-trust |
| Known untrusted | Any | Any | OTP required |
| Any (suspicious) | Any | Any | OTP required + security alert |

### Suspicious Activity Triggers

- 3+ failed password attempts
- Login from new country/region
- Multiple devices logging in simultaneously
- Unusual login time (if pattern established)
- IP address on blocklist

---

## LOGIN FLOWS (Updated with Device Trust)

### Password Login - Full Flow

**POST** `/api/v1/auth/login/password`

**Request:**
```json
{
  "identifier": "alexvibes",
  "password": "MySecurePass123!",
  "deviceInfo": {
    "deviceId": "abc123-device-fingerprint",
    "deviceName": "iPhone 15 Pro",
    "deviceType": "MOBILE_IOS",
    "appVersion": "1.2.0"
  }
}
```

**Response (Success - Trusted Device):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "requiresOtp": false,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/...",
      "onboardingComplete": true
    },
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "trusted": true,
      "trustExpiresAt": "2025-02-10T16:00:00"
    }
  }
}
```

**Response (New/Untrusted Device - OTP Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "New device detected. Please verify with OTP.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "requiresOtp": true,
    "otpReason": "NEW_DEVICE",
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "otpSentTo": "+255*****678",
    "otpMethod": "SMS",
    "expiresAt": "2025-01-11T16:10:00",
    "device": {
      "deviceId": "xyz789-new-device",
      "deviceName": "Chrome on Windows",
      "isNew": true
    },
    "securityNote": "We detected a login from a new device. For your security, please verify with OTP."
  }
}
```

**Response (Inactive Device - OTP Required):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device inactive for 30+ days. Please verify with OTP.",
  "action_time": "2025-01-11T16:00:00",
  "data": {
    "requiresOtp": true,
    "otpReason": "INACTIVE_DEVICE",
    "tempToken": "eyJhbGciOiJIUzI1NiIs...",
    "otpSentTo": "a***@example.com",
    "otpMethod": "EMAIL",
    "expiresAt": "2025-01-11T16:10:00",
    "device": {
      "deviceId": "abc123-device-fingerprint",
      "deviceName": "iPhone 15 Pro",
      "lastActiveAt": "2024-12-01T10:00:00",
      "inactiveDays": 41
    },
    "securityNote": "This device hasn't been used in 41 days. Please verify it's you."
  }
}
```

---

### Verify Device OTP (Step 2 for untrusted devices)

**POST** `/api/v1/auth/login/verify-device`

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456",
  "trustDevice": true,
  "deviceInfo": {
    "deviceId": "xyz789-new-device",
    "deviceName": "Chrome on Windows",
    "deviceType": "WEB_BROWSER"
  }
}
```

**Response (Success):**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device verified and trusted",
  "action_time": "2025-01-11T16:02:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "systemUsername": "usr_550e8400e29b41d4",
      "userName": "alexvibes",
      "displayName": "Alex Johnson",
      "profilePictureUrl": "https://storage.example.com/...",
      "onboardingComplete": true
    },
    "device": {
      "deviceId": "xyz789-new-device",
      "deviceName": "Chrome on Windows",
      "trusted": true,
      "trustExpiresAt": "2025-02-10T16:02:00"
    }
  }
}
```

---

### Login Without Trusting Device (One-time access)

**Request:**
```json
{
  "tempToken": "eyJhbGciOiJIUzI1NiIs...",
  "otpCode": "123456",
  "trustDevice": false
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login successful (device not saved)",
  "action_time": "2025-01-11T16:02:00",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIs...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": { ... },
    "device": {
      "trusted": false,
      "note": "This device will require OTP on next login"
    }
  }
}
```

---

## DEVICE MANAGEMENT

### Get My Devices

**GET** `/api/v1/auth/devices`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Devices retrieved successfully",
  "action_time": "2025-01-11T18:00:00",
  "data": {
    "devices": [
      {
        "id": "device-uuid-1",
        "deviceId": "abc123-device-fingerprint",
        "deviceName": "iPhone 15 Pro",
        "deviceType": "MOBILE_IOS",
        "isTrusted": true,
        "isCurrentDevice": true,
        "lastActiveAt": "2025-01-11T18:00:00",
        "lastIpAddress": "196.41.xxx.xxx",
        "lastLocation": "Dar es Salaam, Tanzania",
        "createdAt": "2025-01-01T10:00:00"
      },
      {
        "id": "device-uuid-2",
        "deviceId": "xyz789-device-fingerprint",
        "deviceName": "Chrome on MacBook",
        "deviceType": "WEB_BROWSER",
        "isTrusted": true,
        "isCurrentDevice": false,
        "lastActiveAt": "2025-01-10T14:30:00",
        "lastIpAddress": "196.41.xxx.xxx",
        "lastLocation": "Dar es Salaam, Tanzania",
        "createdAt": "2024-12-15T08:00:00"
      },
      {
        "id": "device-uuid-3",
        "deviceId": "old-device-fingerprint",
        "deviceName": "Samsung Galaxy S21",
        "deviceType": "MOBILE_ANDROID",
        "isTrusted": false,
        "isCurrentDevice": false,
        "lastActiveAt": "2024-11-20T09:00:00",
        "lastIpAddress": "41.59.xxx.xxx",
        "lastLocation": "Arusha, Tanzania",
        "createdAt": "2024-06-01T12:00:00",
        "untrustedReason": "Inactive for 52 days"
      }
    ],
    "totalDevices": 3,
    "trustedDevices": 2
  }
}
```

---

### Remove/Logout Device

**DELETE** `/api/v1/auth/devices/{deviceId}`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device removed successfully",
  "action_time": "2025-01-11T18:05:00",
  "data": {
    "removedDeviceId": "device-uuid-3",
    "removedDeviceName": "Samsung Galaxy S21",
    "note": "This device has been logged out and will require OTP to login again"
  }
}
```

---

### Logout All Other Devices

**POST** `/api/v1/auth/devices/logout-all`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Request:**
```json
{
  "password": "MySecurePass123!",
  "keepCurrentDevice": true
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "All other devices logged out",
  "action_time": "2025-01-11T18:10:00",
  "data": {
    "devicesLoggedOut": 2,
    "currentDeviceKept": true,
    "note": "All other devices will require OTP to login again"
  }
}
```

---

### Rename Device

**PUT** `/api/v1/auth/devices/{deviceId}`

**Request:**
```json
{
  "deviceName": "My Work Laptop"
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Device renamed successfully",
  "action_time": "2025-01-11T18:15:00",
  "data": {
    "deviceId": "device-uuid-2",
    "deviceName": "My Work Laptop",
    "previousName": "Chrome on MacBook"
  }
}
```

---

## LOGIN EVENTS/HISTORY

### Get Login History

**GET** `/api/v1/auth/login-history?page=1&size=20`

**Headers:**
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Login history retrieved",
  "action_time": "2025-01-11T18:20:00",
  "data": {
    "events": [
      {
        "id": "event-uuid-1",
        "loginMethod": "PASSWORD",
        "deviceName": "iPhone 15 Pro",
        "deviceType": "MOBILE_IOS",
        "ipAddress": "196.41.xxx.xxx",
        "location": "Dar es Salaam, Tanzania",
        "status": "SUCCESS",
        "requiresOtp": false,
        "createdAt": "2025-01-11T16:00:00"
      },
      {
        "id": "event-uuid-2",
        "loginMethod": "OTP",
        "deviceName": "Chrome on Windows",
        "deviceType": "WEB_BROWSER",
        "ipAddress": "196.41.xxx.xxx",
        "location": "Dar es Salaam, Tanzania",
        "status": "SUCCESS",
        "requiresOtp": true,
        "otpReason": "NEW_DEVICE",
        "createdAt": "2025-01-11T14:30:00"
      },
      {
        "id": "event-uuid-3",
        "loginMethod": "PASSWORD",
        "deviceName": "Unknown Device",
        "deviceType": "WEB_BROWSER",
        "ipAddress": "102.89.xxx.xxx",
        "location": "Lagos, Nigeria",
        "status": "FAILED_PASSWORD",
        "requiresOtp": false,
        "createdAt": "2025-01-10T23:45:00",
        "securityAlert": true
      }
    ],
    "pagination": {
      "page": 1,
      "size": 20,
      "totalElements": 45,
      "totalPages": 3
    },
    "securitySummary": {
      "totalLogins30Days": 28,
      "failedAttempts30Days": 2,
      "uniqueDevices30Days": 3,
      "uniqueLocations30Days": 1
    }
  }
}
```

---

## SECURITY ALERTS

### Get Security Alerts

**GET** `/api/v1/auth/security-alerts`

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Security alerts retrieved",
  "action_time": "2025-01-11T18:25:00",
  "data": {
    "alerts": [
      {
        "id": "alert-uuid-1",
        "type": "SUSPICIOUS_LOGIN",
        "severity": "HIGH",
        "title": "Login attempt from new location",
        "description": "Someone tried to login from Lagos, Nigeria",
        "deviceName": "Unknown Device",
        "ipAddress": "102.89.xxx.xxx",
        "location": "Lagos, Nigeria",
        "status": "UNREAD",
        "actionTaken": "BLOCKED",
        "createdAt": "2025-01-10T23:45:00",
        "actions": [
          { "action": "DISMISS", "label": "This was me" },
          { "action": "SECURE_ACCOUNT", "label": "Secure my account" }
        ]
      },
      {
        "id": "alert-uuid-2",
        "type": "NEW_DEVICE_LOGIN",
        "severity": "MEDIUM",
        "title": "New device added",
        "description": "Chrome on Windows was added to your account",
        "deviceName": "Chrome on Windows",
        "location": "Dar es Salaam, Tanzania",
        "status": "READ",
        "actionTaken": null,
        "createdAt": "2025-01-11T14:30:00",
        "actions": [
          { "action": "DISMISS", "label": "This was me" },
          { "action": "REMOVE_DEVICE", "label": "Remove this device" }
        ]
      }
    ],
    "unreadCount": 1,
    "totalAlerts": 2
  }
}
```

---

### Dismiss Security Alert

**POST** `/api/v1/auth/security-alerts/{alertId}/dismiss`

**Request:**
```json
{
  "action": "DISMISS",
  "feedback": "This was me logging in from a friend's device"
}
```

**Response:**
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Alert dismissed",
  "action_time": "2025-01-11T18:30:00",
  "data": {
    "alertId": "alert-uuid-1",
    "status": "DISMISSED",
    "unreadCount": 0
  }
}
```

---

## New Database Entities for Device Trust

### UserDevice
```
- id (UUID)
- userId (UUID) FK → AccountEntity
- deviceId (String) - fingerprint from client (unique per user)
- deviceName (String) - "iPhone 15 Pro", "Chrome on Windows"
- deviceType (Enum) - MOBILE_IOS, MOBILE_ANDROID, WEB_BROWSER, DESKTOP_APP
- userAgent (String)
- lastIpAddress (String)
- lastLocation (String)
- isTrusted (Boolean)
- trustExpiresAt (LocalDateTime) - 30 days sliding window
- lastActiveAt (LocalDateTime)
- createdAt (LocalDateTime)
- updatedAt (LocalDateTime)

Indexes:
- (userId, deviceId) UNIQUE
- (userId, isTrusted)
- (lastActiveAt)
```

### LoginEvent
```
- id (UUID)
- userId (UUID) FK → AccountEntity
- deviceId (UUID) FK → UserDevice (nullable for failed attempts)
- loginMethod (Enum) - PASSWORD, OTP_SMS, OTP_EMAIL, GOOGLE, APPLE
- ipAddress (String)
- location (String)
- status (Enum) - SUCCESS, FAILED_PASSWORD, FAILED_OTP, BLOCKED, REQUIRES_OTP
- requiresOtp (Boolean)
- otpReason (Enum) - NEW_DEVICE, INACTIVE_DEVICE, SUSPICIOUS, MANUAL
- createdAt (LocalDateTime)

Indexes:
- (userId, createdAt)
- (userId, status)
- (ipAddress, createdAt) - for rate limiting
```

### SecurityAlert
```
- id (UUID)
- userId (UUID) FK → AccountEntity
- loginEventId (UUID) FK → LoginEvent (nullable)
- type (Enum) - SUSPICIOUS_LOGIN, NEW_DEVICE_LOGIN, FAILED_ATTEMPTS, PASSWORD_CHANGED, etc.
- severity (Enum) - LOW, MEDIUM, HIGH, CRITICAL
- title (String)
- description (String)
- deviceName (String)
- ipAddress (String)
- location (String)
- status (Enum) - UNREAD, READ, DISMISSED, ACTIONED
- actionTaken (String)
- createdAt (LocalDateTime)
- readAt (LocalDateTime)
- dismissedAt (LocalDateTime)

Indexes:
- (userId, status)
- (userId, createdAt)
```

---

## Device Trust Configuration

```yaml
# application.yml
device:
  trust:
    enabled: true
    window-days: 30                    # Trust window duration
    max-devices-per-user: 10           # Max trusted devices
    auto-cleanup-days: 90              # Remove inactive devices after
    
login:
  security:
    max-failed-attempts: 5             # Before temporary lock
    lock-duration-minutes: 30          # Temporary lock duration
    suspicious-countries: [XX, YY]     # Countries requiring extra verification
    alert-on-new-country: true         # Send alert on new country login
```