# Auth Specification

## Purpose

The `auth` unit is the single authentication gate for the TinyPOS back-office. It verifies administrator credentials against the `admin_users` table using a session-based guard, manages the session lifecycle (login, logout), and lets authenticated administrators update their own account details. Every other application route is protected by this unit; access control reduces to a single decision: authenticated administrator or not.

## Requirements

### Requirement: Display Login Form to Anonymous Users

The system SHALL render the admin login form when an unauthenticated request reaches `GET auth/login`. The form SHALL include fields for `username`, `password`, and a CSRF `_token`. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:23-30`)

#### Scenario: Anonymous user visits login page

- **GIVEN** the user has no active admin session
- **WHEN** the user sends `GET auth/login`
- **THEN** the system responds with `200 OK` and an HTML form containing `username`, `password`, and `_token` fields

### Requirement: Redirect Authenticated Users Away from Login Form

The system SHALL redirect an already-authenticated user who requests `GET auth/login` to the post-login path instead of displaying the form. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:25-27`)

#### Scenario: Authenticated user visits login page

- **GIVEN** the user has an active admin session
- **WHEN** the user sends `GET auth/login`
- **THEN** the system responds with `302 Found` to the post-login path and does not render the login form

### Requirement: Validate Credentials Before Login Attempt

The system SHALL require both `username` and `password` on `POST auth/login` and SHALL reject the request with a redirect back to the form if either field is absent, without attempting any credential lookup. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:44-51`)

#### Scenario: Login attempted without a password

- **GIVEN** the user is on the login form
- **WHEN** the user sends `POST auth/login` with a `username` but no `password`
- **THEN** the system responds with `302 Found` back to `auth/login`
- **AND** the response includes a field-level error indicating `password` is required
- **AND** no session is started

#### Scenario: Login attempted without a username

- **GIVEN** the user is on the login form
- **WHEN** the user sends `POST auth/login` with a `password` but no `username`
- **THEN** the system responds with `302 Found` back to `auth/login`
- **AND** the response includes a field-level error indicating `username` is required
- **AND** no session is started

### Requirement: Authenticate Valid Credentials and Start a Session

The system SHALL authenticate a `POST auth/login` request against the `admin` guard when both `username` and `password` are present and correct, start an authenticated session, and redirect to the intended destination or `/pos`. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:39-60, 172-179`)

#### Scenario: Successful login

- **GIVEN** an administrator account exists with a known `username` and `password`
- **AND** the user is not authenticated
- **WHEN** the user sends `POST auth/login` with the correct `username` and `password` and a valid `_token`
- **THEN** the system responds with `302 Found`
- **AND** the location resolves to `/pos` when no prior intended destination is stored
- **AND** a success toast notification is present in the redirected response

### Requirement: Regenerate Session on Successful Login

The system SHALL regenerate the session identifier upon a successful login. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:176`)

#### Scenario: Session ID changes after login

- **GIVEN** the user has a pre-login session identifier
- **WHEN** the user successfully authenticates via `POST auth/login`
- **THEN** the session identifier in the response differs from the pre-login identifier

### Requirement: Return Generic Error for Bad Credentials

The system SHALL respond to a failed login attempt — whether the username is unknown or the password is wrong — with the same generic error message, preserving the submitted `username` value in the form. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:57-59, 144-149`)

#### Scenario: Wrong password

- **GIVEN** an administrator account exists with a known `username`
- **WHEN** the user sends `POST auth/login` with the correct `username` and an incorrect `password`
- **THEN** the system responds with `302 Found` back to `auth/login`
- **AND** the error message reads "These credentials do not match our records." (or the `auth.failed` translation)
- **AND** the `username` field is pre-filled with the submitted value
- **AND** no session is started

#### Scenario: Unknown username

- **GIVEN** no administrator account exists for a given `username`
- **WHEN** the user sends `POST auth/login` with that `username` and any `password`
- **THEN** the system responds with `302 Found` back to `auth/login`
- **AND** the error message is identical to the wrong-password message
- **AND** no session is started

### Requirement: Protect Every Application Route from Anonymous Access

