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:
docker-pve1/security/tinyauth/— Portainer git 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_WHITELISTare Portainer stack env vars, sourced from the gitignored local.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 local .env, then re-set them as the Portainer
stack env vars (they travel only in the API body, never git).
Protecting an app behind SSO¶
Add one line to the app's site block in docker-pve1/management/caddy/Caddyfile; the
merge deploys via caddy-deploy.timer (no manual copy):
grafana.mdhmedia.uk {
import sso # <- add this line (must be first)
import std
reverse_proxy 192.168.1.241:3300
}
The (sso) snippet at the top of the Caddyfile does the forward_auth to
192.168.1.241:9092/api/auth/caddy and copies the Remote-User/Email/Groups/Name headers
through. Currently protected: grafana, jarvis, mdhdocs, kicad.
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 allowed user: append their email to
TINYAUTH_OAUTH_WHITELIST(comma-separated, or a/regex/) in the local.env, re-set the Portainer stack env, redeploy. - Break-glass local login (optional): set
TINYAUTH_AUTH_USERS=username:bcrypthash(generate the hash with the tinyauth CLI) for a non-Google fallback.
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
docker-pve1/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).