# Product Requirements Document: Unified Users & Modern Authentication

**Specification ID:** 007-unified-users-auth
**Version:** 1.0
**Status:** DRAFT
**Last Updated:** December 2025

---

## 1. Executive Summary

### 1.1 Problem Statement

BuyerKiosk currently maintains two separate systems for managing people:
- **Users** (`uf_user` in central database) - Authentication accounts with login capabilities
- **Employees** (`employees` in per-store databases) - Worker records with employment data

This dual-system architecture creates:
- **Data duplication** - Same person's name, email, phone exists in multiple tables
- **Complex linking** - Bridge table (`user_employee_links`) required to connect systems
- **Maintenance burden** - Updates must happen in multiple places
- **Security vulnerabilities** - Legacy authentication with 8 critical issues identified
- **Limited scalability** - No modern auth standards (OAuth2, MFA)

### 1.2 Proposed Solution

Merge the employee and user systems into a **single unified users table** in the central database, combined with comprehensive authentication modernization including:
- JWT-based authentication with RS256 signing
- Optional multi-factor authentication (TOTP)
- Rate limiting and brute-force protection
- Backward compatibility for existing mobile apps via legacy API keys

### 1.3 Business Value

| Value Driver | Impact |
|--------------|--------|
| Simplified Data Model | One source of truth for person data |
| Reduced Complexity | Eliminate 3 tables and linking logic |
| Modern Security | Industry-standard authentication |
| Future-Ready | OAuth2 enables SSO, partner integrations |
| Maintained Compatibility | Existing mobile apps continue working |

---

## 2. Stakeholder Analysis

### 2.1 Primary Stakeholders

| Stakeholder | Role | Needs |
|-------------|------|-------|
| Store Owners | Business users | Simplified employee management, secure access |
| Store Managers | Daily operators | Easy employee lookup, schedule integration |
| Employees | End users | Seamless login, optional MFA for security |
| Mobile App Users | External access | Continued API key functionality |
| System Administrators | IT/Operations | Clear audit trails, security compliance |

### 2.2 Secondary Stakeholders

| Stakeholder | Role | Needs |
|-------------|------|-------|
| Development Team | Implementation | Clean architecture, maintainable code |
| Security Team | Compliance | Vulnerability remediation, audit logs |
| Integration Partners | External systems | OAuth2 API access |

---

## 3. Goals & Success Criteria

### 3.1 Primary Goals

1. **Unified Identity**: Single `users` table for all person records
2. **Modern Auth**: Industry-standard JWT + OAuth2 authentication
3. **Security Hardening**: Fix all critical and high-severity vulnerabilities
4. **Backward Compatibility**: Existing API keys continue working

### 3.2 Success Criteria

| Criterion | Metric | Target |
|-----------|--------|--------|
| Data Unification | Person records in single table | 100% |
| Legacy API Compatibility | Existing mobile apps functional | 100% |
| Modern JWT Auth | New integrations using OAuth2 | Available |
| MFA Adoption | Users with MFA enabled | Opt-in available |
| Critical Vulnerabilities | Security issues resolved | All 8 critical |
| Authentication Downtime | Service disruption during migration | Zero |

### 3.3 Non-Goals (Explicit Exclusions)

| Exclusion | Rationale |
|-----------|-----------|
| Forced mobile app migration | Would break existing deployments |
| External OAuth providers (Google, Facebook) | Future phase consideration |
| Passwordless-only authentication | Market not ready |
| Complete API key removal | Requires partner coordination |

---

## 4. Current State Analysis

### 4.1 Database Architecture

**Central Database (`kiosk_users`):**
| Table | Purpose | Records (Est.) |
|-------|---------|----------------|
| `uf_user` | User accounts | ~500 |
| `uf_group` | Roles/groups | ~20 |
| `uf_group_user` | User-group membership | ~1,500 |
| `uf_authorize_user` | User permissions | ~200 |
| `uf_authorize_group` | Group permissions | ~100 |
| `uf_store_user` | User-store access | ~1,000 |
| `uf_user_rememberme` | Remember me tokens | Variable |
| `uf_apiKey` / `uf_apiKey_user` | API keys | ~50 |
| `user_employee_links` | User-employee bridges | ~400 |
| `employee_invitations` | Self-registration | ~50 |

**Per-Store Databases (`kiosk_{typeNum}`):**
| Table | Purpose | Records per Store (Est.) |
|-------|---------|--------------------------|
| `employees` | Worker records | 5-50 |
| `employee_sync_log` | Sync audit | Variable |

### 4.2 Security Vulnerabilities (Current)

