Designing a Self-Hosted SSO Platform with Authentik: OIDC, SAML, and Proxy Authentication

Introduction

Single sign-on is no longer a luxury reserved for large enterprises with dedicated identity teams. The combination of SaaS-based identity providers (Okta, Auth0, Azure AD) and self-hosted alternatives has democratized SSO across organizations of every size. But SaaS identity providers come with tradeoffs: data residency constraints, per-seat pricing that scales uncomfortably, opaque audit trails, and limited control over authentication flows. Authentik is an open-source identity provider that runs entirely on your infrastructure, supporting OIDC, SAML, and forward proxy authentication — giving you complete control over your identity stack without sacrificing capability.

Why Self-Hosted SSO

The case for self-hosting your identity provider is strongest in three scenarios. First, data sovereignty: regulated industries (healthcare, government, financial services) may have requirements that prohibit sending authentication events to third-party SaaS platforms. Second, cost predictability: SaaS identity providers charge per user per month — at 500+ users, the annual cost easily exceeds the infrastructure cost of a self-hosted deployment. Third, customization: custom authentication flows, organization-specific MFA policies, and integrations with internal systems (legacy LDAP directories, HR systems, internal APIs) are substantially easier to implement when you own the codebase and configuration.

Authentik Architecture

An Authentik deployment consists of four components:

  • Server — the main application process handling web UI, API, and protocol endpoints (OIDC, SAML, LDAP)
  • Worker — background task processor handling email delivery, scheduled tasks, and flow evaluations
  • PostgreSQL — primary datastore for all configuration and identity data
  • Redis — session store and task queue backend

A minimal Docker Compose deployment:

version: "3.8"
services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    environment:
      POSTGRES_DB: authentik
      POSTGRES_USER: authentik
      POSTGRES_PASSWORD: ${PG_PASS}
    volumes:
      - database:/var/lib/postgresql/data

  redis:
    image: docker.io/library/redis:alpine
    command: --save 60 1

  server:
    image: ghcr.io/goauthentik/server:latest
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_SECRET_KEY: ${SECRET_KEY}
    ports:
      - "9000:9000"
      - "9443:9443"

  worker:
    image: ghcr.io/goauthentik/server:latest
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_SECRET_KEY: ${SECRET_KEY}

volumes:
  database:

The AUTHENTIK_SECRET_KEY must be a cryptographically random string of at least 50 characters. Generate it once and store it securely — rotating it invalidates all existing sessions.

OIDC Provider Setup

OpenID Connect (OIDC) is the standard protocol for delegated authentication in modern web applications. In Authentik, each application that needs SSO is registered as an OIDC provider linked to an Application object.

To create an OIDC provider in the Authentik Admin UI:

  1. Navigate to Applications > Providers > Create
  2. Select “OAuth2/OpenID Provider”
  3. Set the Client Type (Confidential for server-side apps, Public for SPAs)
  4. Generate or specify the Client ID and Client Secret
  5. Configure Redirect URIs matching your application’s callback URL
  6. Set token lifetimes: Access Token (5 minutes), Refresh Token (30 days)

For a typical Node.js application using Passport.js or a similar library, the discovery URL pattern is:

https://login.example-corp.com/application/o/myapp/.well-known/openid-configuration

Scope configuration controls what claims are included in the ID token. Authentik’s default scopes:

  • openid — required; provides the sub claim (user identifier)
  • profile — name, preferred_username, picture
  • email — email, email_verified
  • groups — Authentik-specific; user’s group memberships

Create custom property mappings to include additional claims for fine-grained authorization. A Python expression mapping that adds a department claim:

return user.attributes.get("department", "unknown")

SAML Provider Setup

SAML 2.0 remains the dominant protocol for enterprise integrations, particularly with legacy SaaS applications that predate OIDC’s widespread adoption. In Authentik, SAML providers work through metadata exchange.

The setup flow for a SAML service provider:

  1. Obtain the SP metadata XML from the target application (most enterprise apps publish this at a /metadata or /saml/metadata endpoint)
  2. In Authentik, create a SAML Provider and import the SP metadata
  3. Export Authentik’s IdP metadata XML and upload it to the SP
  4. Map SAML attributes to Authentik user fields using Property Mappings

A common attribute mapping for applications that expect NameID in email format:

<!-- SP metadata excerpt showing NameID format expectation -->
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>

In the Authentik SAML provider settings, set “NameID Property” to the user’s email attribute. For applications expecting specific SAML attribute names (e.g., http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress), create a SAML Property Mapping that maps the Authentik attribute to the expected name.

Proxy Authentication with Traefik, Apache, and nginx

Authentik’s outpost (proxy) authentication allows you to protect any web application — even one with no authentication support — by placing it behind a reverse proxy that forwards authentication decisions to Authentik. This is the fastest path to SSO for legacy applications.

