Authentication (tinyauth SSO / Google login)¶
tinyauth provides single sign-on for the homelab via Caddy's
forward_auth — you log in with your Google/Gmail account. It replaced Authelia
in July 2026: Authelia is an OIDC provider and cannot consume Google as a login
source, so it could never do "Sign in with Google". tinyauth (OpenID-certified) can.
How it fits together¶
flowchart LR
Browser --> Caddy[Caddy CT 112]
Caddy -- "forward_auth /api/auth/caddy" --> Tinyauth[tinyauth<br/>192.168.1.241:9092]
Tinyauth -- "307 to login if no session" --> Portal[auth.mdhmedia.uk/login]
Portal -- "Login with Google" --> Google[(Google OAuth)]
Caddy -- "authenticated (Remote-* headers)" --> App[Protected app]
- Stack:
stacks/security/tinyauth/— Arcane git-sync stacktinyauthon CT 101, port 9092 (imageghcr.io/tinyauthapp/tinyauth) - Portal: auth.mdhmedia.uk
- Login: Google OAuth, restricted to the address(es) in
TINYAUTH_OAUTH_WHITELIST - Sessions/users: SQLite DB in the
tinyauth_tinyauth-datavolume (v5 needs no app secret — sessions live in the DB) - Secrets:
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,TINYAUTH_OAUTH_WHITELISTlive in the stack's Arcane project.envat/opt/docker/tinyauth/.env(see the stack's.env.example)
Google OAuth client (one-time)¶
Create at console.cloud.google.com → APIs & Services → Credentials → OAuth client ID → Web application:
- Authorized redirect URI:
https://auth.mdhmedia.uk/api/oauth/callback/google - Authorized JavaScript origin:
https://auth.mdhmedia.uk - On the OAuth consent screen, add your Gmail as a test user (no need to publish for personal use)
Put the Client ID + secret in the stack's Arcane project .env at /opt/docker/tinyauth/.env
(it stays on the host, never in git).
Protecting an app behind SSO¶
Add one line to the app's site block in edge/caddy/Caddyfile; the
merge deploys via caddy-deploy.timer (no manual copy):
grafana.mdhmedia.uk {
import sso_admin # <- authenticate AND require an admin identity (must be first)
import std
reverse_proxy 192.168.1.241:3300
}
The (sso) snippet does the forward_auth to 192.168.1.241:9092/api/auth/caddy and copies the
Remote-User/Email/Groups/Name headers through — that authenticates any allowed identity.
(sso_admin) wraps (sso) in a route{}, strips any inbound Remote-* headers first (anti-spoof),
then returns 403 unless Remote-Email is an admin — because tinyauth has no per-app access
control, authorization is done in Caddy on the identity header. Two tiers today:
import sso_admin(admin only): grafana, jarvis, mdhdocs, kicad, wgadmin, pve1, pve2, holly, speedtest, libate.import sso(any allowed identity, incl. family): the AudioBookRequest UI (abr.mdhmedia.uk) and the Homepage root (mdhmedia.uk, gated since the 2026-07-20 consolidation).
Browser vs API: 307 vs 401
tinyauth returns a 307 redirect to the login page for real browser navigations
(detected via the Sec-Fetch-Mode: navigate header) and a 401 to API-shaped
requests. So a curl without browser headers sees 401 — that's correct, not a fault.
Caddy copies whichever response back to the client.
Don't SSO-gate an app with non-browser clients on the same host
Native apps (Plex clients, arr companion apps, API keys) can't complete a browser Google flow. Leave those routes public (the arr apps and Plex already are). Only gate browser-only admin apps.
Managing access¶
- Add an OAuth user (Google): append their email to
TINYAUTH_OAUTH_WHITELIST(comma-separated, or a/regex/) in the Arcane project.env(/opt/docker/tinyauth/.env), then redeploy the stack. The whitelist covers the Google login. - Add a local username/password user (no Google account, e.g. a non-Google mailbox that
can't do the Google flow): hash the password with
caddy hash-passwordon CT 112, appendusername:hashto/opt/tinyauth/users.txton CT 101, thendocker restart tinyauth. The file is bind-mounted read-only viaTINYAUTH_AUTH_USERSFILE=/users.txt, lives outside/opt/dockerso the git-sync never touches it, and is not in git (it holds bcrypt hashes). It shows a login form beside the Google button.
Authelia (predecessor — fully removed)¶
Authelia was retired 2026-07-07 and fully torn down 2026-07-08 after the Google flow
soaked: Portainer stack + authelia_authelia-data volume deleted, the repo folder
stacks/security/authelia/ and the (authelia) Caddy snippet removed. The only
remnant is a copy of its users_database.yml at /root/authelia-users-backup.yml on
CT 101 (historical record; the argon2 hashes are useless to tinyauth). Re-introducing
Authelia would be a fresh install from upstream docs, not a rollback.
Troubleshooting¶
# tinyauth logs
ssh root@192.168.1.199 "pct exec 101 -- docker logs tinyauth --tail 50"
# confirm the Google provider is wired (returns a Google auth URL)
ssh root@192.168.1.199 "pct exec 101 -- curl -s http://127.0.0.1:9092/api/oauth/url/google"
Common: a redirect loop means the app's block imports (sso) but auth.mdhmedia.uk itself also
does (it must not); a login that bounces back to the portal means your email isn't in the
whitelist; an "unverified app" Google screen is expected for a personal test-mode OAuth client
(continue past it / add yourself as a test user).