diff --git a/README.md b/README.md index cc8861b..dccff12 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ MiauInv is a secure, light-weight inventory, stock, and project allocation track * [Technical Specifications](#technical-specifications) * [Architecture Overview](#architecture-overview) +* [Detailed Documentation](#detailed-documentation) * [Configuration](#configuration) * [Configuration File (config.yaml)](#configuration-file-configyaml) * [Environment Variables](#environment-variables) @@ -43,6 +44,15 @@ MiauInv splits responsibility cleanly across modularized architecture packages: --- +--- + +## Detailed Documentation + +For deep dives into specific subsystems, database layouts, and security mechanisms, please refer to the dedicated documentation files: + +* **[Database Schema & Integrity](docs/DATABASE.md):** Comprehensive breakdown of the SQLite table structures, fields, and foreign key relations. +* **[Authentication Architecture](docs/AUTHENTICATION.md):** Detailed explanation of the dual-token rotation flow, JWT lifecycle, and frontend loop protection. + ## Configuration The system uses a combination of a structural JSON configuration file and environment variables for system runtime flags. diff --git a/docs/AUTHENTICATION.md b/docs/AUTHENTICATION.md new file mode 100644 index 0000000..aad479c --- /dev/null +++ b/docs/AUTHENTICATION.md @@ -0,0 +1,51 @@ +# Authentication Architecture + +MiauInv implements a stateless JSON Web Token (JWT) architecture combined with a persistent database-backed Refresh Token mechanism to provide high security alongside seamless session retention. + +## Token Lifetime and Properties + +| Token Type | Transport Vector | Storage Location | Lifetime | Purpose | +| --- | --- | --- | --- | --- | +| **Access Token** | HTTP-Only Cookie & Auth Header | Memory / Browser Cookies | 15 Minutes | Signed payload validating current session identity for immediate API interaction. | +| **Refresh Token** | Secure Cookie & JSON Payload | LocalStorage / Secure Cookies | 7 Days | Long-lived high-entropy string used to request a new token pair when the Access Token expires. | + +--- + +## Token Rotation and Flow + +The application coordinates token validation through cooperative interactions between Go authentication middlewares and the frontend runtime environment. + +### 1. Normal Authenticated Requests +During standard interaction loops, the Go server intercepts requests via auth middleware. It checks the incoming context for validity in the following order: +1. `Authorization: Bearer ` request header. +2. `access_token` cookie values. + +If a valid, unexpired Access Token is recovered, the middleware parses the claims (ID, username, role) and injects them into the request context before execution routes fire. + +### 2. Token Refresh Flow +When an Access Token expires mid-session, the following workflow occurs automatically: +1. The backend rejects an API call or routing intent with an HTTP state indicating token expiration. +2. The frontend execution scope identifies the expiration status and reads the `refresh_token` from storage assets. +3. The client submits a POST request containing the token payload to `/api/refresh`. +4. The backend verifies the signature, looks up the hash inside the `refresh_tokens` table, and verifies that `revoked == 0` and `expires_at > now`. +5. If the validation succeeds, a brand-new Access Token and a rotated Refresh Token pair are generated, saved to secure cookies/storage, and the user session continues without explicit re-authentication. + +--- + +## Security Mitigations + +### Loop Protection +To prevent broken, expired, or malformed credentials from triggering infinite network refresh loops (which degrade browser performance and strain backend lookup performance), the frontend utilizes an explicit safety lock. + + +``` + +Token Expired -> Check 'is_refreshing' flag -> True -> Clear Auth & Force Login +-> False -> Set flag 'true' -> Send Request + +``` + +Before issuing an evaluation request to `/api/refresh`, the application checks a temporary session variable (`is_refreshing` within `sessionStorage`). If the flag is already set to `true`, the loop protection triggers a hard clearance routine via `clearAllAuth()`, drops all token storage records, and routes the user back to the primary login view safely. + +### Database Revocation +Refresh sessions can be killed immediately from the server side. When a user requests `/api/logout`, the backend switches the corresponding row state within the `refresh_tokens` database container to `revoked = 1`. Any subsequent rotation requests relying on that token family are automatically dropped, protecting against stolen credential replay attacks. \ No newline at end of file diff --git a/docs/DATABASE.md b/docs/DATABASE.md new file mode 100644 index 0000000..fc764c0 --- /dev/null +++ b/docs/DATABASE.md @@ -0,0 +1,97 @@ +# Database Documentation + +MiauInv utilizes an embedded SQLite database instance for persistent data storage. Foreign key constraints are strictly enforced at the database level. + +## Configuration +To ensure data integrity, every database connection initialization explicitly executes the following command before handling queries: +```sql +PRAGMA foreign_keys = ON; +``` + +--- + +## Schema Architecture + +### Entity-Relationship Summary + +The database consists of primary entity tables (`users`, `items`, `locations`, `projects`) and relational junction tables (`stock`, `project_items`, `refresh_tokens`) designed to track stock distribution and access sessions. + +``` +[users] <--- (1:N) ---> [refresh_tokens] +[items] <--- (1:N) ---> [stock] <--- (N:1) ---> [locations] +[items] <--- (1:N) ---> [project_items] <--- (N:1) ---> [projects] +``` + +--- + +## Table Definitions + +### 1. users + +Stores user credentials and operational roles within the system. + +* **id (TEXT, PK):** Unique UUID +* **username (TEXT, Unique):** Unique account identifier. +* **password (TEXT):** Hashed user password. +* **role (TEXT):** Access control flag (e.g., admin, user). + +### 2. refresh_tokens + +Tracks valid extended sessions linked to specific user accounts. + +* **id (TEXT, PK):** Unique identifier. +* **user_id (TEXT, FK):** References `users(id)`. +* **token_hash (TEXT):** Cryptographic hash of the active refresh token. +* **expires_at (INTEGER):** Unix timestamp indicating token expiration. +* **created_at (INTEGER):** Unix timestamp indicating session creation. +* **revoked (INTEGER):** Boolean flag (0 or 1) indicating if the session was manually invalidated. +* **device_info (TEXT, Optional):** Client metadata for auditing. + +### 3. items + +Represents individual tracked assets. + +* **id (INTEGER, PK, Autoincrement):** Primary key. +* **name (TEXT):** Asset designation. +* **category (TEXT, Optional):** Grouping classification. +* **description (TEXT, Optional):** Detailed asset context. +* **total_quantity (INTEGER):** Absolute global stock baseline counter. + +### 4. locations + +Defines logical or physical facilities. + +* **id (INTEGER, PK, Autoincrement):** Primary key. +* **name (TEXT, Unique):** Unique facility naming constraint. + +### 5. projects + +Defines distinct tasks or allocation targets. + +* **id (INTEGER, PK, Autoincrement):** Primary key. +* **name (TEXT, Unique):** Unique operational tracking name. +* **description (TEXT, Optional):** Scope description. + +### 6. stock + +Junction table mapping physical asset distributions across facilities. + +* **id (INTEGER, PK, Autoincrement):** Primary key. +* **item_id (INTEGER, FK):** References `items(id)`. +* **location_id (INTEGER, FK):** References `locations(id)`. +* **quantity (INTEGER):** Specific quantity present at this location node. + +### 7. project_items + +Junction table tracking asset assignments dedicated to specific ongoing project environments. + +* **id (INTEGER, PK, Autoincrement):** Primary key. +* **item_id (INTEGER, FK):** References `items(id)`. +* **project_id (INTEGER, FK):** References `projects(id)`. +* **quantity (INTEGER):** Quantity allocated to this project context. +--- + +## Data Integrity Constraints + +* **Foreign Keys:** Because standard `ON DELETE` cascades are not defined explicitly in the schema rules, SQLite blocks parent deletion actions if dependent rows exist in `stock` or `project_items`. You must clear out stock allocations and project associations manually before deleting an item, location, or project. +* **Uniqueness:** String uniqueness constraints protect against duplicate namespace registration on `users(username)`, `locations(name)`, and `projects(name)`. \ No newline at end of file