The architecture: reverse proxy receives a request, checks a cookie or header with Authentik’s outpost service, gets approve/deny, then either passes the request upstream or redirects to the login page. Configure an Authentik outpost for Traefik using forward authentication:

# Traefik dynamic configuration
http:
  middlewares:
    authentik:
      forwardAuth:
        address: http://10.0.1.20:9000/outpost.goauthentik.io/auth/traefik
        trustForwardHeader: true
        authResponseHeaders:
          - X-authentik-username
          - X-authentik-groups
          - X-authentik-email
          - X-authentik-name

  routers:
    protected-app:
      rule: Host(`app.example-corp.com`)
      middlewares:
        - authentik
      service: app-backend

For Apache, use mod_auth_openidc or configure forward authentication with mod_proxy:

<VirtualHost *:443>
    ServerName app.example-corp.com

    # Forward auth check to Authentik outpost
    <Location />
        AuthType openid-connect
        Require valid-user
    </Location>

    OIDCProviderMetadataURL https://login.example-corp.com/application/o/app/.well-known/openid-configuration
    OIDCClientID your-client-id
    OIDCClientSecret your-client-secret
    OIDCRedirectURI https://app.example-corp.com/redirect_uri
    OIDCCryptoPassphrase a-long-random-string
    OIDCSessionType server-cache:redis
</VirtualHost>

MFA Policies: TOTP, WebAuthn, and Conditional Access

Authentik implements MFA through Flows — a directed graph of stages that a user traverses during authentication. This model is more flexible than a simple “enable MFA” toggle: you can require MFA only for specific applications, only from certain IP ranges, only for users in the “admins” group, or only after a session has been idle for a threshold period.

Supported authenticator types:

  • TOTP — RFC 6238 time-based one-time passwords (Google Authenticator, Authy, 1Password)
  • WebAuthn — FIDO2 hardware tokens (YubiKey, Windows Hello, Touch ID, Passkeys)
  • Static tokens — backup codes for account recovery
  • Duo Push — via Duo authenticator stage

A conditional MFA policy that requires WebAuthn for administrative applications but only TOTP for standard applications is implemented by creating two separate authentication flows and assigning each to the appropriate Application object. Use a Policy Expression stage to gate on group membership:

# Policy expression: require WebAuthn if user is in the "admins" group
return ak_is_group_member(request.user, name="admins")

User Sources: LDAP/AD Sync and SCIM Provisioning

Authentik can federate with existing identity stores rather than replacing them. LDAP/Active Directory sync populates Authentik with users and groups from your directory, enabling SSO without requiring users to create new credentials.

Configure an LDAP Source in Authentik:

  • Server URI: ldaps://dc01.example-corp.com:636
  • Bind CN: a service account with read-only directory access
  • Base DN: DC=example-corp,DC=com
  • User filter: (objectClass=person)
  • Group filter: (objectClass=group)
  • Sync interval: 300 seconds (5 minutes)

SCIM 2.0 provisioning enables Authentik to push user changes downstream to applications that support SCIM — creating, updating, and deprovisioning accounts automatically when users are added or removed in Authentik. This closes the lifecycle loop: HR system → AD/LDAP → Authentik sync → SCIM provisioning → downstream SaaS apps.

Hardening Authentik

A self-hosted identity provider is a high-value target. Several hardening measures are essential:

Rate limiting: Authentik includes built-in rate limiting on login endpoints, but place a reverse proxy rate limiter in front as a defense-in-depth layer. Configure Traefik or nginx to limit /api/v3/flows/executor/ to 20 requests per minute per IP.

Brute force protection: Enable the “Failed Login” policy stage that locks accounts after 5 failed attempts within 10 minutes. Configure the lockout duration to 30 minutes with email notification to the account owner.

Session management: Set session cookies with Secure, HttpOnly, and SameSite=Strict attributes. Configure session expiry to match your security policy — 8 hours for standard users, 1 hour for privileged accounts.

Admin interface isolation: Restrict access to the Authentik admin interface (/if/admin/) by IP using a reverse proxy ACL. Do not expose the admin interface to the public internet.

Key rotation: Rotate the signing certificate used for OIDC tokens and SAML assertions annually. Authentik’s built-in Certificate Manager handles key generation; update the JWKS endpoint reference in any hardcoded integrations after rotation.

Summary

Authentik provides a production-grade self-hosted SSO platform that matches the capability of commercial identity providers for most use cases. Its flow-based architecture makes it uniquely flexible — authentication logic is composed from reusable stages rather than configured through rigid toggles. Combined with OIDC, SAML, proxy authentication, LDAP federation, and SCIM provisioning, it can serve as the identity backbone for an entire organization’s application portfolio. The operational overhead is manageable: a two-container deployment (server + worker) backed by PostgreSQL and Redis handles thousands of daily authentications with negligible resource consumption, leaving budget and attention for higher-value security work.

Scroll to Top