Connect identity and application access
SSO has two owners: Authentik decides who may authenticate, and the application decides what that identity may do. Finish both sides before considering an integration complete. The manifests often contain the relying-party settings, but the provider and policy bindings still need to exist in Authentik.
Identify the configured protocol
| Integration | Repository configuration | Administrator work remaining |
|---|---|---|
| Argo CD | Dex OIDC connector | Provider, callback, sealed client secret, Argo RBAC |
| Headscale and Headplane | Shared OIDC client, PKCE options | Both callbacks, allowed domain, Headplane admin role and backend API key |
| Open WebUI | OIDC discovery, client ID, sealed secret | Provider policy, callback, user/model permissions |
| Node-RED | Native adminAuth OIDC strategy, public settings/client ID, sealed secret |
Provider policy and exact editor callback |
| Nextcloud | user_oidc installed |
Provider definition in Authentik and Nextcloud, stable account mapping |
| SuiteCRM | SAML environment and certificate references | Authentik SAML provider, metadata/certificates, CRM roles |
| Traefik, BentoPDF, UrBackup UI | Forward-auth middleware | Proxy provider/outpost assignment and a callback route per hostname |
| Other apps | Local or protocol-specific authentication | Follow their app guide; OIDC is not automatically enabled |
Native OIDC
- Create an Authentik application and OAuth2/OIDC provider. Choose a confidential authorization-code client when the application can keep a client secret; match any PKCE requirements.
- Bind the intended users or groups to the application. Use a stable application slug and provider identity.
- Register exact HTTPS redirect URIs. Use the URI supplied by the application/version; do not rely on a broad wildcard.
- Copy the client ID and issuer/discovery location to the owning application settings. Seal the client secret under the precise name and key referenced by the manifest.
- Request the necessary identity scopes, commonly
openid,profile, andemail. Add group claims only when the application is configured to consume and authorize them. - Test new-account provisioning, existing-account linking, allowed/denied access, and logout in a fresh browser session. Retain a local administrator during the transition.
The issuer is an identity URL; the discovery URL returns configuration describing that issuer and its endpoints. In Authentik’s per-provider mode their shapes are:
Issuerhttps://<auth-host>/application/o/<application-slug>/
Discoveryhttps://<auth-host>/application/o/<application-slug>/.well-known/openid-configurationBoth browser and application pods must reach the required endpoints. A successful browser login does not prove that the backend can fetch discovery metadata or exchange a code. See Authentik’s OAuth2 provider reference.
Callback map
| Application | Callback to allow on the Authentik provider |
|---|---|
| Argo CD native OIDC | https://<argocd-host>/auth/callback |
| Headscale | https://<headscale-host>/oidc/callback |
| Headplane | https://<headscale-host>/admin/oidc/callback |
| Open WebUI | https://<open-webui-host>/oauth/oidc/callback |
| Node-RED | https://flows.antblu.net/auth/strategy/callback |
| Nextcloud | The callback supplied by the installed user_oidc provider configuration |
Replace each hostname with its shared variable value. Preserve path and trailing-slash behavior expected by the application. If the deployment uses a base-path override, derive the callback from that deployed configuration.
Stable identity and roles
Prefer an immutable subject identifier for account association. Before enabling automatic linking by email, establish how verified email, renamed users, and existing local accounts are handled. A mistaken identity mapping can attach a new login to the wrong application account.
Explicitly assign application roles after authentication. Headplane’s default member role is not an administrator. SuiteCRM security groups are separate from Authentik portal assignment.
SuiteCRM SAML
SuiteCRM uses signed SAML assertions in this repository. Exchange metadata rather than guessing endpoints:
- Read the deployed SuiteCRM service-provider metadata to obtain its entity ID, ACS URL, and supported bindings.
- Create the Authentik SAML provider, enter those values, and bind the authorized group.
- Match the IdP entity ID and SSO/SLO URLs with
suitecrm-env. - Seal the IdP certificate and SP certificate/private key into
suitecrm-saml. - Match the configured username attribute and test account provisioning plus CRM permissions.
Changing signing certificates requires coordinating both ends. Keep the private key out of metadata, Git, and documentation; only the public certificate is exchanged with the other party. See SuiteCRM administration documentation.
Forward-auth
Forward-auth protects an HTTP origin before requests reach an application. Traefik asks Authentik for an access decision, then routes an allowed request to the backend. This does not automatically create a user or administrator session inside that backend.
The existing middleware is authentik-forward-auth in namespace traefik. Its address points to the embedded outpost in the Authentik server. Configure each protected host as follows:
- Create a proxy provider for the exact external HTTPS origin, using the appropriate single-application forward-auth mode.
- Assign the provider to the embedded outpost and apply the intended access policies.
- Reference the middleware on the protected ingress.
- Route
/outpost.goauthentik.io/on that same host to the outpost without applying forward-auth again. Give this route priority over the protected catch-all. - Test an unauthenticated request, the callback, an allowed session, and a denied account.
apps/traefik/forward-auth.yaml currently supplies this route for the Traefik dashboard hostname only. BentoPDF and UrBackup already reference the middleware but need their own host-specific callback route/provider setup. The following pattern belongs with the protected service and uses its own namespace:
apiVersion: v1kind: Servicemetadata: name: authentik-outpost namespace: bentopdfspec: type: ExternalName externalName: authentik-server.authentik.svc.cluster.local ports: - name: http port: 80---apiVersion: traefik.io/v1alpha1kind: IngressRoutemetadata: name: authentik-outpost namespace: bentopdfspec: entryPoints: - websecure routes: - kind: Rule match: Host(`${BENTOPDF_HOST}`) && PathPrefix(`/outpost.goauthentik.io/`) priority: 100 services: - kind: Service name: authentik-outpost port: 80 tls: secretName: bentopdf-tlsThis example is documentation, not an already deployed resource. The Traefik Kubernetes CRD provider must permit ExternalName services, as required by this routing pattern; retain that provider setting or use an upstream-supported alternative. The route must be processed by yaml-envsubst. Consult Authentik’s Traefik integration guide for the provider and routing requirements.
Native clients and service integrations
Do not insert interactive redirects into mail, Git SSH, RustDesk, TURN, vault API, or model API traffic. Use the protocol’s supported tokens, client credentials, server keys, or application authentication. For example, Open WebUI should call LiteLLM with a scoped virtual key even when the browser interface uses OIDC.
Troubleshoot by the failing boundary
| Symptom | Inspect first |
|---|---|
| Redirect URI rejected | Exact callback scheme, hostname, path, and provider allowlist |
| Login works but code exchange fails | Backend DNS/TLS, client secret, token auth method, discovery document |
| Outpost path returns 404 | Host-specific route, provider external origin, outpost assignment |
| Repeated redirect loop | Cookie origin, trusted proxy/public URL, callback middleware recursion |
| User logs in but cannot act | Application role, group claims, and local authorization |
| Existing account is duplicated | Subject mapping, provider identity, email linking/provisioning policy |
Keep credentials and authorization codes out of logs shared with others. Record the provider name, callback path, time, and error category instead.
