# Phase 1 — Module 1: Authentication

This is the first module of the ERP Foundation System. It's a complete,
production-ready CodeIgniter 4 authentication system: login, logout,
forgot/reset/change password, remember-me, session tracking, idle timeout,
login-attempt rate limiting, account lockout, login history, active-session
management with "force logout other devices," and login/audit event logging.

It also ships the shared application shell (light/dark mode, collapsible
sidebar, sticky header, breadcrumb, notification dropdown, user menu) that
every future module will render inside.

Two-Factor Authentication is architecture-ready (`users.two_factor_enabled` /
`two_factor_secret` columns, `Config\Auth::$twoFactorEnabled` switch) but the
actual OTP/TOTP delivery is intentionally left for a later phase, per the
project brief.

## Requirements

- PHP 8.3+
- MySQL 8+ or MariaDB 10.6+
- Composer 2.x

## Setup

```bash
# 1. Install dependencies (run this on your own machine — Packagist must be
#    reachable, which it isn't from the sandbox this was built in)
composer install

# 2. Copy and edit environment config
cp .env.example .env   # (this repo already ships a filled-in .env — review it)
php spark key:generate

# 3. Create the database (matches .env: erp_foundation by default)
mysql -u root -e "CREATE DATABASE erp_foundation CHARACTER SET utf8mb4;"

# 4. Run migrations
php spark migrate

# 5. Seed the default administrator
#    Set ADMIN_SEED_PASSWORD in .env first, or leave it unset to have a
#    strong random password generated and printed once to your console.
php spark db:seed DatabaseSeeder

# 6. Serve the app
php spark serve
```

Visit `http://localhost:8080/login`.

## What's included

| Layer | Files |
|---|---|
| Config | `app/Config/Auth.php` — lockout, password policy, session timeout, 2FA switch |
| Migrations | `users`, `login_attempts`, `user_sessions`, `password_resets`, `password_history`, `activity_logs`, `ci_sessions` |
| Seeders | `DefaultAdminSeeder`, `DatabaseSeeder` (orchestrator — future modules register here) |
| Models | `Modules\Auth\Models\{UserModel,LoginAttemptModel,UserSessionModel,PasswordResetModel,PasswordHistoryModel}`, `App\Models\ActivityLogModel` (shared) |
| Services | `Modules\Auth\Services\{AuthService,PasswordPolicyService}` — all business logic; controllers stay thin |
| Controllers | `Modules\Auth\Controllers\{LoginController,LogoutController,PasswordController,SessionController}` |
| Filters | `Modules\Auth\Filters\{AuthFilter,GuestFilter}` — registered as `auth` / `guest` in `Config/Filters.php` |
| Views | `Modules/Auth/Views/*` (login, forgot/reset/change password, sessions) + shared `layouts/main`, `layouts/auth`, `partials/sidebar`, `partials/navbar` |
| Routes | `Modules/Auth/Config/Routes.php`, required from `app/Config/Routes.php` |

## Security notes

- Passwords hashed with bcrypt (cost 12); never logged or exposed via `UserEntity::toArray()`.
- CSRF protection is on by default (`Config\Security`, unchanged from the CI4 default — verify it's enabled in your copy).
- Session fixation prevented via `session()->regenerate(true)` on every successful login.
- Sessions persisted in the database (`ci_sessions` table) rather than flat files, so multiple app servers behind a load balancer share session state.
- Login rate limiting is both per-identifier and per-IP (`Config\Auth::$lockoutStrategy = 'both'`).
- Password reset always returns the same generic message whether or not the account exists (prevents user enumeration).
- `remember_token` and `two_factor_secret` are never serialized out of `UserEntity`.

## Module structure (HMVC-style)

Modules live under `app/Modules/{ModuleName}` with their own
Controllers/Models/Services/Views/Filters/Config/Language, registered via the
`Modules\` PSR-4 namespace in `composer.json`. Migrations and seeders stay
centralized under `app/Database` (simpler cross-module dependency management
for an ERP where, e.g., `users` needs `roles`, `branches`, and `departments`).

## Known placeholders (intentional — built in later phases)

- The sidebar links to `/users`, `/roles`, `/permissions`, `/company`,
  `/branches`, `/departments`, `/settings`, `/activity-logs`, `/audit-logs`,
  `/files` — none of these routes exist yet. They 404 until their modules
  are built. This is expected; the navigation is scaffolded ahead of the
  backend so each new module just needs its route added.
- `app/Views/dashboard/index.php` is a bare confirmation page. Real widgets
  (user/branch counts, charts, system health) ship with the Dashboard module.
- Two-Factor Authentication: schema and config switch exist; OTP delivery
  does not yet.
- Email delivery for password-reset links is a TODO hook in
  `PasswordController::forgotSubmit()` — wire in your SMTP settings once the
  Company Settings module (which owns SMTP config) is built.

## Testing checklist once you have `composer install` working

1. `php spark migrate` runs clean, no errors.
2. `php spark db:seed DatabaseSeeder` creates the admin and prints credentials.
3. Log in at `/login` with those credentials → redirected to `/dashboard`.
4. Enter the wrong password 5 times → account locks for 15 minutes (`Config\Auth::$maxLoginAttempts` / `$lockoutMinutes`).
5. `/account/sessions` shows the current session; "Force Logout Other Devices" revokes any others.
6. `/password/change` rejects weak passwords and rejects password reuse.
7. Toggle dark mode (top-right) — preference persists across page loads.
8. Collapse the sidebar (desktop) / open it via the hamburger icon (mobile) — both persist/work correctly.

## Next modules (in build order)

2. User Management (depends on Auth)
3. Roles & Permissions (`role_id` FK on `users` gets enforced here)
4. Branch Management / Department Management (`branch_id`/`department_id` FKs enforced here)
5. Company Settings (SMTP config completes the password-reset email flow)
6. Dashboard (real widgets)
7. Notifications
8. Activity Logs UI (table already exists and is being written to)
9. Audit Logs
10. System Settings
11. File Manager
