Security advanced ⏱ 60 minutes

Authentik SSO: Centralized Authentication for Your Homelab

Enterprise-grade Single Sign-On for your homelab. OAuth2, SAML, LDAP, and multi-factor authentication.

By homelabbing.io • 2025-12-16
#authentik#sso#authentication#oauth2#security

Introduction

Imagine logging into your Portainer dashboard, Proxmox interface, and Nextcloud instance with the same credentials, protected by multi-factor authentication (MFA), all managed from a single, beautiful web interface. That’s the promise of Authentik, a powerful open-source Identity Provider that brings enterprise-grade authentication to your homelab.

In this comprehensive guide, you’ll learn how to deploy Authentik from scratch, configure OAuth2/OIDC providers, implement LDAP integration, set up multi-factor authentication, and even automate user management with AI-powered workflows. By the end, you’ll have a production-ready SSO system securing your entire infrastructure.

What You’ll Build:

  • A fully functional Authentik SSO server with PostgreSQL backend
  • OAuth2/OIDC integrations for Portainer, Proxmox, and Nextcloud
  • Multi-factor authentication with TOTP and WebAuthn/security keys
  • Custom authentication flows with conditional access policies
  • Automated user provisioning and compliance reporting
  • LDAP provider for legacy applications

Why Authentik?

  • Modern Interface: Beautiful, intuitive web UI that rivals commercial solutions
  • Full Protocol Support: OAuth2, OIDC, SAML2, LDAP, and RADIUS
  • Resource Efficient: 2-4GB RAM (vs Keycloak’s 4-8GB)
  • Simplified Deployment: Redis eliminated in version 2025.10
  • Active Development: Monthly releases with new features
  • Open Source: Complete control over your authentication data

Prerequisites

Before we begin, ensure you have:

Technical Requirements:

  • Server with 2 CPU cores, 4GB RAM, 10GB storage
  • Docker 20.10.0+ and Docker Compose 1.27.0+
  • Domain name (internal or external, e.g., authentik.example.com)
  • DNS configured to point to your server
  • (Optional) Reverse proxy: Traefik, Nginx Proxy Manager, or Caddy
  • (Optional) SMTP credentials for email notifications

Knowledge Requirements:

  • Comfortable with Docker and command line
  • Basic understanding of DNS and networking
  • Familiarity with reverse proxies (helpful but not required)

Preparation:

# Verify Docker installation
docker --version  # Should be 20.10.0+
docker-compose --version  # Should be 1.27.0+

# Create working directory
sudo mkdir -p /opt/authentik
cd /opt/authentik

Section 1: Understanding SSO and Authentik

What is Single Sign-On?

Single Sign-On (SSO) is a centralized authentication mechanism where users authenticate once and gain access to multiple interconnected applications. Instead of remembering separate passwords for Portainer, Proxmox, Nextcloud, and Home Assistant, you log in once to Authentik, and all applications trust that authentication.

Key Benefits:

  • Reduced Friction: One set of credentials for all services
  • Improved Security: Centralized password policies and MFA enforcement
  • Easier Administration: Manage users and permissions from one location
  • Complete Audit Trail: Every authentication event logged in one place
  • Consistent Experience: Uniform login flow across all applications

Authentik vs Alternatives

Before diving in, let’s understand why you might choose Authentik over alternatives:

FeatureAuthentikAutheliaKeycloak
TypeFull IdPAuth ProxyFull IdP
RAM Usage2-4GB512MB4-8GB
Setup ComplexityMediumLowHigh
OAuth2/OIDC✅ Full Support⚠️ Limited✅ Full Support
SAML2✅ Yes❌ No✅ Yes
LDAP Provider✅ Yes❌ No✅ Yes
RADIUS✅ Yes❌ No❌ No
Web UI QualityExcellentBasicComprehensive
Best ForHomelabs, SMBLightweight SSOEnterprise

Choose Authentik if:

  • You want a modern, full-featured IdP with intuitive UI
  • Need SAML, LDAP, or RADIUS support
  • Running homelabs or small business with diverse SSO needs
  • Can allocate 2-4GB RAM
  • Want active development and growing community

Authentication Protocols Explained

Authentik supports multiple authentication protocols. Here’s when to use each:

OAuth2/OIDC (Most Common)

  • Used by: Portainer, Proxmox, Nextcloud, Grafana, most modern apps
  • Best for: Web applications and mobile apps
  • How it works: Token-based authentication with delegated authorization
  • Example: “Login with Authentik” button

SAML2 (Enterprise)

  • Used by: Legacy enterprise applications, federated identity systems
  • Best for: Enterprise integrations requiring XML-based auth
  • How it works: XML assertions with digital signatures

LDAP (Directory Services)

  • Used by: Network devices, Jellyfin, traditional applications
  • Best for: Legacy applications and infrastructure
  • How it works: Directory-based authentication with attribute queries

