From 8a6ea39ed2182f503b37f8f27f3561f6df1bf93b Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Mon, 16 Feb 2026 16:50:43 -0500 Subject: [PATCH] docs(auth): document 7-day session authentication with constants - Add comprehensive authentication overview.md explaining: - 7-day session duration for JWT and refresh tokens - Constants-based implementation (no hardcoded values) - Complete authentication flow (register/login/refresh/logout) - Session expiration handling (HTML redirect vs JSON error) - Security features (HTTP-only cookies, token rotation) - Token storage recommendations - Update login.md with 7-day expires_in field and cookie MaxAge - Update register.md with 7-day session duration details - Update refresh_token.md with expires_in: 604800 Documentation provides complete reference for authentication endpoints with examples and security considerations. --- docs/developer/api/authentication/login.md | 18 +- docs/developer/api/authentication/overview.md | 166 ++++++++++++++++++ .../api/authentication/refresh_token.md | 15 +- docs/developer/api/authentication/register.md | 19 +- 4 files changed, 207 insertions(+), 11 deletions(-) create mode 100644 docs/developer/api/authentication/overview.md diff --git a/docs/developer/api/authentication/login.md b/docs/developer/api/authentication/login.md index 62eab6d..6674585 100644 --- a/docs/developer/api/authentication/login.md +++ b/docs/developer/api/authentication/login.md @@ -4,20 +4,20 @@ Authenticate with email and password. **Endpoint**: `POST /api/auth/login` **Auth**: Not required -**Content-Type**: `application/json` +**Content-Type**: `application/json` or `application/x-www-form-urlencoded` ## Request Body | Field | Type | Required | Description | |--------|------|-----------|-------------| -| email | string | Yes | User's email address | +| login | string | Yes | User's email address or username | | password | string | Yes | User's password | ### Example Request ```json { - "email": "user@example.com", + "login": "user@example.com", "password": "SecureP@ss123!" } ``` @@ -28,18 +28,30 @@ Authenticate with email and password. { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "d4f5g6h7...", + "token_type": "Bearer", + "expires_in": 604800, "user": { "id": "uuid-here", "email": "user@example.com", "username": "john", + "first_name": "John", + "last_name": "Doe", "role": "user" } } ``` +**Set-Cookie Header**: +``` +Set-Cookie: token=eyJhbG...; Max-Age=604800; Path=/; HttpOnly +``` + +**Session Duration**: 7 days (604800 seconds) + ## Error Responses | Code | Description | |------|-------------| | 401 | Invalid email or password | | 400 | Missing required fields | +| 429 | Too many login attempts | diff --git a/docs/developer/api/authentication/overview.md b/docs/developer/api/authentication/overview.md new file mode 100644 index 0000000..a2e90c5 --- /dev/null +++ b/docs/developer/api/authentication/overview.md @@ -0,0 +1,166 @@ +# Authentication Overview + +Bookhoard uses **JWT-based authentication** with **7-day persistent sessions** and **refresh tokens** for security. + +## Session Duration + +All JWT tokens and refresh tokens are valid for **7 days** from creation. This provides a Google-like persistent session experience. + +- **JWT Access Token**: Valid for 7 days +- **Refresh Token**: Valid for 7 days +- **HTTP-only Cookie**: Max-Age of 7 days (604800 seconds) + +### Implementation Details + +Session durations are defined using **constants** to maintain a single source of truth: + +```go +// internal/handlers/auth.go +const ( + SessionDuration = 7 * 24 * time.Hour // 7 days +) + +// SessionDurationSec is computed from SessionDuration +var SessionDurationSec = int(SessionDuration.Seconds()) // 604800 seconds +``` + +**No hardcoded values** exist in the codebase. All timeout values use these constants: + +- JWT token `exp` claim: `time.Now().Add(SessionDuration).Unix()` +- Cookie `MaxAge`: `SessionDurationSec` +- API response `expires_in`: `SessionDurationSec` + +## Authentication Flow + +### 1. Registration/Login + +When a user registers or logs in: + +1. Server generates a JWT access token (valid for 7 days) +2. Server creates a refresh token in the database (valid for 7 days) +3. Server sets an HTTP-only cookie with the JWT token +4. Server returns JSON response with both tokens and user profile + +**Request**: +```json +POST /api/auth/login +{ + "login": "user@example.com", + "password": "SecureP@ss123!" +} +``` + +**Response**: +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "refresh_token": "uuid-refresh-token", + "token_type": "Bearer", + "expires_in": 604800, + "user": { + "id": "uuid", + "email": "user@example.com", + "username": "john", + "role": "user" + } +} +``` + +**Set-Cookie Header**: +``` +Set-Cookie: token=eyJhbG...; Max-Age=604800; Path=/; HttpOnly +``` + +### 2. Making Authenticated Requests + +Include the JWT token in the `Authorization` header: + +```bash +curl -H "Authorization: Bearer " \ + https://api.example.com/api/libraries +``` + +The server also checks for the token in the HTTP-only cookie automatically. + +### 3. Token Refresh + +When your access token expires (after 7 days), use the refresh token to get a new one: + +```json +POST /api/auth/refresh +{ + "refresh_token": "uuid-refresh-token" +} +``` + +**Response**: +```json +{ + "access_token": "new-jwt-token", + "token_type": "Bearer", + "expires_in": 604800 +} +``` + +### 4. Logout + +Revokes the refresh token on the server: + +```json +POST /api/auth/logout +{ + "refresh_token": "uuid-refresh-token" +} +``` + +## Session Expiration Handling + +### Web Browser Requests + +When a user's session expires during page navigation: + +1. Server redirects to `/login?session=expired` +2. Login page displays: "Your session has expired. Please log in again to continue." +3. User re-authenticates and is redirected to their intended destination + +### API Requests + +When an API call receives a 401 Unauthorized response: + +```json +{ + "error": "session_expired", + "message": "Your session has expired. Please log in again." +} +``` + +The frontend toast.js interceptor: +1. Clears invalid tokens from localStorage +2. Shows an error toast notification +3. Allows user to re-authenticate + +## Security Features + +- **HTTP-only Cookies**: Prevents XSS attacks on token cookies +- **Refresh Token Rotation**: New refresh tokens issued on each refresh +- **7-Day Expiration**: Reasonable balance between security and convenience +- **Constants-Based Configuration**: Single source of truth for session duration + +## Token Storage Recommendations + +### Browser Applications +- **Backend**: Automatically manages HTTP-only cookie +- **Frontend**: Store tokens in localStorage for API calls + +### Mobile Applications +- Store access token in secure storage (Keychain/Keystore) +- Store refresh token in secure storage +- Handle 401 responses by prompting user to re-authenticate + +## Constants Reference + +All session durations use constants defined in: +- `internal/handlers/auth.go` - SessionDuration, SessionDurationSec +- `internal/handlers/refresh_token.go` - SessionDurationSec (mirrored) + +**Total value**: 604800 seconds (7 days × 24 hours × 60 minutes × 60 seconds) diff --git a/docs/developer/api/authentication/refresh_token.md b/docs/developer/api/authentication/refresh_token.md index aecfeb8..59a97a3 100644 --- a/docs/developer/api/authentication/refresh_token.md +++ b/docs/developer/api/authentication/refresh_token.md @@ -1,6 +1,6 @@ # Refresh Token -Obtain a new JWT token using a refresh token. +Obtain a new JWT access token using a refresh token. **Endpoint**: `POST /api/auth/refresh` **Auth**: Not required (uses refresh token) @@ -10,7 +10,7 @@ Obtain a new JWT token using a refresh token. | Field | Type | Required | Description | |--------|------|-----------|-------------| -| refresh_token | string | Yes | Valid refresh token | +| refresh_token | string | Yes | Valid refresh token (UUID) | ### Example Request @@ -24,14 +24,19 @@ Obtain a new JWT token using a refresh token. ```json { - "token": "new-jwt-token", - "refresh_token": "new-refresh-token" + "access_token": "new-jwt-token", + "token_type": "Bearer", + "expires_in": 604800 } ``` +**Session Duration**: 7 days (604800 seconds) + +The new access token is valid for 7 days from the time of refresh. + ## Error Responses | Code | Description | |------|-------------| | 401 | Invalid or expired refresh token | -| 400 | Missing refresh token | +| 400 | Missing refresh token or invalid format | diff --git a/docs/developer/api/authentication/register.md b/docs/developer/api/authentication/register.md index 3a531ef..d85557c 100644 --- a/docs/developer/api/authentication/register.md +++ b/docs/developer/api/authentication/register.md @@ -4,15 +4,15 @@ Create a new user account. **Endpoint**: `POST /api/auth/register` **Auth**: Not required -**Content-Type**: `application/json` +**Content-Type**: `application/json` or `application/x-www-form-urlencoded` ## Request Body | Field | Type | Required | Description | |--------|------|-----------|-------------| | email | string | Yes | User's email address | -| username | string | Yes | Desired username | -| password | string | Yes | Password (min 8 chars) | +| username | string | Yes | Desired username (3-50 chars) | +| password | string | Yes | Password (min 8 chars, must meet complexity requirements) | | first_name | string | No | User's first name | | last_name | string | No | User's last name | @@ -34,10 +34,14 @@ Create a new user account. { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "d4f5g6h7...", + "token_type": "Bearer", + "expires_in": 604800, "user": { "id": "uuid-here", "email": "user@example.com", "username": "john", + "first_name": "John", + "last_name": "Doe", "role": "user", "theme": "tokyo-night", "created_at": "2026-01-31T10:00:00Z" @@ -45,6 +49,15 @@ Create a new user account. } ``` +**Set-Cookie Header**: +``` +Set-Cookie: token=eyJhbG...; Max-Age=604800; Path=/; HttpOnly +``` + +**Session Duration**: 7 days (604800 seconds) + +**First User**: The first user registered automatically becomes an admin. + ## Error Responses | Code | Description |