| ID | Severity | Issue | Location |
|----|----------|-------|----------|
| CRIT-001 | Critical | CSRF disabled on login | AccountController.php:217-221 |
| CRIT-002 | Critical | SHA1 passwords accepted | MySqlUser.php:358-365 |
| CRIT-003 | Critical | Password hash upgrade bug | MySqlUser.php:401-410 |
| CRIT-004 | Critical | API key timing attack | MySqlUser.php:390 |
| CRIT-005 | Critical | No login rate limiting | AccountController::login() |
| CRIT-006 | Critical | No password reset rate limiting | AccountController::forgotPassword() |
| CRIT-007 | Critical | JWT implementation broken | AccountController.php:296-322 |
| CRIT-008 | Critical | Session cookie missing flags | config-userfrosting.php |
| HIGH-001 | High | No account lockout | Multiple |
| HIGH-002 | High | Password reset tokens don't expire | AccountController.php |
| HIGH-003 | High | RememberMe tokens unencrypted | uf_user_rememberme |
| HIGH-004 | High | No MFA support | System-wide |
| HIGH-005 | High | Fragile hash type detection | Authentication.php:7-14 |
| HIGH-006 | High | Username enumeration | Login errors |

#### HIGH-003 Resolution Decision: Remember-Me Token Strategy

**Current State:** The `uf_user_rememberme` table stores plaintext tokens that allow persistent login. These tokens are vulnerable to theft if the database is compromised.

**Chosen Approach:** **Replace with Hashed, One-Time-Use Tokens**

Rather than remove remember-me functionality (which users expect), the implementation will:
1. Store remember-me tokens as hashed values (using same hash_equals() pattern as refresh tokens)
2. Implement token rotation on each use (one-time-use)
3. Add token expiration (30 days default)
4. Migrate existing tokens during cutover by invalidating them (users will need to re-authenticate)
5. Eventually deprecate `uf_user_rememberme` table in favor of `oauthRefreshTokens` with a `remember_me` client type

See Implementation Plan Task 1.9 for details.

### 4.3 Code Touchpoints

| Category | Files | Queries | Migration Complexity |
|----------|-------|---------|---------------------|
| User Models & Auth | 8 | ~40 | HIGH |
| Employee Management | 12 | ~60 | HIGH |
| User-Employee Linking | 6 | ~20 | MEDIUM |
| Workbook/Daybook | 8 | ~15 | LOW |
| Statistics & Reports | 5 | ~10 | MEDIUM |
| External Integrations | 6 | ~25 | HIGH |
| API Endpoints | 10 | ~30 | MEDIUM |
| **TOTAL** | **55+** | **200+** | |

---

## 5. User Personas & Journeys

### 5.1 Persona: Store Owner (Sarah)

**Background:** Owns 3 pawn shop locations, manages 15 employees across stores.

**Goals:**
- Single view of all employees across stores
- Secure access to business data
- Easy employee onboarding

**Pain Points:**
- Currently must manage user accounts AND employee records separately
- Employees working at multiple stores have multiple records
- No confidence in system security

**User Journey - Employee Onboarding:**
```
Current State:
1. Create employee record in store database
2. Separately create user account (if needed)
3. Manually link user to employee
4. Assign groups/permissions
5. Send login credentials

Target State:
1. Create person record (single step)
2. Assign to stores with roles
3. Enable login access (toggle)
4. System sends invitation email
5. Employee self-registers with MFA option
```

### 5.2 Persona: Store Manager (Mike)

**Background:** Manages daily operations at one location, 8 direct reports.

**Goals:**
- Quick access to employee schedules
- Manage time punches
- Review performance stats

**Pain Points:**
- Employee data scattered across systems
- Cannot easily see which employees have system access
- No audit trail for sensitive operations

**User Journey - Daily Operations:**
```
Current State:
1. Check WhenIWork for schedule
2. Look up employee in store DB for details
3. Check user system for login issues
4. Manual cross-reference between systems

Target State:
1. Single employee list with all data
2. Schedule, contact, and access info unified
3. Clear indication of login capability
4. Activity audit available
```

### 5.3 Persona: Employee (Alex)

**Background:** Part-time buyer, works at 2 locations.

**Goals:**
- Simple login process
- Access work schedule
- Clock in/out easily

**Pain Points:**
- Has 2 employee records (one per store)
- Password reset confusing
- No security confidence

**User Journey - Multi-Store Access:**
```
Current State:
1. Log in with credentials
2. Select store (limited visibility)
3. Actions tied to specific employee record
4. Different "identity" per store

Target State:
1. Log in with credentials (+ optional MFA)
2. See all assigned stores
3. Unified identity across stores
4. Actions tracked to single person
```

---

## 6. Functional Requirements

### 6.1 Unified Users System

