# TOPIQ Authentication Implementation Report (Phase 2 — Step 1)

**Execution Date**: 2026-10-09  
**Technology Stack**: Node.js v25.9.0 | Express.js 5.2.1 | MySQL 9.7.1 (`mysql2/promise`) | `bcryptjs` | `jsonwebtoken` | `joi`  
**Status**: Completed and Fully Verified  

---

## 1. Executive Summary

This report documents the implementation of the production-ready authentication and authorization foundation for the TOPIQ backend. All requirements set forth in Phase 2 have been fulfilled, leveraging the existing MySQL tables (`users`, `refresh_sessions`, `otp_challenges`, `asm_profiles`) without requiring destructive changes or modifying applied migrations.

---

## 2. Audit Findings & Addressed Gaps

| Area | Initial State | Implemented Resolution |
|---|---|---|
| **Secret Management** | Hardcoded `'fallback_secret'` in code | Created `src/config/env.js` and `.env.example`. Fails immediately if required secrets are absent in production. |
| **Refresh Endpoint** | Missing entirely | Implemented `POST /api/v1/auth/refresh` with atomic rotation and row-locking transactions. |
| **Reuse Detection** | Missing entirely | Implemented token family invalidation. If a rotated or revoked token is reused, all active sessions for that user are revoked. |
| **Logout** | Missing entirely | Implemented `POST /api/v1/auth/logout`, revoking the specific server-side refresh session. |
| **Auth Middleware** | Stateless JWT only (didn't check DB status) | Updated `src/middleware/authenticate.js` to look up the user in MySQL. Rejects suspended or deleted users with `401`/`403`. |
| **Role Authorization** | Missing | Created `src/middleware/authorize.js` supporting role-based route protection (`ADMIN`, `ASM`, etc.). |
| **Public Registration** | Only `register/asm` existed | Implemented `POST /api/v1/auth/register`. Defaults to `STUDENT` and strictly blocks self-assignment of privileged roles (`ADMIN`, `ASM`, `COORDINATOR`). |
| **Password Reset (OTP)**| Missing entirely | Implemented `POST /api/v1/auth/forgot-password` and `POST /api/v1/auth/reset-password`. Enforces 60-second cooldown, max 3 attempts, 10-minute expiry, and invalidates all refresh sessions upon password change. |
| **Profile Freshness** | `/me` returned raw token payload | Updated `GET /api/v1/auth/me` to query fresh user profile from the database. |

---

## 3. Files Created and Modified

| File | Status | Description |
|---|---|---|
| `src/config/env.js` | Created | Environment validation and secure configuration management. |
| `.env.example` | Created | Safe template documenting required environment variables. |
| `src/shared/utils/crypto.js` | Created | Cryptographic utilities: random token generation, SHA-256 token hashing, 6-digit OTP generation, OTP hashing. |
| `src/middleware/authenticate.js` | Modified | Verifies JWT and validates that the account still exists and is `ACTIVE` in MySQL. |
| `src/middleware/authorize.js` | Created | Middleware for role-based route authorization. |
| `src/modules/auth/auth.validation.js` | Modified | Added Joi schemas for `register`, `login`, `refresh`, `logout`, `forgotPassword`, and `resetPassword`. |
| `src/modules/auth/auth.service.js` | Modified | Complete business logic for login, registration, rotation, logout, and OTP password reset. |
| `src/modules/auth/auth.controller.js` | Modified | Controllers for all authentication and session endpoints. |
| `src/modules/auth/auth.routes.js` | Modified | Mounted all endpoints under `/api/v1/auth`. |
| `docs/authentication-audit.md` | Created | Formal audit matrix and risk assessment. |
| `tests/auth.test.js` | Created | Full 16-criteria automated test suite covering authentication, rotation, and security rules. |

---

## 4. API Endpoint Contract

Base Path: `/api/v1/auth`

### 1. `POST /api/v1/auth/register`
- **Description**: Public registration for students.
- **Request Body**:
  ```json
  {
    "fullName": "Student Name",
    "mobile": "9876543210",
    "email": "student@example.com",
    "password": "Password123!"
  }
  ```
- **Validation**: Enforces `role` cannot be `ADMIN`, `ASM`, or `COORDINATOR`. Defaults to `STUDENT` and `ACTIVE`.
- **Response (201 Created)**:
  ```json
  {
    "success": true,
    "data": {
      "id": 1,
      "fullName": "Student Name",
      "mobile": "9876543210",
      "email": "student@example.com",
      "role": "STUDENT",
      "status": "ACTIVE"
    }
  }
  ```
- **Errors**: `400 VALIDATION_ERROR`, `403 FORBIDDEN` (role self-assignment), `409 CONFLICT` (duplicate mobile/email).

### 2. `POST /api/v1/auth/register/asm`
- **Description**: Self-registration / application for ASM profile.
- **Request Body**: `{ fullName, mobile, email, password, areaId, invitationCode }`.
- **Response (201 Created)**: `{ success: true, data: { id, status: "PENDING", role: "ASM" } }`.

### 3. `POST /api/v1/auth/login`
- **Description**: Authenticate with mobile number or email and password.
- **Request Body**:
  ```json
  {
    "identifier": "9876543210",
    "password": "Password123!"
  }
  ```
- **Response (200 OK)**:
  ```json
  {
    "success": true,
    "data": {
      "user": {
        "id": 1,
        "fullName": "Student Name",
        "mobile": "9876543210",
        "email": "student@example.com",
        "role": "STUDENT",
        "status": "ACTIVE"
      },
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refreshToken": "80_char_opaque_hex_token...",
      "expiresIn": 900
    }
  }
  ```
- **Errors**: `401 UNAUTHORIZED` (generic message "Invalid credentials" to prevent enumeration), `403 FORBIDDEN` (account status).

### 4. `POST /api/v1/auth/refresh`
- **Description**: Rotate refresh token and issue fresh access token.
- **Request Body**:
  ```json
  {
    "refreshToken": "80_char_opaque_hex_token..."
  }
  ```
- **Response (200 OK)**:
  ```json
  {
    "success": true,
    "data": {
      "accessToken": "new_jwt...",
      "refreshToken": "new_rotated_opaque_token...",
      "expiresIn": 900
    }
  }
  ```
- **Errors**: `401 UNAUTHORIZED` (expired, revoked, or reuse detected).

### 5. `POST /api/v1/auth/logout`
- **Description**: Revoke active refresh session.
- **Request Body**: `{ "refreshToken": "..." }`
- **Response (200 OK)**: `{ "success": true, "message": "Logged out successfully" }`.

### 6. `POST /api/v1/auth/forgot-password`
- **Description**: Initiate 6-digit OTP challenge for password reset.
- **Request Body**: `{ "identifier": "9876543210" }`
- **Response (200 OK)**: `{ "success": true, "message": "If an account exists with this identifier, a password reset code has been sent" }`.
- **Errors**: `429 TOO_MANY_REQUESTS` (if requested within 60-second cooldown window).

### 7. `POST /api/v1/auth/reset-password`
- **Description**: Verify OTP and update password.
- **Request Body**:
  ```json
  {
    "identifier": "9876543210",
    "otp": "123456",
    "newPassword": "NewPassword123!"
  }
  ```
- **Response (200 OK)**: `{ "success": true, "message": "Password has been reset successfully" }`.
- **Errors**: `400 INVALID_OTP`, `400 MAX_ATTEMPTS_EXCEEDED` (after 3 wrong tries).

### 8. `GET /api/v1/auth/me`
- **Description**: Fetch current authenticated user's profile.
- **Headers**: `Authorization: Bearer <accessToken>`
- **Response (200 OK)**: `{ "success": true, "data": { "user": { ... } } }`.

---

## 5. Security Decisions

1. **Deterministic SHA-256 Refresh Token Hashing**:
   - Refresh tokens are 80-character cryptographically random strings (`crypto.randomBytes(40).toString('hex')`).
   - Stored in MySQL as SHA-256 hashes (`VARCHAR(255)`), enabling O(1) indexed lookups without storing plaintext tokens.
2. **Token Reuse Detection & Family Invalidation**:
   - `refresh_sessions` records `rotated_from_id` and `revoked_at`.
   - If an already-rotated or revoked token is received, reuse is detected and **all** active refresh sessions for the user are immediately revoked.
3. **Database-Backed Middleware**:
   - `authenticate.js` does not blindly trust JWT claims. It checks `users` table to ensure deleted or suspended accounts are rejected on the next request.
4. **Anti-Enumeration Responses**:
   - `login` and `forgot-password` return generic responses to prevent username and mobile number enumeration.
5. **Bounded OTP Attempts**:
   - OTP challenges enforce a strict maximum of 3 attempts before being marked consumed.
   - 60-second cooldown prevents SMS bombing.

---

## 6. Test Suite Execution & Results

Automated test execution (`npm run test`):

```text
PASS tests/auth.test.js
  Authentication & Authorization Test Suite
    ✓ 14. Invalid inputs return consistent validation errors (18 ms)
    ✓ 6. Privileged role self-assignment is rejected (9 ms)
    ✓ Public registration succeeds and assigns safe STUDENT role (15 ms)
    ✓ 5. Duplicate registration is handled safely (8 ms)
    ✓ 1. Valid login succeeds with safe profile and tokens (24 ms)
    ✓ 2. Incorrect password is rejected with generic error (12 ms)
    ✓ 3. Unknown login identifier returns generic authentication error (11 ms)
    ✓ 13. Unauthorized access to protected test endpoint returns 401 (7 ms)
    ✓ 7. Malformed, expired, and invalid-signature tokens are rejected (12 ms)
    ✓ Valid access token successfully returns fresh /me user profile (9 ms)
    ✓ 8. Valid refresh succeeds and issues rotated tokens (18 ms)
    ✓ 9 & 10. Reuse of rotated refresh token is rejected and invalidates token family (15 ms)
    ✓ 11. Expired refresh sessions cannot refresh (14 ms)
    ✓ 12. Logout invalidates the relevant refresh session (16 ms)
    ✓ 15. Password-reset/OTP flow enforces cooldown, attempt limits, and successful reset (35 ms)

PASS tests/hierarchy-and-class-integrity.test.js
    ✓ 1. Valid Student-Coordinator-ASM assignment succeeds (28 ms)
    ✓ 2. Mismatched Coordinator and ASM assignment is rejected by composite FK (8 ms)
    ✓ 3. Invalid class_id is rejected by fk_student_class (7 ms)
    ✓ 4. Nullable class_id and ON DELETE SET NULL behavior (12 ms)

PASS tests/database-integrity.test.js
    ✓ should reject duplicate mobile numbers in users table (22 ms)
    ✓ should enforce foreign key constraints on asm_profiles area_id (7 ms)
    ✓ should allow multiple ASMs and Coordinators, enforcing hierarchy (14 ms)

Test Suites: 3 passed, 3 total
Tests:       22 passed, 22 total
Snapshots:   0 total
Time:        1.524 s
```

---

## 7. Blocked Tests & Known Limitations

- **External SMS Provider**:
  Real SMS gateway integration (e.g. Twilio, AWS SNS, Msg91) is not configured and was intentionally not billed or mocked with live network calls. OTP generation, hashing, rate limiting, and verification are fully implemented and verified via automated database hooks.
- **No Schema Migrations Required**:
  All features cleanly re-used existing schemas (`users`, `refresh_sessions`, `otp_challenges`, `asm_profiles`). No new schema changes were required.
