Skip to content

Authentik SSO Infrastructure

Authentik is the central identity provider for the homelab, providing: - LDAP authentication for legacy apps (Jellyfin, arr stack) - OIDC/OAuth2 for modern web applications - Forward authentication for services without native auth

Architecture

                    ┌─────────────────────────────────────┐
                    │      Authentik (authentik ns)       │
                    │  ┌─────────┐ ┌──────────┐          │
                    │  │ Server  │ │  Worker  │          │
                    │  └────┬────┘ └────┬─────┘          │
                    │       │           │                │
                    │  ┌────▼───────────▼────┐           │
                    │  │   Redis (bundled)   │           │
                    │  └─────────────────────┘           │
                    └──────────┬─────────────────────────┘
              ┌────────────────────────────────────────┐
              │  PostgreSQL (supabase/postgres-0)      │
              │  Schema: authentik                     │
              └────────────────────────────────────────┘

Current Configuration

Services

Service Type IP/DNS Port
Authentik Web UI Ingress auth.internal 80
LDAP Outpost LoadBalancer 10.89.97.218 389/636

LDAP Provider

Setting Value
Name jellyfin-ldap
Base DN DC=ldap,DC=goauthentik,DC=io
Bind Mode cached
Search Mode cached

Applications

App Slug Provider Allowed Groups
Jellyfin jellyfin jellyfin-ldap authentik Admins, authentik Read-only

Key Concepts

Provider

The technical backend that handles authentication protocols.

Provider Type Protocol Use Case
LDAP LDAP/LDAPS Legacy apps (Jellyfin, arr stack)
OAuth2/OIDC OAuth 2.0 / OpenID Connect Modern web apps
Proxy Forward Auth Apps without native auth

Application

The "front door" that users see. Links to exactly one provider and controls access via policy bindings.

Outpost

A separate microservice that speaks non-HTTP protocols (LDAP, RADIUS) or handles proxy auth.

Critical: Outposts must have applications explicitly assigned to function.

Policy Bindings

Controls who can access an application. Bind groups (not individual users) to applications for access control.

LDAP Authentication Flow

1. User enters username/password in Jellyfin
2. Jellyfin LDAP plugin BINDS to outpost (as service account)
3. Jellyfin SEARCHES for user in LDAP directory
4. Jellyfin attempts to BIND AS USER with provided password
5. LDAP Outpost validates against Authentik's user database
6. Success → Jellyfin creates/updates local user profile

Adding a New LDAP Application

Step 1: Create LDAP Provider

  1. Go to http://auth.internal → Login as akadmin
  2. Navigate to Providers → Create
  3. Select LDAP Provider
  4. Configure:
  5. Name: <app-name>-ldap
  6. Authorization flow: default-authentication-flow
  7. Base DN: DC=ldap,DC=goauthentik,DC=io (or unique per app)
  8. Bind mode: Cached binding (recommended)
  9. Search mode: Cached querying (recommended)

Step 2: Create Application

  1. Navigate to Applications → Create
  2. Configure:
  3. Name: Display name (e.g., "Jellyfin")
  4. Slug: URL-safe identifier (e.g., "jellyfin")
  5. Provider: Select the LDAP provider you created
  6. Add Policy Bindings:
  7. Click Policy / Group / User Bindings
  8. Create Binding → Select Group
  9. Add groups that should have access (e.g., authentik Admins)

Step 3: Assign to LDAP Outpost

  1. Navigate to Applications → Outposts
  2. Click ldap-outpost → Edit
  3. In Applications, add your new application
  4. Click Update

The outpost will automatically reload configuration.

Step 4: Configure Client Application

Use these LDAP settings in your application:

Setting Value
LDAP Server 10.89.97.218
LDAP Port 389 (or 636 for LDAPS)
Secure LDAP No (for port 389)
Bind User DN cn=akadmin,ou=users,dc=ldap,dc=goauthentik,dc=io
Bind Password akadmin's password
Base DN dc=ldap,dc=goauthentik,dc=io
User Filter (objectClass=user)
User Attributes uid, cn, mail, displayName
Name Attribute cn

