# Auth — Contracts

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

**Confidence scale:** 🟢 CONFIRMED · 🟡 INFERRED · 🔴 GAP

External HTTP contracts exposed by the `auth` unit. All paths sit at the site root (admin prefix is empty). The controller is `Encore\Admin\Controllers\AuthController`. Responses are server-rendered HTML / redirects (this is a session-cookie web app, not a JSON API). 🟢 (`vendor/…/Admin.php:252-256`, `vendor/…/AuthController.php`)

---

## GET `auth/login` — Show login page 🟢

- **Auth:** none (public).
- **Request:** no body; session cookie optional.
- **Behavior:** if the `admin` guard is already authenticated → `302` to `redirectPath()` (root → `/pos`); otherwise render the `admin::login` view.
- **Responses:**
  - `200 OK` — HTML login form with `username`, `password`, and a CSRF `_token` field.
  - `302 Found` — `Location: /` (then 301 → `/pos`) when already authenticated.
- Source: `AuthController::getLogin` (`:23-30`).

## POST `auth/login` — Authenticate 🟢

- **Auth:** none (public); CSRF-protected.
- **Request (form-encoded):**

  | Field | Type | Required | Notes |
  |-------|------|----------|-------|
  | `username` | string | ✅ | login identity (not email) |
  | `password` | string | ✅ | plaintext over TLS; compared to bcrypt hash |
  | `_token` | string | ✅ | Laravel CSRF token |

- **Validation:** `username` required, `password` required. On validation failure → `302` back to `auth/login` with an error bag and old input (`username` retained). 🟢 (`:44-51`)
- **Responses:**
  - `302 Found` → `redirect()->intended(redirectPath())` on success (default target root → 301 → `/pos`); session is regenerated and a `login_successful` toast is flashed. 🟢 (`:53-54,172-179`)
  - `302 Found` → back to `auth/login` with `errors[username] = "These credentials do not match our records."` (or `trans('auth.failed')`) on bad credentials. Identical message for unknown user and wrong password (no enumeration). 🟢 (`:57-59,144-149`)
- **Notes:** no rate-limiting / lockout; failed attempts are not logged. Decided 2026-09-25 (`questions.md#question-15`): not needed in the reimplementation. 🟢
- Source: `AuthController::postLogin` (`:39-60`).

## GET `auth/logout` — End session 🟢

- **Auth:** effectively requires a session (no-op if anonymous).
- **Request:** no body.
- **Behavior:** `guard()->logout()` → `session()->invalidate()` → `302` to admin root (`config('admin.route.prefix')`, `''`).
- **Responses:** `302 Found`, `Location: /`.
- **Note:** logout is a `GET` (not `POST`) in the legacy — CSRF-unprotected by design of the package. 🟡
- Source: `AuthController::getLogout` (`:67-74`).

## GET `auth/setting` — Own-account form 🟢

- **Auth:** required (`admin` middleware); anonymous → `302` to `auth/login`.
- **Request:** no body.
- **Responses:** `200 OK` — HTML form bound to the current user (`username` display-only; `name`, `avatar`, `password`, `password_confirmation`).
- Source: `AuthController::getSetting` / `settingForm` (`:81-139`).

## PUT `auth/setting` — Update own account 🟢

- **Auth:** required; CSRF-protected.
- **Request (form-encoded, `_method=PUT`):**

  | Field | Type | Required | Notes |
  |-------|------|----------|-------|
  | `name` | string | ✅ | display name |
  | `avatar` | file (image) | ❌ | optional profile image |
  | `password` | string | ✅ (`confirmed`) | re-hashed with bcrypt only if changed |
  | `password_confirmation` | string | ✅ | must match `password`; ignored on save |
  | `_token` | string | ✅ | CSRF token |

- **Validation:** `name` required; `password` `confirmed|required`; `password_confirmation` required. 🟢 (`:115-118`)
- **Behavior:** `saving` hook bcrypts `password` when it differs from the stored hash; `password_confirmation` is dropped (`$form->ignore`). 🟢 (`:125-131`)
- **Responses:**
  - `302 Found` → back to `auth/setting` with an `update_succeeded` toast on success. 🟢 (`:133-137`)
  - `302 Found` → back with validation error bag on failure. 🟡
- Source: `AuthController::putSetting` / `settingForm` (`:101-139`).

---

## Cross-cutting contract notes

- **Transport:** HTTPS expected in production (`config/admin.php` `secure => env('ADMIN_SECURE', true)`). 🟢
- **Session:** cookie-based (`web` middleware group); regenerated on login, invalidated on logout. 🟢
- **CSRF:** enforced on `POST`/`PUT` by `VerifyCsrfToken` (web group); `GET auth/logout` is exempt by nature. 🟢/🟡
- **Content type:** all responses are `text/html` or `3xx` redirects — there is no JSON contract. A modernized API would need to define its own token/response scheme (out of scope for the legacy). 🔴
- **Out of scope:** `auth/users`, `auth/roles`, `auth/permissions`, `auth/menu`, `auth/logs` resources (registered by the same `registerAuthRoutes()` call) belong to the unenforced RBAC subsystem, not this unit. 🟢 (`vendor/…/Admin.php:240-251`, `_reversa_sdd/permissions.md`)
