# Auth — Technical Design

> Produced by the Reversa **Writer** (phase: generation) · doc_level: `complete`
> Generated on 2026-09-19

**Confidence scale:** 🟢 CONFIRMED (read directly from code) · 🟡 INFERRED · 🔴 GAP

## Interface

### HTTP endpoints (registered by `Admin::registerAuthRoutes()`)

The application prefix is empty (`config('admin.route.prefix') === ''`), so the paths below sit at the site root. The auth controller lives in the vendored namespace `Encore\Admin\Controllers`. 🟢 (`vendor/…/Admin.php:234-258`)

| Method | Path | Input | Output | Status codes |
|--------|------|-------|--------|--------------|
| GET | `auth/login` | — (session) | `admin::login` view, or redirect if already authenticated | 200, 302 |
| POST | `auth/login` | `username: string` (required), `password: string` (required), `_token` (CSRF) | 302 redirect to intended/`/pos` on success; 302 back to form with errors on failure | 302, 422¹ |
| GET | `auth/logout` | — (session) | 302 redirect to admin root (`/`) | 302 |
| GET | `auth/setting` | — (session) | account edit form for the current user | 200, 302² |
| PUT | `auth/setting` | `name: string` (required), `avatar?: file`, `password?: string`, `password_confirmation?: string`, `_token` (CSRF) | 302 redirect back to `auth/setting` with success toast | 302, 422¹ |

¹ Validation failures use Laravel's redirect-back-with-errors pattern (HTTP 302 to the form), not a JSON 422; the `422` marks *semantic* validation failure. 🟡
² `auth/setting` requires authentication (behind the `admin` middleware); an anonymous request is redirected to `auth/login`. 🟢

### Guard / provider

| Symbol | Signature | Returns | Note |
|--------|-----------|---------|------|
| `AuthController::guard()` | `()` | `StatefulGuard` | `Auth::guard('admin')` — session driver. 🟢 (`vendor/…/AuthController.php:196-199`) |
| `AuthController::username()` | `()` | `string` | literal `'username'` — the login identity field. 🟢 (`:186-189`) |
| `AuthController::redirectPath()` | `()` | `string` | `config('admin.route.prefix')` = `''` → root. 🟢 (`:156-163`) |
| `admin` guard provider | — | `Encore\Admin\Auth\Database\Administrator` on `admin_users` | eloquent driver. 🟢 (`config/admin.php:59-63`) |

## Main Flow

### Login (`POST auth/login`) 🟢 (`vendor/…/AuthController.php:39-60`)
1. Collect `only(['username','password'])` from the request.
2. Validate both fields `required`; on failure `back()->withInput()->withErrors($validator)`.
3. `guard()->attempt($credentials)`:
   - **Success →** `sendLoginResponse()`: emit success toast, `session()->regenerate()`, `redirect()->intended(redirectPath())`.
   - **Failure →** `back()->withInput()->withErrors([username => getFailedLoginMessage()])`.
4. `redirectPath()` = `''` (admin root); root is 301-redirected to `/pos` by `routes/web.php:22`, so the operator lands on the POS terminal.

### Login page (`GET auth/login`) 🟢 (`:23-30`)
1. If `guard()->check()` → `redirect(redirectPath())` (already-authenticated no-op).
2. Else render `view('admin::login')`.

### Logout (`GET auth/logout`) 🟢 (`:67-74`)
1. `guard()->logout()`.
2. `request()->session()->invalidate()`.
3. `redirect(config('admin.route.prefix'))` (admin root).

### Route protection (all app routes) 🟢 (`routes/web.php:23-27`, `config/admin.php:29`)
1. Every application route is wrapped in a group with `middleware => ['web','admin']`.
2. The `admin` middleware verifies the `admin` guard session; unauthenticated requests are redirected to `auth/login`.
3. **No** `admin.permission` middleware is present → no per-route authorization (BR-02).

### Self-setting (`GET`/`PUT auth/setting`) 🟢 (`:81-139`)
1. `getSetting` builds an `Administrator::form(...)` bound to `Admin::user()->id` — `username` display-only, `name` required, `avatar` image, `password` + `password_confirmation` (`confirmed|required`).
2. `putSetting` calls `settingForm()->update(Admin::user()->id)`.
3. `saving` hook: if `password` present and differs from the stored hash, `bcrypt()` it; `password_confirmation` is ignored (`$form->ignore`).
4. `saved` hook: success toast + redirect back to `auth/setting`.