| ID | Requirement | Priority | Acceptance Criteria |
|----|-------------|----------|---------------------|
| FR-001 | Single person record per individual | P0 | All person data in central `users` table |
| FR-002 | Store assignments via junction table | P0 | `user_store_assignments` links users to stores |
| FR-003 | Login capability as flag | P0 | `can_login` boolean controls system access |
| FR-004 | Account types distinguish roles | P1 | employee, user, admin, system types supported |
| FR-005 | External provider sync preserved | P0 | WhenIWork/Homebase sync creates users with source tracking |
| FR-006 | Employee promotion to user | P1 | Setting `can_login=true` grants access |
| FR-007 | Multi-store person detection | P2 | Same email/name across stores merged into single record |

### 6.2 Modern Authentication

| ID | Requirement | Priority | Acceptance Criteria |
|----|-------------|----------|---------------------|
| FR-101 | JWT access tokens (RS256) | P0 | 15-minute lifetime, asymmetric signing |
| FR-102 | Refresh token rotation | P0 | 7-day lifetime, one-time use |
| FR-103 | Session-based auth for web | P0 | PHP sessions with secure cookies |
| FR-104 | Legacy API key support | P0 | Existing mobile apps continue working |
| FR-105 | TOTP multi-factor auth | P1 | Google Authenticator compatible |
| FR-106 | Backup codes for MFA recovery | P1 | 10 one-time use codes |
| FR-107 | Rate limiting on login | P0 | 10 attempts per 15 min per IP |
| FR-108 | Rate limiting on password reset | P0 | 3 attempts per hour per email |
| FR-109 | Account lockout on failures | P1 | Lock after 5 failures for 15 minutes |

### 6.3 Security Hardening

| ID | Requirement | Priority | Acceptance Criteria |
|----|-------------|----------|---------------------|
| FR-201 | CSRF protection enabled | P0 | Login endpoint validated; other state-changing routes (password change, account settings) in follow-on phase |
| FR-202 | Argon2id password hashing | P0 | All new passwords use Argon2id |
| FR-203 | Legacy hash upgrade on login | P0 | SHA1/BCrypt auto-upgraded to Argon2id |
| FR-204 | Timing-safe comparisons | P0 | All secrets compared with hash_equals() |
| FR-205 | Secure session cookies | P0 | HttpOnly, Secure, SameSite=Strict (environment-aware) |
| FR-206 | Password reset expiration | P0 | Tokens expire after 1 hour |
| FR-207 | Auth audit logging | P1 | All auth events logged with IP/timestamp |

> **Note on FR-201:** Phase 1 re-enables CSRF on the login endpoint. Comprehensive CSRF coverage for other POST/PUT/DELETE routes (password change, account settings, API endpoints) should be addressed in a follow-on security hardening pass.

### 6.4 Backward Compatibility

| ID | Requirement | Priority | Acceptance Criteria |
|----|-------------|----------|---------------------|
| FR-301 | Legacy API key authentication | P0 | `Authorization: ApiKey` header still works |
| FR-302 | X-Api-Key header support | P0 | Alternative header format preserved |
| FR-303 | Employee API endpoint compatibility | P1 | Existing endpoints return same shape data |
| FR-304 | Session-based web login | P0 | Existing web login flow preserved |
| FR-305 | Compatibility database views | P1 | `uf_user` view for legacy queries |

---

## 7. Non-Functional Requirements

### 7.1 Performance

| ID | Requirement | Target |
|----|-------------|--------|
| NFR-001 | Login response time | < 500ms (95th percentile) |
| NFR-002 | JWT validation time | < 10ms |
| NFR-003 | User lookup time | < 50ms |
| NFR-004 | Rate limit check | < 5ms (Redis) |

### 7.2 Security

| ID | Requirement | Target |
|----|-------------|--------|
| NFR-101 | Password hash time | ~100ms (Argon2id tuning) |
| NFR-102 | Token entropy | 256 bits minimum |
| NFR-103 | Key rotation support | Quarterly rotation capability |
| NFR-104 | Audit log retention | 90 days minimum |

### 7.3 Availability

| ID | Requirement | Target |
|----|-------------|--------|
| NFR-201 | Authentication uptime | 99.9% |
| NFR-202 | Migration downtime | Zero (parallel systems) |
| NFR-203 | Rollback time | < 4 hours |

### 7.4 Scalability

| ID | Requirement | Target |
|----|-------------|--------|
| NFR-301 | Concurrent users | 1,000+ |
| NFR-302 | Users per system | 10,000+ |
| NFR-303 | Login attempts per minute | 100+ |

---

## 8. External Integrations

### 8.1 WhenIWork Integration

**Current Behavior:**
- Syncs employees from WhenIWork scheduling system
- Creates/updates `employees` records in store databases
- Tracks `source='wheniwork'` and `externalId`

**Target Behavior:**
- Syncs to central `users` table instead
- Creates `user_store_assignments` for the specific store
- Sets `can_login=false` (employee-only by default)
- Preserves sync functionality and conflict resolution

### 8.2 Homebase Integration

**Current Behavior:**
- Stub implementation for Homebase scheduling
- Same pattern as WhenIWork