Security: MFA Setup

Since Authentik is internet-facing at auth.bogocat.com, MFA is essential.

MFA Options Comparison

Method Pros Cons
WebAuthn/Passkeys Fastest (tap & done), phishing-resistant, no codes Requires compatible device
TOTP Universal support, works anywhere Code entry friction, clock sync issues

Recommendation: Use WebAuthn as primary, TOTP as fallback.

Current Configuration

The authentication flow uses:

default-authentication-flow:
  Order 10: identification-stage (username/email)
  Order 20: password-stage
  Order 30: mfa-validation (TOTP + WebAuthn)

Step 1: Create TOTP Setup Stage

  1. Go to http://auth.internalFlows & Stages → Stages
  2. Click CreateAuthenticator TOTP Setup Stage
  3. Configure:
  4. Name: totp-setup
  5. Friendly Name: Authenticator App Setup
  6. Digits: 6 (default)
  7. Click Create

Step 2: Create WebAuthn Setup Stage

  1. Still in Stages, click CreateAuthenticator WebAuthn Setup Stage
  2. Configure:
  3. Name: webauthn-setup
  4. Friendly Name: Passkey / Security Key
  5. User verification: Preferred
  6. Resident key requirement: Preferred
  7. Authenticator attachment: Any
  8. Click Create

Step 3: Create Authenticator Validation Stage

  1. Still in Stages, click CreateAuthenticator Validation Stage
  2. Configure:
  3. Name: mfa-validation
  4. Device Classes: Check BOTH:
    • TOTP Authenticators
    • WebAuthn Authenticators
  5. Not configured action: Configure (forces setup if no device)
  6. Configuration Stages: Select BOTH totp-setup AND webauthn-setup
  7. WebAuthn user verification: Preferred
  8. Click Create

Step 4: Add to Authentication Flow

  1. Go to Flows & Stages → Flows
  2. Find default-authentication-flow
  3. Click on it, then go to Stage Bindings
  4. Click Bind Stage
  5. Configure:
  6. Stage: mfa-validation
  7. Order: 30 (after password validation, usually order 20)
  8. Click Create

Step 5: Test WebAuthn Enrollment

  1. Open incognito window
  2. Go to https://auth.bogocat.com
  3. Login with username/password
  4. At MFA prompt, choose Passkey / Security Key
  5. Follow browser prompts to register your device (fingerprint, Face ID, or security key)
  6. Future logins: tap fingerprint/key instead of entering TOTP code

Enrolling Additional WebAuthn Devices

Users can add more devices via self-service:

  1. Log into Authentik
  2. Click user icon → User Settings
  3. Go to MFA Devices
  4. Click EnrollWebAuthn Device
  5. Name the device (e.g., "MacBook Touch ID", "YubiKey")
  6. Follow browser prompts

Passkey Storage Options

Where your passkey is stored determines which devices can use it:

Browser Storage Location Syncs To
Chrome Google Password Manager All devices signed into Google
Safari iCloud Keychain All Apple devices
Firefox macOS local keychain This Mac only

For multi-device access, either: - Enroll a separate passkey from each device, OR - Use Chrome/Safari to store in a syncing provider (Google/iCloud)

To add a phone as a passkey device: 1. During enrollment, choose "Use a phone or tablet" or scan QR 2. Complete enrollment on the phone 3. Phone's biometrics now work for login

Troubleshooting WebAuthn

"This browser doesn't support WebAuthn" - Use Chrome, Firefox, Safari, or Edge (modern versions) - Ensure HTTPS (WebAuthn requires secure context)

"User verification failed" - Device may not have biometrics configured - Try a hardware security key instead

"Registration failed" - Check browser console for errors - Ensure auth.internal or auth.bogocat.com is the RP ID

WebAuthn loops / "Authenticating" spins forever - Check for duplicate MFA stage bindings in the authentication flow:

kubectl exec -n authentik deploy/authentik-server -- ak shell -c "
from authentik.flows.models import Flow, FlowStageBinding
flow = Flow.objects.get(slug='default-authentication-flow')
for b in FlowStageBinding.objects.filter(target=flow).order_by('order'):
    print(f'Order {b.order}: {b.stage.name}')
