Service — Authentik (SSO)
What it is
Authentik is a self-hosted identity provider (IdP). It provides single sign-on (SSO) via OAuth2 / OIDC so users register once and log in to every junc service with one account. The Authentik UUID is the canonical per-user identity across the whole junc — including Quorra.
Location
- Admin UI:
https://auth.juncyard.com/if/admin/ - User portal:
https://auth.juncyard.com - Port:
9090:9000(host:container) - Compose:
~/projects/junc1/compose/auth/docker-compose.yml - Data:
~/data/auth/
Integrated services
| Service | URL | Status |
|---|---|---|
| Matrix (Synapse) | chat.juncyard.com | Active |
| Gitea | git.juncyard.com | Active |
| Open-WebUI | quorra.juncyard.com | Active |
| Nextcloud | cloud.juncyard.com | Active |
| Actual Budget | budget.juncyard.com | Active |
| Jellyfin | watch.juncyard.com | Active (via SSO plugin) |
| Immich | photos.juncyard.com | Active |
Adding a new service
- In Authentik admin: Applications → Providers → Create → OAuth2/OpenID Provider
- Use an RSA signing key (required for Synapse and Actual Budget; safe default for all).
- Set
issuer_modetoper_providerfor services that validate the issuer claim (e.g. the Jellyfin SSO plugin). - Set the redirect URI to
https://<service-url>/<service-specific-callback-path>.
- Create an Application linked to the provider (the slug becomes part of the discovery URL).
- Discovery URL format:
https://auth.juncyard.com/application/o/<slug>/.well-known/openid-configuration - Configure the service with the client ID, secret, and discovery URL.
User invitations
To invite a new household member:
- Admin → Directory → Invitations → Create — set an expiry, optionally tie it to a group.
- Send them the generated link (format:
https://auth.juncyard.com/if/flow/default-source-enrollment/?itoken=...). - They complete signup (username + password) and are logged in automatically as an Internal user.
How the enrollment flow is wired (default-source-enrollment):
| Order | Stage | Purpose |
|---|---|---|
| 0 | invitation-stage | Validates the invite token; blocks the flow without one |
| 0 | default-source-enrollment-prompt | Collects username, password, repeat password |
| 1 | default-source-enrollment-write | Creates the user (type: Internal) |
| 2 | default-source-enrollment-login | Logs the user in immediately |
Notes
- Authentik requires Redis — the
authentik-rediscontainer must run alongsideauthentik-serverandauthentik-worker. - Synapse’s
public_baseurlmust behttps://matrix.juncyard.comfor the OIDC callback URL to resolve correctly. - Actual Budget requires the RS256 signing algorithm specifically; its callback
URI is
https://budget.juncyard.com/openid/callback; it auto-creates users on first SSO login (ACTUAL_USER_CREATION_MODE=login). - Gitea: the provider must use
sub_mode=user_id(returns an integer PK); theexternal_login_user.external_idcolumn must match that integer string. - Jellyfin: uses the 9p4/jellyfin-plugin-sso plugin; the provider must use
issuer_mode=per_provider; register both HTTP and HTTPS redirect URIs (/sso/OID/r/authentik). - Nextcloud: the User OIDC app must be installed (
occ app:install user_oidc) before configuring the provider; configure it viaocc user_oidc:provider. - Immich stores its OAuth config in the
system_metadataPostgres table (key = 'system-config'), not in env vars.