**Target Behavior:**
- Full implementation targeting `users` table
- Same sync patterns as WhenIWork

### 8.3 Mobile App API

**Current Behavior:**
- Uses `ApiKey` header authentication
- Calls `/:typeNum/api/employees` endpoints

**Target Behavior:**
- Legacy API key authentication preserved
- Existing endpoints continue working
- New OAuth2 authentication available for upgrades

---

## 9. Data Migration Requirements

### 9.1 Migration Scope

| Source | Records | Destination | Strategy |
|--------|---------|-------------|----------|
| `uf_user` | ~500 | `users` (can_login=true) | Direct migration |
| `employees` (linked) | ~400 | Merge into existing `users` | Add store assignments |
| `employees` (unlinked) | ~2,000 | `users` (can_login=false) | Create new records |
| `uf_group_user` | ~1,500 | `user_groups` | Rename/migrate |
| `user_employee_links` | ~400 | (deprecated) | Data used during migration |
| `employee_sync_log` | Variable | `user_sync_log` | Centralize per-store logs |

### 9.2 Duplicate Detection

**Problem:** Same person may exist in multiple stores with different employee IDs.

**Detection Strategy:**
1. Match by email (primary identifier)
2. Match by first_name + last_name (secondary)
3. Match by external_id + source (definitive for synced)

**Resolution:**
- Same email across stores → merge into single user
- Same name but different email → keep separate (flag for review)
- Same external_id and source → definitely same person

### 9.3 Password Migration

**Strategy:** Migrate hashes as-is, upgrade on next login

- SHA1 hashes: Preserved, upgraded to Argon2id on successful login
- BCrypt hashes: Preserved, upgraded to Argon2id on successful login
- New passwords: Argon2id from creation

---

## 10. Risks & Mitigations

### 10.1 Technical Risks

| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| Data loss during migration | Low | Critical | Backup before migration, parallel run period |
| Session invalidation | Medium | High | Gradual rollout, force re-login during maintenance |
| API compatibility break | Medium | Critical | Dual-auth middleware, extensive testing |
| Performance degradation | Low | Medium | Index optimization, Redis caching |
| External sync failures | Medium | High | Maintain sync compatibility, extensive testing |

### 10.2 Business Risks

| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| Mobile app disruption | Low | Critical | Maintain API key auth, test thoroughly |
| User confusion (MFA) | Medium | Low | Optional MFA, clear documentation |
| Store data isolation | Low | Medium | Clear audit trails, permission checks |

### 10.3 Rollback Strategy

| Phase | Rollback Action | Time |
|-------|-----------------|------|
| Schema Creation | Drop new tables | < 1 hour |
| Auth Infrastructure | Disable middleware | < 1 hour |
| Data Migration | Restore from backup | 2-4 hours |
| Code Migration | Git revert | 1-2 hours |

---

## 11. Compliance & Audit

### 11.1 PCI-DSS Considerations

| Requirement | Current | Target |
|-------------|---------|--------|
| 8.1.6 Lockout after 6 attempts | FAIL | PASS |
| 8.2.3 Strong passwords | FAIL | PASS |
| 8.2.5 Password history | FAIL | Future |
| 8.4 Multi-factor auth | FAIL | PASS (optional) |

### 11.2 Audit Requirements

| Event | Logged Data | Retention |
|-------|-------------|-----------|
| Login success/failure | User, IP, timestamp, user agent | 90 days |
| Password change | User, IP, timestamp | 90 days |
| MFA events | User, IP, timestamp, method | 90 days |
| Token operations | User, IP, timestamp, token type | 90 days |
| Account lockout | User, IP, timestamp, reason | 90 days |

---

## 12. Future Considerations

### 12.1 Phase 2 Candidates

| Feature | Description | Dependencies |
|---------|-------------|--------------|
| External OAuth (Google/Microsoft) | Social login | Core auth complete |
| WebAuthn/Passkeys | Passwordless option | MFA infrastructure |
| API key deprecation | Remove legacy auth | Partner migration |
| Single Sign-On (SSO) | Enterprise integration | OAuth2 complete |

### 12.2 Technical Debt to Address

| Item | Current State | Target State |
|------|---------------|--------------|
| UserFrosting framework | Heavily customized | Consider migration path |
| Session management | PHP native | Redis-backed for scaling |
| Permission system | Complex authorize tables | Simplified RBAC |

---

## Document History

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | December 2025 | System Analysis | Initial formal PRD extracted from unified spec |
| 1.1 | December 2025 | Consistency Review | Clarified FR-201 CSRF scope (login only in Phase 1, other routes follow-on); updated FR-205 to note environment-aware secure flag |
| 1.2 | December 2025 | Gap Analysis Fix | Added HIGH-003 resolution decision (hashed, one-time-use remember-me tokens) |