RADIUS (Network Access)

  • Used by: WiFi networks, VPN servers, network equipment
  • Best for: Network authentication scenarios
  • How it works: Challenge-response authentication protocol

Section 2: Installing Authentik

Step 1: Download Docker Compose Configuration

Authentik provides an official Docker Compose file that includes all necessary components:

# Navigate to working directory
cd /opt/authentik

# Download official docker-compose.yml
wget https://goauthentik.io/docker-compose.yml

# Or use curl if wget isn't available
curl -O https://goauthentik.io/docker-compose.yml

Step 2: Generate Secure Credentials

Security starts with strong credentials. Generate secure passwords and secret keys:

# Generate PostgreSQL password
export PG_PASS=$(openssl rand -base64 32)

# Generate Authentik secret key
export AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)

# Generate initial admin password
export AUTHENTIK_BOOTSTRAP_PASSWORD=$(openssl rand -base64 16)

# Display generated credentials (SAVE THESE SECURELY!)
echo "PostgreSQL Password: $PG_PASS"
echo "Authentik Secret Key: $AUTHENTIK_SECRET_KEY"
echo "Bootstrap Password: $AUTHENTIK_BOOTSTRAP_PASSWORD"

Important: Save these credentials in a password manager. You’ll need them for recovery.

Step 3: Configure Environment Variables

Create a .env file with your configuration:

cat > .env << 'EOF'
# PostgreSQL Configuration
POSTGRES_PASSWORD=REPLACE_WITH_PG_PASS
POSTGRES_DB=authentik
POSTGRES_USER=authentik

# Authentik Configuration
AUTHENTIK_SECRET_KEY=REPLACE_WITH_SECRET_KEY
AUTHENTIK_BOOTSTRAP_PASSWORD=REPLACE_WITH_BOOTSTRAP_PASSWORD
[email protected]

# Error Reporting (optional, disable for privacy)
AUTHENTIK_ERROR_REPORTING__ENABLED=false

# SMTP Configuration (optional, for email notifications)
# Uncomment and configure if you want email functionality
#AUTHENTIK_EMAIL__HOST=smtp.resend.com
#AUTHENTIK_EMAIL__PORT=587
#AUTHENTIK_EMAIL__USERNAME=resend
#AUTHENTIK_EMAIL__PASSWORD=your-resend-api-key
#[email protected]
#AUTHENTIK_EMAIL__USE_TLS=true
EOF

Replace the placeholder values with your generated credentials:

# Linux/Mac: Use sed to replace values
sed -i "s/REPLACE_WITH_PG_PASS/$PG_PASS/" .env
sed -i "s/REPLACE_WITH_SECRET_KEY/$AUTHENTIK_SECRET_KEY/" .env
sed -i "s/REPLACE_WITH_BOOTSTRAP_PASSWORD/$AUTHENTIK_BOOTSTRAP_PASSWORD/" .env

# Update email to your actual admin email
sed -i "s/[email protected]/[email protected]/" .env

Step 4: Optional SMTP Configuration

Email functionality is optional but recommended for password resets and notifications. Here’s how to configure with Resend (free tier):

  1. Sign up for Resend: Visit https://resend.com and create a free account
  2. Generate API Key: In Resend dashboard, create a new API key
  3. Configure Domain: Add and verify your domain (or use Resend’s test domain)
  4. Update .env: Uncomment and fill in SMTP settings
# Example Resend configuration
AUTHENTIK_EMAIL__HOST=smtp.resend.com
AUTHENTIK_EMAIL__PORT=587
AUTHENTIK_EMAIL__USERNAME=resend
AUTHENTIK_EMAIL__PASSWORD=re_YourAPIKeyHere
[email protected]
AUTHENTIK_EMAIL__USE_TLS=true

Step 5: Start Authentik Services

Now let’s bring everything up:

# Pull latest images
docker-compose pull

# Start services in background
docker-compose up -d

# Monitor startup logs (Ctrl+C to exit)
docker-compose logs -f

Wait for startup: Authentik typically takes 30-60 seconds to become available. You’ll see messages like:

authentik_server | Successfully migrated database
authentik_server | Started server on 0.0.0.0:9000
authentik_worker | Started worker

Step 6: Verify Installation

Check that all services are running:

# View container status
docker-compose ps

# Expected output:
# NAME                 STATUS
# authentik_server     Up
# authentik_worker     Up
# postgres             Up

All services should show “Up” status. If any are restarting, check logs:

docker-compose logs authentik_server

Step 7: Access Initial Setup

Open your browser and navigate to the initial setup URL:

http://your-server-ip:9000/if/flow/initial-setup/

Important: The trailing slash is required, or you’ll get a 404 error.

