Single Sign-On (SSO)¶
SnatchArr supports two single sign-on mechanisms:
- OpenID Connect (OIDC): Authorization code flow with PKCE S256 against identity providers like Authentik, Keycloak, Okta, or Google.
- Trusted reverse-proxy authentication: Pre-authenticated user identity asserted via a reverse-proxy HTTP header (Authelia, Authentik proxy outpost, Cloudflare Access, Tailscale).
Both modes map verified identities to the local administrator account (GetFirstAdmin in api/internal/store/queries/auth.sql), preserving SnatchArr's single-admin model. Both modes are disabled by default. If the initial setup has not been completed, SSO login attempts are refused until the admin account is created.
OpenID Connect (OIDC)¶
Getting started¶
To configure OIDC:
- Register an application in your identity provider with:
- Redirect URI:
https://snatcharr.example.com/api/v1/auth/oidc/callback(or your configuredAPP_SNATCHARR_PUBLIC_URL+/api/v1/auth/oidc/callback). - Grant type: Authorization Code.
- PKCE: S256 code challenge.
- Provide your IdP's issuer URL, client ID, client secret, and at least one allowed subject or email.
- The web UI login page detects that OIDC is enabled via
GET /api/v1/auth/ssoand displays a "Sign in with SSO" button.
Configuration¶
| Environment variable | Helm value | Default | Description |
|---|---|---|---|
APP_SNATCHARR_OIDC_ENABLED |
auth.oidc.enabled |
false |
Enable OIDC single sign-on. |
APP_SNATCHARR_OIDC_ISSUER_URL |
auth.oidc.issuerUrl |
OpenID Connect discovery URL. Must use https:// in production; http:// is accepted only under APP_SNATCHARR_PROFILE=dev. |
|
APP_SNATCHARR_OIDC_CLIENT_ID |
auth.oidc.clientId |
OAuth2 client identifier. | |
APP_SNATCHARR_OIDC_CLIENT_SECRET |
via auth.oidc.existingSecret or auth.oidc.secretKeyRef |
OAuth2 client secret. Never committed or inlined in values files. | |
APP_SNATCHARR_OIDC_REDIRECT_URL |
auth.oidc.redirectUrl |
public.url + /api/v1/auth/oidc/callback |
OAuth2 redirect URL. |
APP_SNATCHARR_OIDC_SCOPES |
auth.oidc.scopes |
openid,email,profile |
Comma-separated list of scopes. openid is always required and added automatically if omitted. |
APP_SNATCHARR_OIDC_ALLOWED_SUBJECTS |
auth.oidc.allowedSubjects |
Comma-separated list of IdP subject (sub) claims allowed to log in as admin. |
|
APP_SNATCHARR_OIDC_ALLOWED_EMAILS |
auth.oidc.allowedEmails |
Comma-separated list of email addresses allowed to log in as admin (case-insensitive exact match; requires email_verified == true). |
|
APP_SNATCHARR_OIDC_DISCOVERY_TIMEOUT |
auth.oidc.discoveryTimeout |
10s |
HTTP timeout for provider discovery and token exchange. |
Security guarantees¶
- Mandatory allowlist: Startup fails if OIDC is enabled without at least one entry in
allowed_subjectsorallowed_emails(config.Validateinapi/internal/config/config.go). - Email verification: When matching by email address, the ID token's
email_verifiedclaim must be explicitlytrue. - PKCE and state integrity: Every login generates 32 random bytes for state, nonce, and PKCE verifier (
sso.LoginHandlerinapi/internal/sso/oidc.go). State and nonce verification uses constant-time comparison. - Single-use session tokens: The state, nonce, and verifier are deleted from the session storage on the very first callback request before exchanging tokens, preventing replay attacks.
- Flow timeout: An authorization flow expires 10 minutes after generation.
- Resilient startup: Provider discovery is performed lazily. An IdP outage will prevent OIDC logins but will not prevent the API server from starting or serving credential logins.
Trusted reverse-proxy authentication¶
Getting started¶
When deploying behind an authenticating reverse proxy (such as Authelia, Authentik outpost, or Tailscale serve/funnel with authentication):
- The reverse proxy authenticates the user and forwards their verified identity in an HTTP request header (e.g.
Remote-User). - The proxy forwards the request to SnatchArr.
- When the user loads the SnatchArr web UI, the frontend checks
GET /api/v1/auth/ssoand callsPOST /api/v1/auth/proxy-session. - SnatchArr validates that the direct TCP peer is inside the configured trusted CIDR list and that the username is in the allowed list, then issues a session cookie.
Configuration¶
| Environment variable | Helm value | Default | Description |
|---|---|---|---|
APP_SNATCHARR_PROXY_AUTH_ENABLED |
auth.proxyAuth.enabled |
false |
Enable trusted proxy-header authentication. |
APP_SNATCHARR_PROXY_AUTH_HEADER |
auth.proxyAuth.header |
Remote-User |
Name of the incoming header containing the pre-authenticated username. |
APP_SNATCHARR_PROXY_AUTH_TRUSTED_CIDRS |
auth.proxyAuth.trustedCidrs |
Comma-separated list of IP CIDRs allowed to assert proxy authentication (e.g. 10.0.0.0/8,172.16.0.0/12). |
|
APP_SNATCHARR_PROXY_AUTH_ALLOWED_USERS |
auth.proxyAuth.allowedUsers |
Comma-separated list of usernames allowed to log in as admin (case-insensitive exact match). |
Security risks and operational rules¶
[!WARNING] Any client capable of reaching the SnatchArr HTTP port directly from an IP within
trusted_cidrscan set the configured header and gain admin access.
To prevent unauthorized access when using proxy authentication:
- Direct peer verification: The proxy authentication middleware (
sso.ProxyHeaderMiddlewareinapi/internal/sso/sso.go) checks the direct TCP peer IP beforemiddleware.TrustProxyevaluates anyX-Forwarded-Forheader. SpoofedX-Forwarded-Forheaders cannot trick the proxy check. - Wildcard CIDRs rejected: Startup fails if
trusted_cidrscontains0.0.0.0/0or::/0(config.Validateinapi/internal/config/config.go). - Mandatory user allowlist: Startup fails if
allowed_usersis empty. - Header stripping: The header is deleted from the request immediately after evaluation. Downstream application handlers never see the raw incoming header.
- No direct request authentication: The proxy header does not authenticate API requests directly. It is only accepted at
POST /api/v1/auth/proxy-sessionto issue a standard cookie session. - Network isolation: Ensure the SnatchArr HTTP service is not exposed directly to untrusted networks or pods. Only the reverse proxy should be able to route traffic to the container port.