Skip to content

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 stack tinyauth on CT 101, port 9092 (image ghcr.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-data volume (v5 needs no app secret — sessions live in the DB)
  • Secrets: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, TINYAUTH_OAUTH_WHITELIST are 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.comAPIs & 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).