Complete the Setup Wizard:

  1. Set Admin Password: Create a strong password (or use the bootstrap password)
  2. Admin Email: Enter your email address
  3. Instance Name: Enter a friendly name (e.g., “Homelab Authentication”)
  4. SMTP Settings: Configure email (or skip for now)
  5. Complete Setup: Click finish and log in

Step 8: Post-Installation Security

After initial setup, immediately secure your installation:

# 1. Enable MFA for admin account
# Log in to https://your-ip:9000/
# Click your username → Settings → MFA Devices → Enroll TOTP

# 2. Create database backup
docker-compose exec postgres pg_dump -U authentik authentik > backup-$(date +%Y%m%d).sql

# 3. Backup .env file
cp .env .env.backup

# 4. Set up automated backups
sudo cat > /etc/cron.daily/authentik-backup << 'EOF'
#!/bin/bash
cd /opt/authentik
docker-compose exec -T postgres pg_dump -U authentik authentik > /backup/authentik-$(date +%Y%m%d).sql
find /backup -name "authentik-*.sql" -mtime +7 -delete
EOF

sudo chmod +x /etc/cron.daily/authentik-backup

While optional, a reverse proxy provides TLS certificates and simplifies domain management. Here’s a Traefik configuration:

Traefik Docker Compose Integration

Update your docker-compose.yml to add Traefik labels:

version: '3.4'

services:
  # ... (postgres service remains unchanged)

  authentik_server:
    image: ghcr.io/goauthentik/server:latest
    command: server
    environment:
      # ... (existing environment variables)
    networks:
      - traefik
      - internal
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.authentik.rule=Host(`authentik.example.com`)"
      - "traefik.http.routers.authentik.entrypoints=websecure"
      - "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
      - "traefik.http.services.authentik.loadbalancer.server.port=9000"

networks:
  traefik:
    external: true
  internal:

Replace authentik.example.com with your actual domain.

After updating, restart services:

docker-compose down
docker-compose up -d

You should now be able to access Authentik at https://authentik.example.com.

Section 4: Creating Your First Application

Let’s integrate Portainer with Authentik using OAuth2.

Step 1: Create OAuth2 Provider in Authentik

  1. Navigate to Admin Interface: Click “Admin Interface” in top right

  2. Create Application: Go to Applications → Applications

  3. Click “Create with Provider”

  4. Fill in Application Details:

    • Name: Portainer
    • Slug: portainer (used in URLs)
    • Provider Type: Select “OAuth2/OpenID Provider”
  5. Configure OAuth2 Provider:

    • Name: Portainer OAuth2
    • Authorization flow: default-provider-authorization-implicit-consent
    • Redirect URIs: https://portainer.example.com/auth/oauth/callback
    • Scopes: Select openid, profile, email
  6. Save and Copy Credentials:

    • Note the Client ID (looks like: abcd1234efgh5678)
    • Note the Client Secret (click to reveal and copy)

Step 2: Configure Portainer

  1. Log into Portainer as admin
  2. Navigate to: Settings → Authentication
  3. Select: OAuth
  4. Choose: Custom
  5. Fill in OAuth Configuration:
Provider Name: Authentik
Client ID: [paste from Authentik]
Client Secret: [paste from Authentik]
Authorization URL: https://authentik.example.com/application/o/authorize/
Access Token URL: https://authentik.example.com/application/o/token/
Resource URL: https://authentik.example.com/application/o/userinfo/
Redirect URL: https://portainer.example.com/auth/oauth/callback
Scopes: openid profile email

Important: Enter scopes without commas (space-separated).

  1. Save settings and test by logging out

Step 3: Test OAuth2 Integration

  1. Logout from Portainer
  2. Click “Authenticate with OAuth”
  3. Redirected to Authentik → Enter credentials
  4. Grant permissions (if prompted)
  5. Redirected back to Portainer → Successfully logged in!

Troubleshooting: If you see “redirect_uri_mismatch”, verify that the redirect URI in Authentik exactly matches what Portainer is sending (check protocol, domain, port, and path).

Section 5: Multi-Factor Authentication (MFA)

MFA dramatically improves security. Let’s enable TOTP (Time-based One-Time Passwords) for all users.

Step 1: Create TOTP Stage

  1. Navigate to: Flows & Stages → Stages
  2. Click Create and select “TOTP Authenticator Setup Stage”
  3. Configure:
    • Name: TOTP Authenticator
    • Issuer: Homelab (shown in authenticator app)
    • Digits: 6 (standard)
  4. Save

Step 2: Add TOTP to Authentication Flow

  1. Navigate to: Flows & Stages → Flows
  2. Find and Edit: default-authentication-flow
  3. Click “Stage Bindings”
  4. Click “Bind Stage”
  5. Select:
    • Stage: authenticator-validation-setup
    • Order: 25 (after password validation)
    • Evaluate on plan: false
  6. Click “Create”