The system SHALL redirect any unauthenticated request to an application route to `auth/login`. (Implemented in `routes/web.php:23-27`, `config/admin.php:29`)

#### Scenario: Anonymous access to a protected route

- **GIVEN** the user has no active admin session
- **WHEN** the user sends `GET /pos`
- **THEN** the system responds with `302 Found` to `auth/login`

#### Scenario: Authenticated access to a protected route

- **GIVEN** the user has an active admin session
- **WHEN** the user sends `GET /pos`
- **THEN** the system does not redirect to `auth/login`

### Requirement: Log Out and Invalidate the Session

The system SHALL, on `GET auth/logout`, destroy the admin guard session, fully invalidate the underlying session, and redirect to the admin root. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:67-74`)

#### Scenario: Successful logout

- **GIVEN** the user has an active admin session
- **WHEN** the user sends `GET auth/logout`
- **THEN** the system responds with `302 Found` to `/`
- **AND** a subsequent request to any protected route redirects to `auth/login`

### Requirement: Display Self-Service Account Form

The system SHALL render the own-account edit form for the authenticated administrator on `GET auth/setting`, showing `username` (read-only), `name`, `avatar`, and `password`/`password_confirmation` fields. Anonymous access SHALL redirect to `auth/login`. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:81-100`)

#### Scenario: Authenticated user views own account form

- **GIVEN** the user has an active admin session
- **WHEN** the user sends `GET auth/setting`
- **THEN** the system responds with `200 OK` containing a form pre-filled with the current user's `username` (read-only), `name`, and `avatar`

#### Scenario: Anonymous user attempts to view account form

- **GIVEN** the user has no active admin session
- **WHEN** the user sends `GET auth/setting`
- **THEN** the system responds with `302 Found` to `auth/login`

### Requirement: Update Own Account Details

The system SHALL accept `PUT auth/setting` to update the authenticated administrator's `name`, `avatar`, and `password`. `name` is required. `password` requires a matching `password_confirmation`. On success, the system SHALL redirect back to `auth/setting` with a success notification. On validation failure, the system SHALL redirect back with an error bag. (Implemented in `vendor/salipropham/laravel55-admin/src/Controllers/AuthController.php:101-139`)

#### Scenario: Successful account update with a new password

- **GIVEN** the user is authenticated
- **WHEN** the user sends `PUT auth/setting` with a valid `name`, a new `password`, and a matching `password_confirmation`
- **THEN** the system persists the updated `name` and a bcrypt hash of the new `password`
- **AND** responds with `302 Found` back to `auth/setting` with a success toast

#### Scenario: Password unchanged — not re-hashed

- **GIVEN** the user is authenticated
- **WHEN** the user sends `PUT auth/setting` with the same value currently stored as the `password`
- **THEN** the system does not alter the stored password hash

#### Scenario: Mismatched password confirmation

- **GIVEN** the user is authenticated
- **WHEN** the user sends `PUT auth/setting` with a `password` and a `password_confirmation` that differ
- **THEN** the system responds with `302 Found` back to `auth/setting` with a validation error

#### Scenario: Missing required name

- **GIVEN** the user is authenticated
- **WHEN** the user sends `PUT auth/setting` without a `name` value
- **THEN** the system responds with `302 Found` back to `auth/setting` with a validation error indicating `name` is required

### Requirement: Enforce CSRF Protection on State-Changing Auth Requests

The system SHALL require a valid CSRF `_token` on `POST auth/login` and `PUT auth/setting`. Requests missing or supplying an invalid token SHALL be rejected. (Implemented in `app/Http/Middleware/VerifyCsrfToken.php` via the `web` middleware group)

#### Scenario: Login without CSRF token

- **GIVEN** the user is not authenticated
- **WHEN** the user sends `POST auth/login` with valid credentials but no `_token`
- **THEN** the system does not start a session and does not redirect to `/pos`

#### Scenario: Account update without CSRF token

- **GIVEN** the user is authenticated
- **WHEN** the user sends `PUT auth/setting` with valid fields but no `_token`
- **THEN** the system does not persist any changes