"
- If multiple MFA stages at same order, remove duplicates via UI

Additional Security Recommendations

  • Strong akadmin password: Change immediately after initial setup
  • Create personal admin account: Don't use akadmin for daily use
  • Rate limiting: Built-in (5 failed attempts = temporary lockout)
  • Consider disabling akadmin: After creating another admin user
  • Enroll multiple WebAuthn devices: Have a backup (e.g., phone + laptop)

User Management

Creating Users

  1. Directory → Users → Create
  2. Set username, email, name
  3. Set Password via user actions menu

Adding Users to Groups

  1. Directory → Groups → Select Group
  2. Click Users tab
  3. Add existing user

Default Groups

Group Purpose
authentik Admins Full admin access to Authentik + all apps
authentik Read-only Read-only Authentik access + configured apps

Troubleshooting

"No provider found for request"

Cause: Application not assigned to outpost.

Fix: Applications → Outposts → ldap-outpost → Edit → Add application.

"Invalid password" but password works in Authentik UI

Cause: Session cache from previous failed attempt.

Fix: Restart the LDAP outpost:

kubectl rollout restart deployment -n authentik ak-outpost-ldap-outpost

LDAP service reverts to ClusterIP

Cause: Authentik controller recreates service with default type.

Fix: Update outpost config in database:

kubectl exec -n supabase postgres-0 -- psql -U postgres -d postgres -c "
SET search_path TO authentik;
UPDATE authentik_outposts_outpost
SET _config = jsonb_set(_config::jsonb, '{kubernetes_service_type}', '\"LoadBalancer\"')
WHERE name = 'ldap-outpost';
"

Or manually create/patch service:

kubectl patch svc -n authentik ak-outpost-ldap-outpost -p '{"spec": {"type": "LoadBalancer"}}'

Testing LDAP Connection

# Test bind as service account
ldapsearch -x -H ldap://10.89.97.218:389 \
  -D "cn=akadmin,ou=users,dc=ldap,dc=goauthentik,dc=io" \
  -w "<password>" \
  -b "dc=ldap,dc=goauthentik,dc=io" "(objectClass=user)"

# Test bind as specific user
ldapsearch -x -H ldap://10.89.97.218:389 \
  -D "cn=<username>,ou=users,dc=ldap,dc=goauthentik,dc=io" \
  -w "<password>" \
  -b "dc=ldap,dc=goauthentik,dc=io" "(cn=<username>)"

Checking Outpost Logs

kubectl logs -n authentik -l goauthentik.io/outpost-name=ldap-outpost --tail=50

Deployment

Initial Deployment

# Automated deployment
/root/tower-fleet/manifests/authentik/deploy.sh

This script: 1. Creates namespace 2. Sets up PostgreSQL user/schema in Supabase 3. Creates Kubernetes secrets 4. Deploys via Helm chart

Post-Deployment

  1. Access initial setup: http://auth.internal/if/flow/initial-setup/
  2. Set akadmin password
  3. Create LDAP provider and application
  4. Create LDAP outpost
  5. Manually create LoadBalancer service (see below)

Manual LDAP Service (Required)

The Authentik-managed service defaults to ClusterIP. Create LoadBalancer manually:

apiVersion: v1
kind: Service
metadata:
  name: ak-outpost-ldap-outpost
  namespace: authentik
spec:
  type: LoadBalancer
  ports:
    - name: ldap
      port: 389
      targetPort: 3389
    - name: ldaps
      port: 636
      targetPort: 6636
    - name: metrics
      port: 9300
      targetPort: 9300
  selector:
    goauthentik.io/outpost-name: ldap-outpost

Files Reference

File Purpose
/root/tower-fleet/manifests/authentik/deploy.sh Automated deployment script
/root/tower-fleet/manifests/authentik/values.yaml Helm chart values
/root/tower-fleet/manifests/authentik/namespace.yaml Namespace definition
/root/tower-fleet/manifests/authentik/migrations/ PostgreSQL migrations