Step 3: Configure Authentication Validation

  1. In same flow, find: default-authentication-mfa-validation
  2. Edit this stage
  3. Configure:
    • Device classes: Select “TOTP” and “Static”
    • Configuration stage: Select your “TOTP Authenticator” stage
  4. Save

Step 4: Test TOTP Enrollment

  1. Logout and login with your admin account
  2. You’ll be prompted: “Configure Authenticator”
  3. Scan QR code with authenticator app:
    • Google Authenticator (iOS/Android)
    • Authy (iOS/Android/Desktop)
    • 1Password (if you have it)
    • Microsoft Authenticator
  4. Enter verification code from app
  5. Save backup codes (print or store securely)
  6. Complete login

Next logins will require both password and TOTP code.

Enabling WebAuthn/Security Keys (Optional)

For even stronger security, enable WebAuthn for hardware security keys:

  1. Create WebAuthn Stage:

    • Flows & Stages → Stages → Create
    • Type: “WebAuthn Authenticator Setup Stage”
    • Name: Security Keys
    • User verification: preferred
  2. Add to Authentication Flow:

    • Edit default-authentication-flow
    • Add WebAuthn validation alongside TOTP
  3. User Enrollment:

    • Login → Prompted to register security key
    • Insert YubiKey, Google Titan, or use platform authenticator
    • Touch key to confirm
    • Give device a name (e.g., “YubiKey 5 NFC”)

Supported Devices:

  • YubiKey (4, 5 series)
  • Google Titan Security Key
  • Windows Hello
  • TouchID/FaceID (Mac/iOS)
  • Android biometric authentication

Section 6: Proxmox Integration (OIDC)

Proxmox VE supports OpenID Connect, making integration straightforward.

Step 1: Create OAuth2 Provider for Proxmox

  1. In Authentik: Applications → Create with Provider
  2. Application Details:
    • Name: Proxmox
    • Slug: proxmox
  3. Provider Configuration:
    • Type: OAuth2/OpenID
    • Name: Proxmox OAuth2
    • Redirect URIs: https://proxmox.example.com/auth-verify.html?realm=authentik
    • Scopes: openid profile email
  4. Save and copy Client ID and Secret

Step 2: Configure Proxmox OpenID Realm

Via Web UI:

  1. Datacenter → Permissions → Realms → Add
  2. Select: OpenID Connect Server
  3. Configure:
    • Issuer URL: https://authentik.example.com/application/o/proxmox/
    • Realm: authentik
    • Client ID: [from Authentik]
    • Client Key: [Client Secret from Authentik]
    • Username Claim: preferred_username
    • Scopes: openid profile email
    • Autocreate Users: ✅ Enabled

Via CLI (Alternative):

pveum realm add authentik   --type openid   --issuer-url https://authentik.example.com/application/o/proxmox/   --client-id YOUR_CLIENT_ID   --client-key YOUR_CLIENT_SECRET   --username-claim preferred_username   --autocreate 1

Step 3: Test Proxmox Authentication

  1. Logout from Proxmox
  2. On login screen, select Realm: authentik
  3. Click Login → Redirected to Authentik
  4. Enter credentials (with MFA if enabled)
  5. Redirected back → Logged into Proxmox!

Step 4: Configure User Permissions

Users auto-created by OpenID have no permissions by default:

# Grant user admin privileges (via Proxmox CLI)
pveum user modify user@authentik --group Administrator

# Or create a custom role
pveum role add AuthentikUser -privs VM.Console,VM.PowerMgmt,VM.Monitor
pveum acl modify / -user user@authentik -role AuthentikUser

Section 7: Nextcloud Integration (OIDC)

Nextcloud requires an app for OpenID Connect support.

Step 1: Install OIDC App in Nextcloud

  1. Login to Nextcloud as admin
  2. Apps → Search “OpenID”
  3. Install: “OpenID Connect Login” (by DAITA)
  4. Enable the app

Step 2: Create OAuth2 Provider in Authentik

  1. Applications → Create with Provider
  2. Application Details:
    • Name: Nextcloud
    • Slug: nextcloud
  3. Provider Configuration:
    • Type: OAuth2/OpenID
    • Redirect URIs: https://nextcloud.example.com/apps/user_oidc/code
    • Scopes: openid profile email
  4. Copy Client ID and Secret

Step 3: Configure Nextcloud OIDC

  1. Settings → OpenID Connect
  2. Click “Add provider”
  3. Fill in configuration:
Identifier: Authentik
Client ID: [from Authentik]
Client Secret: [from Authentik]
Discovery endpoint: https://authentik.example.com/application/o/nextcloud/.well-known/openid-configuration

