OIDC SSO¶
Argos speaks OIDC Authorization Code + PKCE. Any compliant provider works. This page covers the generic setup, then vendor-specific steps for the five providers most homelab operators run.
What argos needs from the provider¶
Every provider setup ends with the same three values pasted into System → Single sign-on:
- Issuer URL — the OIDC discovery root. Argos fetches
<issuer>/.well-known/openid-configurationand pins the JWKS URL from there. - Client ID — the OAuth client identifier you created at the provider.
- Client secret — the matching secret. Encrypted at rest with
ARGOS_MASTER_KEY(AES-GCM, fresh nonce per save).
Plus one redirect URI to register at the provider:
Visible in the panel's System → Single sign-on → Redirect URI box, with a copy button. The exact string the provider must have on its allow-list.
Flow¶
- User hits
/loginand clicks Sign in with SSO. - Panel calls
/api/auth/oidc/loginwhich creates a server-sidestate+nonce+ PKCEcode_verifier, stores them in an in-memory pending store (10-min TTL, single-use), then 302s the browser to the provider withcode_challenge=S256(verifier). - Provider authenticates the user (password, MFA, passkey, etc.), redirects back to
/api/auth/oidc/callback?code=...&state=.... - Argos validates the state (single-use, not expired), exchanges the code for tokens using the stored
code_verifier, validates the id_token (signature, issuer, audience, expiry, nonce), and extracts claims (sub,email,email_verified,name,preferred_username). - Argos upserts the user keyed by
(external_provider='oidc', external_id=sub), applies the allowlist, optionally enforcesemail_verified, mints a session cookie, 302s back to the original destination.
The state cookie is single-use and consumed on the first successful callback. A replay with the same state fails.
Configuration surface (panel side)¶
System → Single sign-on:
| Field | Notes |
|---|---|
| Enable single sign-on | Master toggle. Off = /api/auth/oidc/* returns 404. |
| Issuer URL | Discovery root. |
| Client ID | OAuth client id. |
| Client secret | Keep blank on re-save to keep the stored value. |
| Scopes | Space-separated. openid is required and auto-added. Defaults to openid email profile. |
| Cookie parent domain | For ForwardAuth across subdomains. See ForwardAuth. |
| Auto-provision | On: first-time users are created; off: first-time users are rejected. |
| Require verified email | On: reject when id_token has email_verified=false or missing. Default off for backcompat. |
| Allowed emails | Exact lowercase match. Empty = allow any authenticated user. |
| Allowed domains | Domain part of email, exact match. example.com matches a@example.com NOT a@sub.example.com. |
Save runs a discovery probe against the issuer before persisting, so invalid URLs fail fast.
Provider walkthroughs¶
Five providers below. Each ends with the three values to paste into the panel.
Google Workspace / Google Cloud¶
- Go to https://console.cloud.google.com/, pick or create a project.
- APIs & Services → OAuth consent screen. User type Internal for Workspace domains, External for personal projects. Fill app name + support email.
- APIs & Services → Credentials → Create Credentials → OAuth client ID.
- Application type: Web application.
- Authorized redirect URIs: paste the value from argos' Redirect URI box.
- Save. Copy the Client ID + Client secret shown.
- On argos:
- Issuer URL:
https://accounts.google.com - Scopes:
openid email profile - Allowed domains: your Workspace domain
- Require verified email: on
- Issuer URL:
Notes:
- Google signs with rotating keys; the discovery URL + JWKS handle rotation transparently.
- Internal-user mode means only accounts in your Workspace org can sign in, in addition to argos' allowlist.
Microsoft 365 / Azure AD¶
- https://portal.azure.com → Microsoft Entra ID (formerly Azure AD) → App registrations → New registration.
- Name:
argos-edge. Account type: whichever matches your tenancy (single-tenant is simplest). - Redirect URI: Web, paste the value from argos.
- After creation:
- Certificates & secrets → New client secret. Copy the Value (not the secret id).
- API permissions → Add → Microsoft Graph → Delegated: at minimum
openid,email,profile.User.Readis added by default; keep it.
- Application (client) ID + Directory (tenant) ID are visible on the app's Overview page.
- On argos:
- Issuer URL:
https://login.microsoftonline.com/<tenant-id>/v2.0 - Scopes:
openid email profile - Allowed domains: your tenant's verified domains
- Issuer URL:
Notes:
- The v2.0 issuer URL is important. v1.0 returns tokens with different claim names that argos' parser does not expect.
- The
emailclaim may come from theupnattribute depending on tenant config; Microsoft sometimes maps these interchangeably.
Authentik (self-hosted)¶
- Authentik UI → Applications → Providers → Create.
- Type: OAuth2/OpenID Provider.
- Name:
argos-edge. - Authorization flow:
default-provider-authorization-implicit-consent(or explicit, whichever your org policy wants). - Signing key: any with RS256 support (Authentik ships with defaults).
- Redirect URI: paste argos' value. Strict mode is fine.
- After save, copy Client ID and Client Secret from the provider detail.
- Applications → Applications → Create. Attach the provider. Slug can be
argos. - On argos:
- Issuer URL:
https://<your-authentik-host>/application/o/argos/(trailing slash matters). - Scopes:
openid email profile - Cookie parent domain: set to the parent shared by panel + target hosts.
- Issuer URL:
Notes:
- Authentik's discovery root is the application-slug-scoped URL. The top-level
/application/o/without a slug does not work. - Enforce MFA in Authentik's own flow; argos' local TOTP is NOT applied to OIDC users.
Authelia¶
- Edit Authelia's
configuration.yml:
identity_providers:
oidc:
hmac_secret: <32+ random chars>
jwks:
- key_id: main
algorithm: RS256
key: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
clients:
- client_id: argos-edge
client_name: "Argos Edge"
client_secret: <keep this matching what you paste in argos>
public: false
authorization_policy: two_factor
redirect_uris:
- https://panel.example.com/api/auth/oidc/callback
scopes:
- openid
- email
- profile
token_endpoint_auth_method: client_secret_post
- Restart Authelia.
- On argos:
- Issuer URL:
https://<authelia-host> - Client ID:
argos-edge - Client secret: the value from config
- Scopes:
openid email profile
- Issuer URL:
Notes:
- Authelia's
authorization_policycontrols whether MFA is required during the OIDC handshake.two_factorforces it for this client. token_endpoint_auth_method: client_secret_postmatches what argos sends;client_secret_basicalso works.
Keycloak¶
- Keycloak admin UI → Clients → Create client.
- Client type: OpenID Connect.
- Client ID:
argos-edge.
- Capability config: enable Client authentication (so a secret is generated). Standard flow on, Implicit + Direct access + Service accounts off.
- Login settings → Valid redirect URIs: paste argos' value.
- After creation: Credentials tab → copy Client secret.
- On argos:
- Issuer URL:
https://<keycloak-host>/realms/<realm-name> - Client ID:
argos-edge - Client secret: from the Credentials tab
- Scopes:
openid email profile
- Issuer URL:
Notes:
- Keycloak's issuer is realm-scoped. Master realm is for admin only; create a purpose-specific realm for argos users.
email_verifiedclaim is populated from the user's "Email verified" profile flag. Turn Require verified email on in argos.
Testing the connection¶
System → Single sign-on → Test connection. Argos runs a discovery fetch against the issuer and surfaces what the provider advertised:
- Issuer the provider reports (sanity-check this matches the URL you typed).
- Authorization endpoint.
- Token endpoint.
- Userinfo + JWKS URLs if advertised.
- Signing algorithms supported.
Discovery failures show as red in the UI. Most common: the issuer URL has a typo, is behind authentication that blocks unauthed fetches, or the cert chain is not trusted by the Go default pool.
Allowlisting¶
The two fields work together:
- An identity is accepted if its email is on
allowed_emailsOR its email domain is onallowed_domains. - Both empty: every authenticated identity the IdP sends is accepted (permissive-trust mode; safe only when the IdP is itself tightly controlled, like an Authentik with a pre-approved user list).
- The email_verified gate (when enabled) runs BEFORE the allowlist check, so an attacker cannot probe the allowlist by sending unverified emails at known domains.
Gotchas¶
- Panel cookie parent domain must cover every protected host. Set it BEFORE adding hosts under ForwardAuth; switching it invalidates every existing session.
- OIDC users bypass argos TOTP. The IdP is authoritative for MFA. If the IdP has no MFA, the argos account has no MFA.
- Break-glass — the bootstrap local admin is NOT OIDC. Keep their password + TOTP recovery codes. An IdP outage that breaks SSO does not lock you out of the panel.
auto_provision=offis restrictive. With it off, there is no UI path to pre-create OIDC user rows; the only way is direct SQL. Leave on unless you are deliberately operating a closed set.- Changing client_secret rotates correctly. Save with the new value in the Client Secret field; the previous ciphertext is overwritten with a fresh AES-GCM nonce.
Related¶
- ForwardAuth — put backends behind the same session cookie.
- Publish with SSO — operational walkthrough.
- Onboard an admin — allowlist + auto-provision.