# Specification: 023-mobile-jwt-auth

## Status

| Field | Value |
|-------|-------|
| **Created** | 2025-12-31 |
| **Current Phase** | ✅ IMPLEMENTATION COMPLETE |
| **Last Updated** | 2026-01-01 |

## Documents

| Document | Status | Notes |
|----------|--------|-------|
| product-requirements.md | skipped | Requirements in initial spec |
| solution-design.md | completed | Codex review completed, all blockers resolved |
| implementation-plan.md | ✅ implemented | All 5 phases complete, 108 tests passing |

**Status values**: `pending` | `in_progress` | `completed` | `skipped`

## Decisions Log

| Date | Decision | Rationale |
|------|----------|-----------|
| 2025-12-31 | PRD skipped | Requirements fully detailed in initial spec with API contracts |
| 2025-12-31 | JWT Algorithm: HS256 | Simpler setup, sufficient for single-server architecture |
| 2025-12-31 | Rotate refresh tokens | Enhanced security - stolen tokens invalid after one use |
| 2025-12-31 | Allow multi-device sessions | User convenience - multiple devices can be logged in simultaneously |
| 2025-12-31 | Route path: `/api/mobile/auth/*` | Matches existing `/api/mobile/*` pattern (resolved during SDD review) |
| 2025-12-31 | Rate limiting: Fail open | User prioritized availability when Redis unavailable |
| 2025-12-31 | Response schema: Match existing | Use storeName/storeCity format from `/api/mobile/verify` |
| 2025-12-31 | Store access middleware | Added StoreAccessMiddleware for store-level authorization (security critical) |
| 2026-01-01 | Implementation Plan complete | 5 phases with TDD structure, 100+ tasks, ready for implementation |
| 2026-01-01 | PLAN Codex Review | 5 blockers resolved, 6 important items addressed |
| 2026-01-01 | Backward compat: app.jwtUser | Set BOTH app.jwtUser AND app.authContext during transition |
| 2026-01-01 | Error codes standardized | Documented 9 granular error codes in PLAN |
| 2026-01-01 | TypeNum validation | Added regex validation `/^[a-z]{2}\d+$/` to prevent injection |
| 2026-01-01 | Phase 1-4 complete | Core components, middleware, endpoints, route integration |
| 2026-01-01 | Phase 5 validation | All 108 tests pass, all 9 SDD scenarios verified |
| 2026-01-01 | **IMPLEMENTATION COMPLETE** | Ready for deployment |

## Context

**Mobile JWT Authentication Migration**

The mobile app is migrating from static API key authentication to JWT-based authentication. This is a **critical security upgrade** that requires:

### Priority Matrix

| Priority | Endpoint | Status |
|----------|----------|--------|
| 🔴 Critical | `POST /api/mobile/auth/login` | NEW |
| 🔴 Critical | `POST /api/mobile/auth/refresh` | NEW |
| 🟡 Important | `POST /api/mobile/auth/logout` | NEW |
| 🟢 Existing | All `/api/mobile/*` | MODIFY (accept Bearer token + store validation) |

### Key Requirements
- 15-minute access token TTL
- 30-day refresh token TTL
- Device fingerprinting for audit trail
- Rate limiting (5 failed attempts per 15 minutes)
- Backward compatibility with API keys during migration
- Multi-store user support in login response

### Open Questions (RESOLVED)
1. ✅ JWT Algorithm: **HS256** (shared secret) - simpler setup, sufficient for single-server
2. ✅ Refresh Token Rotation: **Yes** - enhanced security with atomic transactions
3. ✅ Multi-Device: **Yes** - allow multiple simultaneous sessions
4. ✅ Token Revocation: Via refresh token rotation (immediate revocation built-in)
5. ⏳ Migration Timeline: TBD (APIKey deprecated but supported during transition)

---

## SDD Review Summary (2025-12-31)

### Codex Review Findings

**Blockers Resolved:**
1. ✅ **Store-Level Authorization** - Added `StoreAccessMiddleware` to validate `userStoreAssignments` on store-scoped routes (critical security gap)
2. ✅ **Route Prefix Mismatch** - Standardized on `/api/mobile/auth/*` pattern
3. ✅ **Rate Limiting Fail Behavior** - Documented fail-open with user decision
4. ✅ **Refresh Token Rotation Atomicity** - Added `SELECT FOR UPDATE` transaction pattern

**Changes Made to SDD:**
- Added `StoreAccessMiddleware` component with store authorization logic
- Added `AuthContext` model for standardized auth context contract
- Updated refresh token rotation to use atomic DB transaction
- Standardized error codes (401 for auth, 403 for authorization)
- Updated login response schema to match existing `/api/mobile/verify` format
- Added test scenarios for store access denial and concurrent refresh
- Added ADR-6 (Store Access Middleware) and ADR-7 (Rate Limit Fail-Open)

**Readiness Checklist:**
- [x] All blockers resolved
- [x] Design covers all requirements
- [x] Architecture is sound and justified
- [x] Interfaces clearly defined
- [x] Security and error handling addressed
- [x] README updated with review notes

**Status: ✅ Ready for Implementation**

---

## Implementation Plan Summary (2026-01-01)

### Codex Review Findings

**Blockers Resolved:**
1. ✅ **app.jwtUser vs app.authContext** - Set BOTH for backward compatibility
2. ✅ **Missing account_not_activated** - Added test and error handling
3. ✅ **Missing no_store_access** - Added 403 error for zero-assignment users
4. ✅ **Store access denial logging** - Added logging for security audit
5. ✅ **TypeNum injection risk** - Added regex validation before DB query