Advanced settings:

  • Attribute for display name: name
  • Attribute for user id: sub
  • Attribute for email: email
  1. Save

Step 4: Test Nextcloud Login

  1. Logout from Nextcloud
  2. Login page shows “Login with Authentik”
  3. Click the button → Authenticate via Authentik
  4. First login creates user automatically
  5. Subsequent logins use same user account

User Mapping: Nextcloud creates local users based on Authentik profiles. Group memberships must be configured separately in Nextcloud.

Section 8: LDAP Provider for Legacy Applications

Some applications (like Jellyfin) work best with LDAP. Authentik can act as an LDAP server.

Step 1: Create LDAP Provider

  1. Providers → Create → LDAP Provider
  2. Configure:
    • Name: Internal LDAP
    • Bind DN: cn=ldap,dc=example,dc=com
    • Base DN: dc=example,dc=com
    • Search group: Select a group (e.g., “authentik Admins” or create “LDAP Users”)
  3. Generate and save Bind password

Step 2: Create LDAP Outpost

Outposts handle protocol-specific functionality:

  1. System → Outposts → Create
  2. Configure:
    • Name: LDAP Outpost
    • Type: LDAP
    • Integration: Docker
    • Provider: Select your “Internal LDAP” provider
  3. Save

Step 3: Deploy LDAP Outpost Container

Add to your docker-compose.yml:

  authentik_ldap:
    image: ghcr.io/goauthentik/ldap:latest
    environment:
      AUTHENTIK_HOST: http://authentik_server:9000
      AUTHENTIK_INSECURE: "true"
      AUTHENTIK_TOKEN: [Get from Outpost page in Authentik]
    ports:
      - "389:3389"
      - "636:6636"
    networks:
      - internal

Get the token: In Authentik, go to the Outpost you created, and copy the token.

# Restart services
docker-compose up -d

Step 4: Test LDAP Connection

# Test LDAP search (install ldapsearch if needed: apt-get install ldap-utils)
ldapsearch -x -H ldap://localhost:389   -D "cn=ldap,dc=example,dc=com"   -w YOUR_BIND_PASSWORD   -b "dc=example,dc=com"   "(objectClass=user)"

You should see your Authentik users in LDAP format!

Step 5: Configure Application (Jellyfin Example)

  1. In Jellyfin: Dashboard → Plugins → Catalog
  2. Install: “LDAP Authentication” plugin
  3. Configure LDAP:
    • LDAP Server: your-authentik-server:389
    • Bind DN: cn=ldap,dc=example,dc=com
    • Bind Password: [from Authentik]
    • Base DN: dc=example,dc=com
    • Search Filter: (cn={username})
  4. Save and test by logging in with Authentik user

Section 9: Custom Flows and Policies

Authentik’s real power lies in customizable authentication flows.

Creating a Risk-Based Authentication Flow

Let’s create a flow that requires stronger MFA for suspicious logins:

Step 1: Create Risk Evaluation Policy

  1. Flows & Stages → Policies → Create → Expression Policy
  2. Name: Risk Assessment
  3. Expression:
# Risk-based authentication policy
from datetime import datetime, timedelta

risk_score = 0

# Check if login from new IP
if not request.context.get("ip_seen_before"):
    risk_score += 20

# Check if outside business hours
now = datetime.now()
if now.hour &lt; 8 or now.hour &gt; 18 or now.weekday() &gt; 4:
    risk_score += 10

# Check if user hasn't logged in recently
if user.last_login and (datetime.now() - user.last_login) > timedelta(days=30):
    risk_score += 15

# Store risk score for later stages
request.context["risk_score"] = risk_score

# Allow continuation but flag high risk
return True
  1. Save

Step 2: Create Conditional MFA Stage

  1. Create Expression Policy for MFA requirement:
# Require WebAuthn for high risk logins
risk_score = request.context.get("risk_score", 0)
return risk_score >= 30  # Require stronger MFA if risk >= 30
  1. Name: High Risk MFA Required

Step 3: Build Custom Flow

  1. Flows & Stages → Flows → Create
  2. Name: Risk-Based Authentication
  3. Designation: Authentication
  4. Add Stage Bindings:
    • Stage 1: default-authentication-identification
    • Stage 2: default-authentication-password
    • Stage 3: Policy evaluation (bind your Risk Assessment policy)
    • Stage 4: TOTP validation (for medium risk)
    • Stage 5: WebAuthn validation (for high risk, bind High Risk MFA Required policy)
    • Stage 6: default-authentication-login

Step 4: Assign Flow to Application

  1. Edit an application (e.g., Portainer)
  2. Set Authentication flow: Select “Risk-Based Authentication”
  3. Save

Now logins to that application will use adaptive MFA based on risk!

IP-Based Access Control

Create a policy to restrict access to corporate networks:

# IP whitelist policy
import ipaddress

