Skip to content

Authentication and Security

Luc Garrabos edited this page May 6, 2026 · 1 revision

Authentication and Security

How BookStorage handles user authentication, session management, authorization, and HTTP security hardening.


Table of contents


Local authentication

Registration

New users register at /register with a username and password. The password is hashed using bcrypt (golang.org/x/crypto/bcrypt) before storage.

If BOOKSTORAGE_REQUIRE_ACCOUNT_VALIDATION is true (default), the account is created with validated = 0 and must be approved by an admin before the user can log in.

Login

Users authenticate at /login with username and password. The submitted password is compared against the stored bcrypt hash.

The codebase also supports legacy password formats (Werkzeug pbkdf2 and plain text comparison) for migration purposes.

Password requirements

Passwords are stored as bcrypt hashes. There is no enforced minimum length at the application level beyond what bcrypt supports.


Session management

How sessions work

  1. On successful login, a 32-byte random token is generated (base64 URL-encoded)
  2. The SHA-256 hash of the token is stored in the sessions table (the raw token is never persisted)
  3. A cookie named session is set with the raw token value

Cookie properties

Property Value
Name session
HttpOnly true
SameSite Varies by environment
Secure true in production
Path /

Session lifetimes

TTL Duration Behavior
Sliding TTL 2 hours Resets on every authenticated request
Absolute TTL 24 hours Maximum session lifetime from creation
  • If a user is inactive for more than 2 hours, the session expires
  • Regardless of activity, sessions expire 24 hours after creation
  • On each request, last_seen_at is updated and the cookie TTL is refreshed

Session revocation

  • Logout: deletes the current session via GET /logout
  • Logout all: revokes all sessions for the user via POST /profile/logout_all (sets revoked_at on all sessions)
  • Account deletion: POST /profile/delete removes the account and all associated data

Token hashing

func hashSessionToken(token string) string {
    h := sha256.Sum256([]byte(token))
    return hex.EncodeToString(h[:])
}

Only the SHA-256 hash is stored in the database. Even if the database is compromised, raw session tokens cannot be recovered.


Google OAuth

BookStorage supports optional Sign in with Google via OAuth 2.0.

Prerequisites

Set three environment variables (see Configuration):

  • BOOKSTORAGE_PUBLIC_ORIGIN (must be https in production)
  • BOOKSTORAGE_GOOGLE_CLIENT_ID
  • BOOKSTORAGE_GOOGLE_CLIENT_SECRET

OAuth flow

sequenceDiagram
    participant User
    participant BookStorage
    participant Google

    User->>BookStorage: GET /auth/google
    BookStorage->>BookStorage: Generate state + PKCE code_verifier
    BookStorage->>BookStorage: Store in oauth_states table
    BookStorage->>Google: Redirect to Google consent
    Google->>User: Show consent screen
    User->>Google: Approve
    Google->>BookStorage: GET /auth/google/callback?code=...&state=...
    BookStorage->>BookStorage: Validate state from oauth_states
    BookStorage->>Google: Exchange code for tokens
    Google->>BookStorage: ID token + access token
    BookStorage->>BookStorage: Extract google_sub, email, name
    BookStorage->>BookStorage: Find or create user by google_sub
    BookStorage->>User: Set session cookie, redirect to dashboard
Loading

Account linking

  • Link: authenticated users can link their Google account via GET /auth/google/link
  • Unlink: users with a local password can unlink via POST /profile/google/unlink
  • Users without a local password cannot unlink (they would lose access)

CSRF protection

OAuth states are stored in the oauth_states table with:

  • A hashed state parameter
  • A PKCE code_verifier
  • An expiration timestamp
  • The intended purpose (login or link)

Roles and permissions

BookStorage has three permission levels:

Role Capabilities
User Manage own library, profile, reading sites, export/import
Admin (is_admin) All user capabilities + account management, monitoring, database tools, catalog enrichment, app updates
Super-admin (is_superadmin) All admin capabilities + SQLite-to-PostgreSQL migration

Superadmin creation

The initial superadmin is created automatically on first startup if no superadmin exists, using:

  • BOOKSTORAGE_SUPERADMIN_USERNAME (default: superadmin)
  • BOOKSTORAGE_SUPERADMIN_PASSWORD

Promoting users

Admins can promote other users to admin via GET /admin/promote/{id}.

Middleware

Middleware Check
RequireLogin Valid session exists
RequireAdmin is_admin = 1
RequireSuperadmin is_admin = 1 AND is_superadmin = 1
RequireWebOnly Request is not from mobile PWA

For /api/* routes, RequireLogin returns a JSON error:

{"error": "session_expired"}

For HTML routes, it redirects to /login.


Account validation

When BOOKSTORAGE_REQUIRE_ACCOUNT_VALIDATION=true (default):

  1. New accounts are created with validated = 0
  2. Users cannot log in until an admin approves their account
  3. Admins approve accounts via Admin > Accounts (GET /admin/approve/{id})

When set to false, users can log in immediately after registration.


HTTP security hardening

Origin and referer checks

Authenticated mutating requests (POST, PATCH, DELETE, PUT) are validated against the Origin or Referer header to prevent CSRF attacks. Requests with mismatched origins are rejected with 403 Forbidden.

Rate limiting

Lightweight rate limiting is applied on sensitive endpoints:

  • Authentication routes (login, registration)
  • Write-heavy API routes

Security headers

The SecurityHeaders middleware sets protective response headers including:

  • Content Security Policy (CSP)
  • X-Frame-Options
  • X-Content-Type-Options
  • Referrer-Policy

HSTS

When BOOKSTORAGE_ENABLE_HSTS=true and the app is served over HTTPS, the Strict-Transport-Security header is sent.

HTTP server timeouts

Timeout Default Purpose
ReadHeaderTimeout 5s Time to read request headers
ReadTimeout 15s Time to read the full request (BOOKSTORAGE_HTTP_READ_TIMEOUT_SEC)
WriteTimeout 120s Time to write the response (BOOKSTORAGE_HTTP_WRITE_TIMEOUT_SEC)
IdleTimeout 60s Keep-alive idle timeout

These timeouts protect against slowloris-style attacks and resource exhaustion.


CI / CD — Next: learn about the CI/CD pipeline and deployment tools.