**Important Items Addressed:**
1. ✅ Error code consistency documented in PLAN
2. ✅ Additional context sources added to priming
3. ✅ Vague tasks made specific with output artifacts
4. ✅ Per-phase Definition of Done added
5. ✅ Multi-session tests added (ADR-4 validation)
6. ✅ Contract smoke test checkpoint added after Phase 3

**Readiness Checklist:**
- [x] All blockers resolved
- [x] Plan covers all SDD components
- [x] Tasks are actionable and well-sequenced
- [x] Test tasks included in each phase
- [x] Dependencies correctly mapped
- [x] Per-phase DoD defined

### Phase Structure

| Phase | Description | Tasks | Dependencies |
|-------|-------------|-------|--------------|
| 1 | Core Auth Components | 27 | None |
| 2 | Middleware Layer | 30 | Phase 1 |
| 3 | Auth Endpoints | 38 | Phases 1, 2 |
| 4 | Route Integration | 18 | Phases 2, 3 |
| 5 | E2E Validation | 24 | All phases |

**Total: 137 tasks**

### Files to Create

```
userfrosting/src/BuyerKiosk/MobileApi/
├── Controllers/MobileAuthController.php
├── Middleware/HybridAuthMiddleware.php
├── Middleware/StoreAccessMiddleware.php
├── Models/AuthContext.php
└── Services/LoginRateLimiter.php

userfrosting/routes/groups/mobile-auth.php
```

### Files to Modify

- `userfrosting/routes/groups/mobile.php` - Add middleware
- `userfrosting/routes/api.php` - Register auth routes

**Status: ✅ Ready for Implementation**

### Next Steps

1. Run `/start:implement 023` to begin TDD implementation

### Phase 1 Kickoff

**What Phase 1 Delivers:**
- AuthContext model with readonly properties and helper methods
- LoginRateLimiter service with Redis-based rate limiting

**Files to Create:**
- `userfrosting/src/BuyerKiosk/MobileApi/Models/AuthContext.php`
- `userfrosting/src/BuyerKiosk/MobileApi/Services/LoginRateLimiter.php`
- `tests/Unit/MobileApi/Models/AuthContextTest.php`
- `tests/Unit/MobileApi/Services/LoginRateLimiterTest.php`

**Tests to Write First (TDD):**
1. AuthContext construction with all properties
2. hasStoreAccess() returns true/false correctly
3. isManager() returns true for roleLevel <= 2
4. checkRateLimit returns null under limit, TTL at limit
5. recordFailedAttempt increments counter
6. Fail-open when Redis unavailable

---

## Implementation Complete Summary (2026-01-01)

### Final Test Results

| Category | Tests | Assertions | Status |
|----------|-------|------------|--------|
| AuthContext | 21 | 45 | ✅ PASS |
| LoginRateLimiter | 20 | 41 | ✅ PASS |
| HybridAuthMiddleware | 12 | 61 | ✅ PASS |
| StoreAccessMiddleware | 23 | 61 | ✅ PASS |
| MobileAuthController | 23 | 103 | ✅ PASS |
| Route Integration | 12 | 18 | ✅ PASS |
| **TOTAL** | **111** | **329** | ✅ **ALL PASS** |

### SDD Scenario Coverage

All 9 test scenarios from SDD verified:
1. ✅ Successful Login (returns tokens, user, stores)
2. ✅ Invalid Credentials (401 with error_code)
3. ✅ Rate Limiting (429 + Retry-After after 5 attempts)
4. ✅ Token Refresh with Rotation (atomic, old token revoked)
5. ✅ JWT Protected Endpoint (sets authContext + jwtUser)
6. ✅ Expired Token Rejected (no APIKey fallback)
7. ✅ APIKey Backward Compatibility (deprecated usage logged)
8. ✅ Store Access Denied (403 + security audit log)
9. ✅ Concurrent Refresh (race condition handled)

### Files Created

```
userfrosting/src/BuyerKiosk/MobileApi/
├── Controllers/
│   └── MobileAuthController.php    # Login, refresh, logout endpoints
├── Middleware/
│   ├── HybridAuthMiddleware.php    # JWT + APIKey hybrid auth
│   └── StoreAccessMiddleware.php   # Store-level authorization
├── Models/
│   └── AuthContext.php             # Standardized auth context
└── Services/
    └── LoginRateLimiter.php        # Redis-based rate limiting

userfrosting/routes/groups/
└── mobile-auth.php                 # Auth endpoint routes

tests/Unit/MobileApi/                # 96 unit tests
tests/Integration/MobileApi/         # 12 integration tests
```

### Files Modified

- `userfrosting/routes/groups/mobile.php` - Added middleware to ~60 routes
- `userfrosting/routes/api.php` - Registered auth route group

### Environment Requirements

| Variable | Required | Description |
|----------|----------|-------------|
| `JWT_SECRET` | ✅ Yes | Secret for HS256 JWT signing (32+ chars recommended) |
| `REDIS_HOST` | Recommended | Redis host for rate limiting (fails open if unavailable) |
| `REDIS_PORT` | Optional | Redis port (default: 6379) |

### PHPStan Status

✅ No errors on new MobileApi code (Controllers, Middleware, Models, Services)

### Deployment Checklist

- [x] All unit tests pass (96 tests)
- [x] All integration tests pass (12 tests)
- [x] All 9 SDD scenarios verified
- [x] All 6 security tests pass
- [x] PHPStan clean on new code
- [x] Environment variables documented
- [ ] JWT_SECRET configured in production
- [ ] Redis available for rate limiting
- [ ] Deploy to staging for E2E testing
- [ ] Mobile app updated to use new endpoints

---
*This file is managed by the specification-management skill.*