allowed_networks = [
    ipaddress.ip_network("192.168.1.0/24"),  # Home network
    ipaddress.ip_network("10.0.0.0/8"),      # Internal network
]

client_ip = ipaddress.ip_address(request.http_request.client_ip)

# Check if IP is in allowed networks
for network in allowed_networks:
    if client_ip in network:
        return True

# Deny access from other networks
ak_message("Access denied: Not from allowed network")
return False

Apply to application:

  1. Edit application
  2. Policy Bindings tab
  3. Bind this policy
  4. Set Order to run early

Section 10: AI Automation and Advanced Workflows

Here’s where Authentik gets really powerful: automation.

Use Case 1: Automated User Provisioning

Automatically create users based on external events (HR system, LDAP sync, webhooks).

Python Script for API-Based Provisioning:

#!/usr/bin/env python3
"""
Authentik User Provisioning Script
Automatically creates users based on CSV input
"""

import requests
import csv
import sys

# Configuration
AUTHENTIK_URL = "https://authentik.example.com"
API_TOKEN = "your-api-token-here"  # Generate in Authentik UI: Admin → Tokens

def create_user(username, email, first_name, last_name, groups=None):
    """Create a new user in Authentik"""

    headers = {
        "Authorization": f"Bearer {API_TOKEN}",
        "Content-Type": "application/json"
    }

    user_data = {
        "username": username,
        "email": email,
        "name": f"{first_name} {last_name}",
        "is_active": True,
        "groups": groups or []
    }

    response = requests.post(
        f"{AUTHENTIK_URL}/api/v3/core/users/",
        headers=headers,
        json=user_data
    )

    if response.status_code == 201:
        print(f"✅ Created user: {username}")
        return response.json()
    else:
        print(f"❌ Failed to create {username}: {response.text}")
        return None

def add_user_to_group(user_id, group_name):
    """Add user to a group"""

    headers = {"Authorization": f"Bearer {API_TOKEN}"}

    # Find group by name
    response = requests.get(
        f"{AUTHENTIK_URL}/api/v3/core/groups/?name={group_name}",
        headers=headers
    )

    if response.json()["results"]:
        group_id = response.json()["results"][0]["pk"]

        # Add user to group
        group_data = {"pk": group_id, "users": [user_id]}
        response = requests.patch(
            f"{AUTHENTIK_URL}/api/v3/core/groups/{group_id}/",
            headers=headers,
            json=group_data
        )

        if response.status_code == 200:
            print(f"✅ Added user to group: {group_name}")
        else:
            print(f"❌ Failed to add to group: {response.text}")

# Example: Provision users from CSV
if __name__ == "__main__":
    with open("users.csv", "r") as f:
        reader = csv.DictReader(f)
        for row in reader:
            user = create_user(
                username=row["username"],
                email=row["email"],
                first_name=row["first_name"],
                last_name=row["last_name"]
            )

            if user and "department" in row:
                add_user_to_group(user["pk"], row["department"])

CSV Format (users.csv):

username,email,first_name,last_name,department
jdoe,[email protected],John,Doe,Engineering
asmith,[email protected],Alice,Smith,Operations

Run the script:

chmod +x provision_users.py
./provision_users.py

Use Case 2: Automated Compliance Reporting

Generate weekly reports on MFA enrollment and security posture:

#!/usr/bin/env python3
"""
Authentik Compliance Report Generator
Tracks MFA enrollment, inactive accounts, and security metrics
"""

import requests
from datetime import datetime, timedelta
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

AUTHENTIK_URL = "https://authentik.example.com"
API_TOKEN = "your-api-token"

def get_all_users():
    """Fetch all users from Authentik"""
    headers = {"Authorization": f"Bearer {API_TOKEN}"}
    response = requests.get(f"{AUTHENTIK_URL}/api/v3/core/users/", headers=headers)
    return response.json()["results"]