## Alternative Flows

- **Already authenticated hits `auth/login`:** redirect to post-login path (no form). 🟢
- **Missing username or password:** validation short-circuits before any DB hit; redirect back with field errors. 🟢
- **Wrong credentials:** generic failure message, input preserved (except password). 🟢
- **Anonymous hits any protected route:** `admin` middleware redirects to login. 🟢
- **Dead framework path:** `RedirectIfAuthenticated` would send authenticated users to `/home`, and the stock `Auth\*` controllers reference the missing `App\User`; neither is routed, so these branches are unreachable. 🟢 (`app/Http/Middleware/RedirectIfAuthenticated.php:19-21`, `_reversa_sdd/code-analysis.md#module-auth`)

## Dependencies

- **Encore\Admin package (`salipropham/laravel55-admin`)** — provides `AuthController`, the `admin` guard, the `admin::login` view, and `registerAuthRoutes()`. This unit is a thin wrapper over it; reimplementation must either vendor equivalent behavior or reproduce the guard contract. 🟢
- **Laravel session + `web` middleware group** — cookies, CSRF (`VerifyCsrfToken`), session store. 🟢
- **`admin_users` table** (Administrator model) — the credential store. 🟢 (`database/migrations/2016_01_04_173148_create_admin_tables.php`)
- **Root redirect (`routes/web.php:22`)** — determines the effective post-login landing page (`/pos`). 🟢

## Identified Design Decisions

| Decision | Evidence in code | Confidence |
|----------|------------------|------------|
| Single Encore\Admin session guard as the sole auth stack (framework `web` guard left dead). | `routes/web.php:20`, `config/admin.php:50-64`, `config/auth.php:71` | 🟢 |
| Authentication-only access control; RBAC seeded but permission middleware not wired. | `config/admin.php:29`, `_reversa_sdd/adrs/0009-authentication-only-access-control.md` | 🟢 |
| Login identity is `username`, not email. | `vendor/…/AuthController.php:186-189` | 🟢 |
| bcrypt password hashing, re-hash only on change. | `vendor/…/AuthController.php:127-131` | 🟢 |
| Session regenerate on login / invalidate on logout. | `vendor/…/AuthController.php:71,176` | 🟢 |
| Post-login landing delegated to root→`/pos` 301 redirect rather than a controller redirect. | `routes/web.php:22`, `AuthController.php:156-163` | 🟢 |

## Internal State

The unit itself is stateless request-to-request; the only persisted state is:
- The **session** (server-side session store) holding the authenticated `admin` guard identity; created on login, regenerated on login, destroyed on logout. 🟢
- The **`admin_users`** row (credential + profile), mutated only via `PUT auth/setting`. 🟢

## Observability

- Success toasts via `admin_toastr(trans('admin.login_successful'))` and `admin.update_succeeded` (UI feedback, not server logs). 🟢 (`vendor/…/AuthController.php:134,174`)
- `admin_operation_log` table exists but operation logging is disabled (`operation_log.enable = false`), so admin actions — including auth — are **not** audit-logged. 🟢 (`config/admin.php`, `_reversa_sdd/code-analysis.md#module-auth`)
- 🟢 No dedicated authentication log (no failed-login counter, no lockout). Decided 2026-09-25 (`questions.md#question-15`): not needed in the reimplementation — kept as legacy behaviour.

## Risks & Gaps

- 🟡 The `422` status in the interface table is a semantic label; the legacy actually redirects (302) back to the form with a Laravel error bag. A REST reimplementation must decide whether to keep redirect-based errors or return JSON 422.
- 🟢 No brute-force protection: `postLogin` has no throttle/lockout and failed logins are not recorded. Confirmed intentional 2026-09-25 (`questions.md#question-15`) — not needed.
- 🟡 HTTPS enforcement depends on `ADMIN_SECURE` (default `true`); the effective value in each environment is not in the repo. Validate with ops.
- 🟢 The RBAC management screens (`auth/users|roles|permissions|menu|logs`) are registered by the same `registerAuthRoutes()` call but are intentionally excluded from this unit; they are covered by the (unenforced) RBAC model documented in `_reversa_sdd/permissions.md`.
