Multi-user & OIDC
A plain Lector install is single-user with no login — that never changes by itself. Setting LECTOR_MODE=cloud opts into accounts: a login/register page, sessions, and a fully separate library (books, vocabulary, progress, settings) per user. It's how you share one instance with a household without sharing a vocabulary list.
On top of that, BYO OIDC (v3.2.0+) lets everyone sign in through an identity provider you already have — Google, Authentik, Keycloak, Auth0, Entra, Pocket ID — instead of managing passwords.
Step 1: turn on accounts
environment:
- LECTOR_MODE=cloud
- BETTER_AUTH_SECRET=${LECTOR_BETTER_AUTH_SECRET} # openssl rand -base64 32
- BETTER_AUTH_URL=https://lector.home.yourdomain.com # the origin you browse to BETTER_AUTH_SECRETis required — cloud mode refuses to boot without one. Generate it once and keep it stable (rotating it signs everyone out).BETTER_AUTH_URLis the public origin login links and OAuth callbacks are minted against — use the exact URL you open Lector on. Accounts work best behind a same-origin reverse proxy (see Tailscale & HTTPS).
With just these, Lector offers email/password signup. Verification emails need an email transport (RESEND_API_KEY) — without one, links only appear in the server logs. If you're doing SSO anyway, skip straight to OIDC: OIDC users don't need email verification at all.
Step 2: point it at your IdP
environment:
# …the three lines above, plus:
- OIDC_ISSUER=https://auth.yourdomain.com
- OIDC_CLIENT_ID=${LECTOR_OIDC_CLIENT_ID}
- OIDC_CLIENT_SECRET=${LECTOR_OIDC_CLIENT_SECRET}
- OIDC_PROVIDER_NAME=Authentik # login button label, default "SSO" All three credentials set → the login page grows a "Continue with <name>" button. Lector reads the provider's discovery document (<issuer>/.well-known/openid-configuration) and uses PKCE — any spec-compliant OIDC provider works. OIDC_ISSUER accepts either the issuer origin or a full discovery URL pasted from your IdP's docs (handy for Keycloak's /realms/<name> paths).
On the IdP side, register a web application client for Lector with this redirect URI:
https://lector.home.yourdomain.com/api/auth/oauth2/callback/oidc Worked example: Google
Google is a perfectly good OIDC provider for a household — everyone already has an account, and there's nothing extra to self-host. This is the setup we run at home:
- In Google Cloud Console → Credentials, create (or reuse) an OAuth client ID of type Web application.
- Under Authorized redirect URIs, add
https://lector.home.yourdomain.com/api/auth/oauth2/callback/oidc - Copy the client ID and secret into your
.env, and set the issuer to Google's:
environment:
- LECTOR_MODE=cloud
- BETTER_AUTH_SECRET=${LECTOR_BETTER_AUTH_SECRET}
- BETTER_AUTH_URL=https://lector.home.yourdomain.com
- OIDC_ISSUER=https://accounts.google.com
- OIDC_CLIENT_ID=${LECTOR_OIDC_CLIENT_ID}
- OIDC_CLIENT_SECRET=${LECTOR_OIDC_CLIENT_SECRET}
- OIDC_PROVIDER_NAME=Google Then docker compose up -d, open Lector, and sign in with Google. If Google answers with redirect_uri_mismatch, the URI in step 2 doesn't exactly match your BETTER_AUTH_URL origin — scheme, host, and path must all agree.
If you already use an OAuth client for another self-hosted app (Actual Budget, say), you can reuse it: just add Lector's redirect URI alongside the existing ones.
Who can sign in?
Anyone who can reach the page and pass your IdP gets an account with its own empty library. Two consequences worth thinking through:
- With a private IdP (Authentik, Keycloak, Pocket ID), your IdP's user list is the allowlist — done.
- With a public IdP like Google, any Google account qualifies — so keep the instance on a private network. On a Tailscale-only hostname that means: your tailnet is the gate, Google is just the login. Avoid exposing a Google-OIDC instance to the open internet.
Email/password signup stays available on the login page, but without an email transport configured those accounts can't complete verification — in practice an instance with OIDC and no RESEND_API_KEY is SSO-only.
Notes
- Existing single-user data: flipping an established instance to cloud mode doesn't delete anything, but data created before the flip belongs to the internal single-user tenant and isn't visible to any account. Back up first, and treat the flip as a fresh start — or migrate the old rows to your new user in the database (open a discussion if you want a hand).
- Per-user API tokens: in cloud mode each signed-in user can mint scoped API tokens in Settings for CLI/script access — the same tokens single-user mode has, tenanted per account.
- GitHub login is also available as a dedicated provider via
GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET, and both can coexist with OIDC. - Bot protection: internet-exposed instances can put Cloudflare Turnstile on the auth forms with
TURNSTILE_SITE_KEY/TURNSTILE_SECRET_KEY. Pointless on a tailnet; useful on the open web.