# AUTH-004 — Session Timeout and Authorization Middleware
## Implementation Memory

| Field | Value |
|-------|-------|
| Task | AUTH-004 — Session Timeout and Authorization Middleware |
| Sprint | Sprint 2 — Authentication & Authorization |
| Status | ✅ Implementation Complete — Awaiting Human Approval |
| Depends On | AUTH-001 (commit `50e9b19`), AUTH-002 (commit `499f8c2`) |
| Conflict Resolved | `33_AUTH004_conflict.md` — RoleMiddleware colon-suffix notation |

---

## 1. Files Created

| File | Purpose |
|------|---------|
| `app/middleware/AuthMiddleware.php` | Authentication presence + session timeout enforcement |
| `app/middleware/RoleMiddleware.php` | Role-based portal access guard (path-prefix inference) |

## 2. Files Modified

| File | Change |
|------|--------|
| `app/core/Session.php` | Added 3 timeout helpers: `isInactivityExpired()`, `isAbsoluteExpired()`, `touch()`. Updated class docblock. |
| `app/modules/Auth/AuthService.php` | Added 2 public timeout-property methods: `getAbsoluteTimeout()`, `getInactivityTimeout()`. Updated class docblock. |

---

## 3. Conflict Resolution Summary

**Conflict:** `Router.php` docblock (line 111) showed `'App\\Middleware\\RoleMiddleware:couple'` notation implying parameterized middleware. `MiddlewarePipeline::wrap()` (line 162) uses `new $class()` with zero constructor args — the colon-suffix notation is not supported.

**Resolution (approved by Human Approver via `42_AUTH004_REV.md`):**
- The colon-suffix notation in the Router docblock is **non-normative/illustrative only**.
- MiddlewarePipeline remains frozen.
- `RoleMiddleware` infers the required role from the URL path prefix (Option D):
  - `/app/couple/*` → `couple`
  - `/app/crew/*` → `crew`
  - `/app/admin/*` → `super_admin`
- The conflict and resolution are documented in `RoleMiddleware.php` docblock.
- A Design Revision Pack is required to add parameterized middleware support in a future sprint.

---

## 4. Design Decisions

### AuthMiddleware
- Reads timeout config directly via `config()` helper — no AuthService dependency.
- On unauthenticated: simple `header('Location: /login') + exit` — no flash message.
- On timeout: `Session::destroy()` → `session_start()` → `Session::regenerate(false)` → write flash error → redirect. Mirrors `AuthService::performLogout()`.
- `ROLE_TIMEOUT_KEY` constant maps `super_admin` → `admin` config key (matching `config/session.php` key names).
- Falls back to `DEFAULT_ABSOLUTE_TIMEOUT = 28800` (8h / strictest) for unrecognised roles.

### RoleMiddleware
- `PATH_ROLE_MAP` constant: `/app/couple` → `couple`, `/app/crew` → `crew`, `/app/admin` → `super_admin`.
- Prefix match: `$path === $prefix || strpos($path, $prefix . '/') === 0` — prevents `/app/coupleOther` false-positive.
- Unknown path (RoleMiddleware applied outside portal paths): logs warning + renders 403. Safe-fail default.

### Session.php
- `isInactivityExpired()` and `isAbsoluteExpired()`: return `false` (safe-pass) when session key is missing.
- `touch()`: no-op when session not active.
- AUTH-001's `bindSession()` always sets both `last_active_at` and `login_at` on login, so the safe-pass case only applies to edge-case malformed sessions.

### AuthService.php
- Added `getAbsoluteTimeout(string $role): int` and `getInactivityTimeout(): int`.
- Makes AuthService the canonical holder of timeout config within the Auth module.
- AuthMiddleware does NOT call AuthService to avoid middleware-to-module coupling.

---

## 5. Session Keys Used (AUTH-004)

| Key | Set By | Read By |
|-----|--------|---------|
| `auth_user_id` | `AuthService::bindSession()` (AUTH-001) | `AuthMiddleware::handle()` |
| `role_snapshot` | `AuthService::bindSession()` (AUTH-001) | `AuthMiddleware::handle()`, `RoleMiddleware::handle()` |
| `login_at` | `AuthService::bindSession()` (AUTH-001) | `Session::isAbsoluteExpired()` |
| `last_active_at` | `AuthService::bindSession()` (AUTH-001), `Session::touch()` | `Session::isInactivityExpired()` |

---

## 6. Middleware Registration Pattern

Portals register both middleware in this order (per SPRINT2_EXECUTION_GUIDE §7):

```php
$router->group('/app/couple', function ($router) {
    $router->get('/dashboard', 'App\Modules\Dashboard\CoupleDashboardController@index');
    // ...
}, [
    'App\Middleware\AuthMiddleware',
    'App\Middleware\RoleMiddleware',
]);
```

`CsrfMiddleware` is prepended on POST routes only. `WorkspaceMiddleware` is added in Sprint 3.

---

## 7. Documentation Deviation

| Item | Specification | Actual Implementation |
|------|--------------|-----------------------|
| RoleMiddleware role injection | Router docblock implied colon-suffix `RoleMiddleware:couple` | Path-prefix inference (approved). Zero constructor args. |

**Status:** Formally documented in `RoleMiddleware.php` class docblock and `33_AUTH004_conflict.md`. Requires DRP for parameterized middleware support.
