- 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
4.1 KiB
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:
// 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
expclaim: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:
- Server generates a JWT access token (valid for 7 days)
- Server creates a refresh token in the database (valid for 7 days)
- Server sets an HTTP-only cookie with the JWT token
- Server returns JSON response with both tokens and user profile
Request:
POST /api/auth/login
{
"login": "user@example.com",
"password": "SecureP@ss123!"
}
Response:
{
"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:
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:
POST /api/auth/refresh
{
"refresh_token": "uuid-refresh-token"
}
Response:
{
"access_token": "new-jwt-token",
"token_type": "Bearer",
"expires_in": 604800
}
4. Logout
Revokes the refresh token on the server:
POST /api/auth/logout
{
"refresh_token": "uuid-refresh-token"
}
Session Expiration Handling
Web Browser Requests
When a user's session expires during page navigation:
- Server redirects to
/login?session=expired - Login page displays: "Your session has expired. Please log in again to continue."
- User re-authenticates and is redirected to their intended destination
API Requests
When an API call receives a 401 Unauthorized response:
{
"error": "session_expired",
"message": "Your session has expired. Please log in again."
}
The frontend toast.js interceptor:
- Clears invalid tokens from localStorage
- Shows an error toast notification
- 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, SessionDurationSecinternal/handlers/refresh_token.go- SessionDurationSec (mirrored)
Total value: 604800 seconds (7 days × 24 hours × 60 minutes × 60 seconds)