# User Stories — Authentication

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

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

**Actor:** Store Administrator (the only system user).
**Owning unit:** `auth` (`Admin::registerAuthRoutes()` — login / logout / password-setting).

Authentication is the single gate into TinyPOS. Every other journey assumes an authenticated admin session; there is no customer-facing login. Access control across the whole application is authenticate-only — RBAC tables are seeded but never enforced at the route layer (🟢, ADR-0009, `permissions.md`).

---

### US-AUTH-1 — Sign in to the back-office

**As a** Store Administrator, **I want** to sign in with my username and password, **so that** I can operate the POS terminal and back-office.

- **Given** I am unauthenticated and open any protected URL
- **When** the `['web','admin']` middleware finds no `admin` guard session
- **Then** I am redirected to `GET auth/login` 🟢
- **Given** I am on the login page
- **When** I submit a valid **username** (not email) and password to `POST auth/login`
- **Then** a session is created, the session id is regenerated, and I land on the app — effectively `/pos`, because root `/` 301-redirects to the terminal 🟢
- **Given** I submit wrong credentials
- **When** authentication fails
- **Then** I am returned to the login form with a **generic** failure message that does not reveal whether the username exists (no account enumeration) 🟢

Notes / gaps:
- Identity is **username**, not email. 🟢
- Passwords are bcrypt-hashed and only re-hashed on change. 🟢
- There is **no brute-force protection, rate limiting, or failed-login logging**. 🔴 (flagged in `auth`)
- Whether login errors should be an HTML redirect vs a JSON 422 is an unresolved architecture decision. 🔴

Traces to: `auth/` (`routes/web.php:20`, Encore\Admin `AuthController::postLogin`)

---

### US-AUTH-2 — Sign out

**As a** Store Administrator, **I want** to sign out, **so that** the terminal cannot be used by someone else after I leave.

- **Given** I have an active session
- **When** I request `GET auth/logout`
- **Then** my session is invalidated and I am returned to the login page 🟢

Traces to: `auth/` (Encore\Admin `AuthController::getLogout`)

---

### US-AUTH-3 — Change my own password / profile

**As a** Store Administrator, **I want** to edit my own profile and password, **so that** I can keep my credentials current.

- **Given** I am authenticated
- **When** I open `GET auth/setting` and submit changes via `PUT auth/setting`
- **Then** my profile is updated; the password is re-hashed **only when it is actually changed** 🟢

Notes / gaps:
- The framework's full user-management auth stack (`App\User`) is intentionally **dead** — `App\User` is missing and the app authenticates via `admin_users`. Do **not** reimplement the framework user model. 🟢
- The default seeded credential is `admin` / `admin` — flagged for change before production. 🔴

Traces to: `auth/` (Encore\Admin `AuthController::getSetting` / `putSetting`)