def generate_compliance_report():
    """Generate comprehensive security report"""

    users = get_all_users()
    total_users = len(users)

    # MFA Statistics
    mfa_enabled = sum(1 for u in users if u.get("attributes", {}).get("mfa_enabled"))
    mfa_percentage = (mfa_enabled / total_users * 100) if total_users &gt; 0 else 0

    # Inactive Account Detection
    thirty_days_ago = datetime.now() - timedelta(days=30)
    inactive_users = [
        u for u in users
        if u.get("last_login") and datetime.fromisoformat(u["last_login"].replace("Z", "+00:00")) < thirty_days_ago
    ]

    # Admin Accounts
    admin_users = [u for u in users if any("admin" in g.lower() for g in u.get("groups", []))]

    # Generate Report
    report = f"""
    === AUTHENTIK SECURITY COMPLIANCE REPORT ===
    Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

    USER STATISTICS
    ---------------
    Total Users: {total_users}
    Active Users: {total_users - len(inactive_users)}
    Inactive Users (30+ days): {len(inactive_users)}

    MFA ENROLLMENT
    --------------
    Users with MFA: {mfa_enabled}
    MFA Enrollment Rate: {mfa_percentage:.1f}%
    Users without MFA: {total_users - mfa_enabled}

    ADMIN ACCOUNTS
    --------------
    Total Admin Accounts: {len(admin_users)}
    Admin accounts: {', '.join(u['username'] for u in admin_users[:10])}

    RECOMMENDATIONS
    ---------------
    """

    if mfa_percentage &lt; 90:
        report += "⚠️  MFA enrollment below 90% - Consider mandatory MFA policy\n"

    if len(inactive_users) &gt; 0:
        report += f"⚠️  {len(inactive_users)} inactive accounts - Review and disable\n"
        report += f"   Inactive users: {', '.join(u['username'] for u in inactive_users[:5])}\n"

    if mfa_percentage >= 90 and len(inactive_users) == 0:
        report += "✅ All security metrics within acceptable ranges\n"

    return report

def send_email_report(report, recipient):
    """Send report via email"""

    msg = MIMEMultipart()
    msg['From'] = "[email protected]"
    msg['To'] = recipient
    msg['Subject'] = f"Authentik Compliance Report - {datetime.now().strftime('%Y-%m-%d')}"

    msg.attach(MIMEText(report, 'plain'))

    # Configure your SMTP settings
    smtp_server = "smtp.resend.com"
    smtp_port = 587
    smtp_user = "resend"
    smtp_pass = "your-api-key"

    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()
        server.login(smtp_user, smtp_pass)
        server.send_message(msg)

    print("📧 Report sent via email")

if __name__ == "__main__":
    report = generate_compliance_report()
    print(report)

    # Optionally send via email
    # send_email_report(report, "[email protected]")

Schedule with cron:

# Create weekly report every Monday at 9 AM
0 9 * * 1 /usr/bin/python3 /opt/authentik/compliance_report.py

Use Case 3: Anomaly Detection with SIEM Integration

Export Authentik events to a SIEM (Security Information and Event Management) system for ML-based threat detection:

#!/usr/bin/env python3
"""
Export Authentik events to Splunk/ELK for analysis
"""

import requests
import json
from datetime import datetime, timedelta

AUTHENTIK_URL = "https://authentik.example.com"
API_TOKEN = "your-api-token"
SPLUNK_HEC_URL = "https://splunk.example.com:8088/services/collector"
SPLUNK_HEC_TOKEN = "your-splunk-token"

def fetch_recent_events(hours=1):
    """Fetch authentication events from last N hours"""

    headers = {"Authorization": f"Bearer {API_TOKEN}"}

    # Calculate timestamp
    since = datetime.now() - timedelta(hours=hours)

    response = requests.get(
        f"{AUTHENTIK_URL}/api/v3/events/events/",
        headers=headers,
        params={
            "created__gte": since.isoformat(),
            "ordering": "-created"
        }
    )

    return response.json()["results"]

def send_to_splunk(events):
    """Send events to Splunk HTTP Event Collector"""

    headers = {
        "Authorization": f"Splunk {SPLUNK_HEC_TOKEN}",
        "Content-Type": "application/json"
    }

    for event in events:
        payload = {
            "event": event,
            "sourcetype": "authentik:events",
            "index": "security"
        }

        response = requests.post(SPLUNK_HEC_URL, headers=headers, json=payload)

        if response.status_code != 200:
            print(f"Failed to send event: {response.text}")

if __name__ == "__main__":
    events = fetch_recent_events(hours=1)
    print(f"Fetched {len(events)} events")
    send_to_splunk(events)
    print("✅ Events exported to SIEM")

Use SIEM for:

  • Impossible travel detection (login from distant locations)
  • Brute force attack identification
  • Anomalous login patterns
  • Credential stuffing attempts
  • Unusual API token usage

Use Case 4: Webhook-Triggered Automation

Configure Authentik to send webhooks on user events:

  1. System → Webhooks → Create
  2. Configure:
    • Name: User Creation Webhook
    • URL: https://your-automation-server.com/webhook/user-created
    • Events: User Created
    • Authentication: Bearer token

Webhook Receiver (Flask Example):

from flask import Flask, request
import requests

app = Flask(__name__)

@app.route("/webhook/user-created", methods=["POST"])
def user_created():
    """Handle user creation webhook from Authentik"""

    data = request.json
    username = data["event"]["user"]["username"]
    email = data["event"]["user"]["email"]

    print(f"New user created: {username} ({email})")

    # Trigger downstream automation:
    # - Create home directory
    # - Provision email account
    # - Send welcome message
    # - Add to monitoring systems

    send_welcome_email(email, username)
    create_home_directory(username)

    return {"status": "ok"}, 200

