feat: added activity log
This commit is contained in:
@@ -12,6 +12,7 @@ JWTs are signed with a symmetric secret from the `JWT_SECRET` environment variab
|
||||
| Middleware | `auth/middleware.go` | Extracts access tokens from bearer headers or cookies and injects claims into the request context. |
|
||||
| Password helpers | `auth/password.go` | bcrypt hashing and verification. |
|
||||
| Login/account handlers | `handlers/account.go` | Register, login, 2FA, refresh, logout, account settings, and user metadata. |
|
||||
| Activity handlers | `handlers/activity.go` | Activity log endpoint, activity metadata recording, and audit middleware. |
|
||||
| Passkey handlers | `handlers/passkeys.go` | WebAuthn/passkey registration, login, removal, and disable flows. |
|
||||
| Persistent session storage | `storage/storage.go`, `storage/passkeys.go` | Refresh tokens, 2FA state, TOTP secret, recovery-code hashes, passkey credentials, and WebAuthn challenge state. |
|
||||
| Frontend auth logic | `frontend/assets/js/auth.js`, `frontend/assets/js/login.js`, `frontend/assets/js/api.js` | Login UI, passkey login, token refresh, account settings, 2FA UI interactions, and passkey UI interactions. |
|
||||
@@ -263,3 +264,22 @@ The current implementation is usable for private/self-hosted deployments, but th
|
||||
- Add optional session/device management UI.
|
||||
- Consider encrypting TOTP secrets and passkey credential data at rest if the deployment threat model includes database disclosure.
|
||||
- Expand tests for all authentication and account settings handlers.
|
||||
|
||||
## Activity Log
|
||||
|
||||
MiauInv records account, authentication, security, and inventory activity in the `activity_logs` table. The log is designed for user-visible traceability and lightweight security review.
|
||||
|
||||
Recorded examples include:
|
||||
|
||||
- password login success and failure,
|
||||
- 2FA login success and failure,
|
||||
- refresh-token rotation,
|
||||
- logout,
|
||||
- username and password changes,
|
||||
- TOTP setup, enable, disable, and recovery-code regeneration,
|
||||
- passkey registration, login, removal, and disable,
|
||||
- inventory, location, project, stock, and allocation mutations.
|
||||
|
||||
The log does not store request bodies or secret values. Passwords, TOTP codes, recovery codes, refresh tokens, and WebAuthn payloads are excluded.
|
||||
|
||||
`GET /api/activity` returns the current user's recent activity with bounded `limit` and `offset` pagination. Admin users may request all activity with `?all=true`.
|
||||
|
||||
@@ -6,7 +6,7 @@ MiauInv uses SQLite for persistent storage. The schema is initialized in `storag
|
||||
PRAGMA foreign_keys = ON;
|
||||
```
|
||||
|
||||
The database stores users, refresh tokens, 2FA recovery codes, passkey credentials, passkey challenge state, inventory items, locations, projects, stock mappings, and project allocations.
|
||||
The database stores users, refresh tokens, 2FA recovery codes, passkey credentials, passkey challenge state, activity log entries, inventory items, locations, projects, stock mappings, and project allocations.
|
||||
|
||||
## Entity Overview
|
||||
|
||||
@@ -14,6 +14,7 @@ The database stores users, refresh tokens, 2FA recovery codes, passkey credentia
|
||||
[users] 1 ──── N [refresh_tokens]
|
||||
[users] 1 ──── N [two_factor_recovery_codes]
|
||||
[users] 1 ──── N [passkey_credentials]
|
||||
[users] 1 ──── N [activity_logs]
|
||||
|
||||
[passkey_challenges] stores short-lived WebAuthn ceremony state
|
||||
|
||||
@@ -96,6 +97,30 @@ Stores short-lived server-side WebAuthn session data for registration and login
|
||||
|
||||
Challenge rows are consumed once during the finish step and expired rows are cleaned up opportunistically.
|
||||
|
||||
|
||||
### `activity_logs`
|
||||
|
||||
Stores account, authentication, security, and inventory activity metadata. Request bodies, passwords, TOTP codes, recovery codes, passkey payloads, and token values are not stored.
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `id` | `TEXT` | Primary key | Activity row UUID. |
|
||||
| `user_id` | `TEXT` | Not null, default `''` | User associated with the activity. Empty for unknown-user failed login attempts. |
|
||||
| `username` | `TEXT` | Not null, default `''` | Username known at the time of the event. |
|
||||
| `action` | `TEXT` | Not null | Normalized action name, for example `auth.login.succeeded` or `inventory.update`. |
|
||||
| `entity_type` | `TEXT` | Not null, default `''` | Logical entity, for example `auth`, `security`, `item`, `location`, or `project`. |
|
||||
| `entity_id` | `TEXT` | Not null, default `''` | Optional target ID when available. |
|
||||
| `details` | `TEXT` | Not null, default `''` | Short sanitized status summary. |
|
||||
| `method` | `TEXT` | Not null, default `''` | HTTP method. |
|
||||
| `path` | `TEXT` | Not null, default `''` | Request path without query parameters. |
|
||||
| `status_code` | `INTEGER` | Not null, default `0` | Final HTTP status code. |
|
||||
| `success` | `INTEGER` | Not null, default `0` | Boolean success flag derived from the status code. |
|
||||
| `ip_address` | `TEXT` | Not null, default `''` | Client IP address, considering common reverse-proxy headers. |
|
||||
| `user_agent` | `TEXT` | Not null, default `''` | User-Agent metadata. |
|
||||
| `created_at` | `INTEGER` | Not null | Unix timestamp for the event. |
|
||||
|
||||
Indexes are created for per-user timeline lookups, global timestamp ordering, and action filtering. Non-admin users can only read their own activity through `/api/activity`. Admin users can request all activity with `?all=true`.
|
||||
|
||||
### `items`
|
||||
|
||||
Stores tracked inventory items.
|
||||
|
||||
91
docs/ENDPOINTS.md
Normal file
91
docs/ENDPOINTS.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# API Endpoints
|
||||
|
||||
This document lists the public page routes and JSON API endpoints exposed by MiauInv. API endpoints that modify account or inventory state require authentication unless explicitly marked as public.
|
||||
|
||||
## Page Routes
|
||||
|
||||
| Route | Authentication | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `/` | No | Landing page. |
|
||||
| `/login` | No | Login page with password and passkey login. |
|
||||
| `/register` | Optional | Registration page when registration is enabled. |
|
||||
| `/dashboard` | Yes | Dashboard overview. |
|
||||
| `/inventory` | Yes | Inventory item management. |
|
||||
| `/items` | Yes | Item list view. |
|
||||
| `/locations` | Yes | Location management. |
|
||||
| `/projects` | Yes | Project allocation management. |
|
||||
| `/profile/settings` | Yes | Account, 2FA, and passkey settings. |
|
||||
| `/profile/activity` | Yes | User activity log. |
|
||||
|
||||
## Authentication and Account API
|
||||
|
||||
| Endpoint | Method | Authentication | Purpose |
|
||||
| --- | --- | --- | --- |
|
||||
| `/api/register` | `POST` | No | Create a user when registration is enabled. |
|
||||
| `/api/login` | `POST` | No | Password login. Returns a 2FA challenge if required. |
|
||||
| `/api/login/2fa` | `POST` | No | Complete TOTP or recovery-code login. |
|
||||
| `/api/passkeys/login/options` | `POST` | No | Start discoverable passkey login. |
|
||||
| `/api/passkeys/login/finish` | `POST` | No | Complete passkey login. |
|
||||
| `/api/refresh` | `POST` | No | Rotate a refresh token and issue a new session. |
|
||||
| `/api/logout` | `POST` | Yes | Revoke refresh sessions and clear auth cookies. |
|
||||
| `/api/userinfo` | `GET` | Yes | Return current user metadata and security status. |
|
||||
| `/api/profile` | `GET` | Yes | Alias for current user metadata. |
|
||||
| `/api/account/username` | `POST` | Yes | Change username with password confirmation. |
|
||||
| `/api/account/password` | `POST` | Yes | Change password and refresh the current session. |
|
||||
|
||||
## Two-Factor Authentication API
|
||||
|
||||
| Endpoint | Method | Authentication | Purpose |
|
||||
| --- | --- | --- | --- |
|
||||
| `/api/2fa/setup` | `POST` | Yes | Create a short-lived setup challenge, QR code, and manual setup secret. |
|
||||
| `/api/2fa/enable` | `POST` | Yes | Confirm the setup challenge, enable 2FA, and generate recovery codes. |
|
||||
| `/api/2fa/disable` | `POST` | Yes | Disable 2FA with password and TOTP confirmation. |
|
||||
| `/api/2fa/recovery-codes/regenerate` | `POST` | Yes | Replace recovery codes with password and TOTP confirmation. |
|
||||
|
||||
## Passkey Management API
|
||||
|
||||
| Endpoint | Method | Authentication | Purpose |
|
||||
| --- | --- | --- | --- |
|
||||
| `/api/passkeys` | `GET` | Yes | List registered passkeys. |
|
||||
| `/api/passkeys` | `DELETE` | Yes | Remove a passkey with password confirmation. |
|
||||
| `/api/passkeys/register/options` | `POST` | Yes | Start passkey registration. |
|
||||
| `/api/passkeys/register/finish` | `POST` | Yes | Finish passkey registration and store the credential. |
|
||||
| `/api/passkeys/disable` | `POST` | Yes | Remove all passkeys with password confirmation. |
|
||||
|
||||
## Activity API
|
||||
|
||||
| Endpoint | Method | Authentication | Purpose |
|
||||
| --- | --- | --- | --- |
|
||||
| `/api/activity` | `GET` | Yes | Return recent activity entries for the current user. Admin users may request `?all=true`. |
|
||||
|
||||
Query parameters:
|
||||
|
||||
| Parameter | Default | Max | Purpose |
|
||||
| --- | --- | --- | --- |
|
||||
| `limit` | `50` | `100` | Number of entries to return. |
|
||||
| `offset` | `0` | `100000` | Offset for pagination. |
|
||||
| `all` | `false` | n/a | Admin-only flag for reading all users' activity. |
|
||||
|
||||
## Inventory API
|
||||
|
||||
| Endpoint | Method | Authentication | Purpose |
|
||||
| --- | --- | --- | --- |
|
||||
| `/api/item` | `GET` | Yes | List items or read an item by `id`. |
|
||||
| `/api/item` | `POST` | Yes | Create an item. |
|
||||
| `/api/item` | `PUT` | Yes | Update an item by `id`. |
|
||||
| `/api/item` | `DELETE` | Yes | Delete an item by `id`. |
|
||||
| `/api/location` | `GET` | Yes | List locations, read a location by `id`, or read location contents with `content=true`. |
|
||||
| `/api/location` | `POST` | Yes | Create a location. |
|
||||
| `/api/location` | `PUT` | Yes | Update a location by `id`. |
|
||||
| `/api/location` | `DELETE` | Yes | Delete a location by `id`. |
|
||||
| `/api/project` | `GET` | Yes | List projects, read a project by `id`, or read project allocation details with `details=true`. |
|
||||
| `/api/project` | `POST` | Yes | Create a project. |
|
||||
| `/api/project` | `PUT` | Yes | Update a project by `id`. |
|
||||
| `/api/project` | `DELETE` | Yes | Delete a project by `id`. |
|
||||
| `/api/stock` | `GET` | Yes | List stock rows, optionally filtered by `item_id`. |
|
||||
| `/api/stock` | `POST` | Yes | Add stock to a location. |
|
||||
| `/api/stock` | `DELETE` | Yes | Delete a stock row by `id`. |
|
||||
| `/api/association` | `GET` | Yes | List project-item allocations, optionally filtered by `project_id`. |
|
||||
| `/api/association` | `POST` | Yes | Allocate item quantity to a project. |
|
||||
| `/api/association` | `PUT` | Yes | Update an allocation by `id`. |
|
||||
| `/api/association` | `DELETE` | Yes | Delete an allocation by `id`. |
|
||||
@@ -1,4 +1,4 @@
|
||||
# Security Notes
|
||||
# Security
|
||||
|
||||
This document summarizes the current security-relevant behavior of MiauInv. It is intended as implementation documentation, not as a guarantee that the application is production-ready for untrusted public deployments.
|
||||
|
||||
@@ -80,9 +80,17 @@ When passkeys are added, removed, or disabled, existing refresh sessions are rev
|
||||
|
||||
Passkey ceremonies require HTTPS except for localhost. Reverse proxy deployments must preserve the correct public `Host`, `X-Forwarded-Host`, and `X-Forwarded-Proto` information so that the relying party origin and ID match the browser-visible origin.
|
||||
|
||||
## Activity Logging
|
||||
|
||||
MiauInv stores an authenticated activity log for security-relevant and state-changing actions, including login attempts, refresh-token rotation, account changes, 2FA changes, passkey management, logout, and inventory mutations.
|
||||
|
||||
The activity log intentionally stores metadata only: action name, entity type, optional target ID, HTTP method, path, status, success flag, IP address, user agent, and timestamp. Request bodies are not logged, so passwords, TOTP codes, recovery codes, refresh tokens, and WebAuthn payloads are not persisted in the activity table.
|
||||
|
||||
Users can read their own entries from `/profile/activity` and `/api/activity`. Admin users may request all users' activity with `?all=true`. The API enforces authentication, bounds pagination limits, and is protected by rate limiting.
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
Basic in-memory rate limiting protects login, passkey ceremonies, 2FA, refresh, registration, and sensitive account endpoints.
|
||||
Basic in-memory rate limiting protects login, passkey ceremonies, 2FA, refresh, registration, activity, and sensitive account endpoints.
|
||||
|
||||
This is suitable for a single-instance private deployment. It is not sufficient for multi-instance deployments because limiter state is process-local. A public or multi-instance deployment should use persistent or distributed rate limiting at the application, reverse proxy, or infrastructure layer.
|
||||
|
||||
@@ -93,5 +101,4 @@ This is suitable for a single-instance private deployment. It is not sufficient
|
||||
- Passkey credential metadata and public-key data are stored in the database after registration.
|
||||
- TOTP secrets are not encrypted at rest.
|
||||
- There is no dedicated session/device management UI yet.
|
||||
- There is no audit log for account security changes yet.
|
||||
- The current rate limiter is process-local and memory-only.
|
||||
|
||||
Reference in New Issue
Block a user