Files
bookhoard/docs/developer/api/authentication/overview.md
T
john-okeefe 2a7ac881fd docs(user): align sync and user guides with current device support
- sync-guide: only Web and KOReader are fully supported; move Kobo to
  coming soon, drop fake Q2-Q4 2026 release dates for mobile/Kindle/
  Remarkable, and describe the plugin + server-approval registration
  flow instead of QR-code/URL approval
- sync-guide: remove cellular/mobile-app advice from battery and
  best-practice sections, correct the Calibre compatibility FAQ, update
  the changelog to reflect shipped vs. pending sync features, and fix
  the license header (GPL-3.0, not MIT)
- user-guide: lead device setup with KOReader; mark the Kobo guide as
  coming soon
- calibre-integration: OPDS client list no longer implies native Kobo
  support
- auth overview: label the mobile-application token guidance as
  'coming later' since no mobile apps exist yet
2026-08-20 14:14:49 -04:00

175 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <token>" \
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 (coming later)
- 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)