def send_welcome_email(email, username):
    """Send automated welcome email"""
    # Implementation here
    pass

def create_home_directory(username):
    """Create user home directory on file server"""
    # Implementation here
    pass

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Section 11: Troubleshooting Common Issues

Issue 1: OAuth2 Redirect URI Mismatch

Error: redirect_uri_mismatch

Cause: The redirect URI configured in Authentik doesn’t exactly match what the application sends.

Solution:

  1. Check exact redirect URI in Authentik:

    • Applications → Your App → Provider
    • Note exact URI: https://portainer.example.com/auth/oauth/callback
  2. Compare with application configuration:

    • Verify protocol (http vs https)
    • Verify domain exactly matches
    • Verify port (if specified)
    • Verify path exactly matches
  3. Test with curl:

# Verify OAuth endpoint responds
curl -I https://authentik.example.com/application/o/authorize/

Issue 2: LDAP Sync Not Working

Symptoms: Users not syncing from Active Directory

Diagnosis:

# Test LDAP connectivity
ldapsearch -x -H ldap://ad.example.com:389   -D "cn=service-account,dc=example,dc=com"   -w password   -b "dc=example,dc=com"   "(objectClass=person)"

# Check Authentik logs
docker-compose logs authentik_worker | grep -i ldap

Solutions:

  1. Verify LDAP credentials are correct
  2. Check service account has read permissions
  3. Validate object filters match your directory
  4. Review property mappings
  5. Manually trigger sync: Directory → Sources → [Your LDAP] → Run Sync

Issue 3: Email Not Sending

Symptoms: Password reset emails not received

Diagnosis:

# Test SMTP connectivity
telnet smtp.resend.com 587

# Check Authentik logs
docker-compose logs authentik_server | grep -i smtp

Solutions:

  1. Verify SMTP credentials
  2. Check SMTP port (587 for STARTTLS, 465 for implicit TLS)
  3. Ensure firewall allows outbound SMTP
  4. Test with different SMTP provider
  5. Check spam folder

Issue 4: High Memory Usage

Symptoms: Authentik consuming excessive RAM

Solutions:

  1. Increase container limits:
# In docker-compose.yml
services:
  authentik_server:
    deploy:
      resources:
        limits:
          memory: 4G
  1. Optimize PostgreSQL:
# Connect to database
docker-compose exec postgres psql -U authentik

# Vacuum database
VACUUM ANALYZE;

# Check database size
SELECT pg_size_pretty(pg_database_size('authentik'));
  1. Archive old events:
    • Admin → System → Events
    • Set retention policy (e.g., 90 days)
    • Old events automatically deleted

Conclusion

Congratulations! You’ve built a production-ready SSO infrastructure with Authentik. You now have:

  • ✅ Centralized authentication for all your homelab services
  • ✅ Multi-factor authentication protecting your accounts
  • ✅ OAuth2/OIDC integrations with Portainer, Proxmox, and Nextcloud
  • ✅ LDAP provider for legacy applications
  • ✅ Custom authentication flows with conditional access
  • ✅ Automated user provisioning and compliance reporting
  • ✅ Advanced security policies and monitoring

Next Steps

  1. Integrate More Applications: Use the same patterns for Home Assistant, Grafana, Jellyfin, etc.
  2. Implement High Availability: Set up PostgreSQL replication and multiple Authentik instances
  3. Advanced Policies: Create time-based access, IP restrictions, and risk-based authentication
  4. Monitoring: Set up Prometheus metrics and Grafana dashboards
  5. Compliance: Automate SOC2, HIPAA, or GDPR compliance reporting

Maintenance Checklist

Weekly:

  • Review failed authentication attempts
  • Check MFA enrollment progress
  • Verify backup completion

Monthly:

  • Update Authentik to latest version
  • Review and update access policies
  • Generate compliance reports
  • Test disaster recovery procedures

Quarterly:

  • Security audit of all integrations
  • Review user accounts and permissions
  • Update documentation
  • Test all authentication flows

Resources

Final Thoughts

Authentik transforms homelab security from managing dozens of passwords to a single, secure authentication point. The initial setup investment pays dividends in improved security, user experience, and administrative efficiency.

The automation capabilities—from API-based provisioning to webhook-driven workflows—enable you to build sophisticated identity management that rivals enterprise solutions, all while maintaining complete control over your authentication infrastructure.

Your homelab is now protected by enterprise-grade authentication. Happy homelabbing!


Guide Version: 1.0 Last Updated: December 2025 Authentik Version: 2025.10+ Tested On: Ubuntu 22.04, Docker 24.0+

Author Note: This guide is maintained as part of the homelabbing.io project. Found an issue or have improvements? Contribute on GitHub!