Authentik SSO: Centralized Authentication for Your Homelab
Enterprise-grade Single Sign-On for your homelab. OAuth2, SAML, LDAP, and multi-factor authentication.
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:
| Feature | Authentik | Authelia | Keycloak |
|---|---|---|---|
| Type | Full IdP | Auth Proxy | Full IdP |
| RAM Usage | 2-4GB | 512MB | 4-8GB |
| Setup Complexity | Medium | Low | High |
| OAuth2/OIDC | ✅ Full Support | ⚠️ Limited | ✅ Full Support |
| SAML2 | ✅ Yes | ❌ No | ✅ Yes |
| LDAP Provider | ✅ Yes | ❌ No | ✅ Yes |
| RADIUS | ✅ Yes | ❌ No | ❌ No |
| Web UI Quality | Excellent | Basic | Comprehensive |
| Best For | Homelabs, SMB | Lightweight SSO | Enterprise |
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):
- Sign up for Resend: Visit https://resend.com and create a free account
- Generate API Key: In Resend dashboard, create a new API key
- Configure Domain: Add and verify your domain (or use Resend’s test domain)
- 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:
- Set Admin Password: Create a strong password (or use the bootstrap password)
- Admin Email: Enter your email address
- Instance Name: Enter a friendly name (e.g., “Homelab Authentication”)
- SMTP Settings: Configure email (or skip for now)
- 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 Section 3: Reverse Proxy Setup (Recommended)
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
Navigate to Admin Interface: Click “Admin Interface” in top right
Create Application: Go to Applications → Applications
Click “Create with Provider”
Fill in Application Details:
- Name:
Portainer - Slug:
portainer(used in URLs) - Provider Type: Select “OAuth2/OpenID Provider”
- Name:
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
- Name:
Save and Copy Credentials:
- Note the Client ID (looks like:
abcd1234efgh5678) - Note the Client Secret (click to reveal and copy)
- Note the Client ID (looks like:
Step 2: Configure Portainer
- Log into Portainer as admin
- Navigate to: Settings → Authentication
- Select: OAuth
- Choose: Custom
- 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).
- Save settings and test by logging out
Step 3: Test OAuth2 Integration
- Logout from Portainer
- Click “Authenticate with OAuth”
- Redirected to Authentik → Enter credentials
- Grant permissions (if prompted)
- 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
- Navigate to: Flows & Stages → Stages
- Click Create and select “TOTP Authenticator Setup Stage”
- Configure:
- Name:
TOTP Authenticator - Issuer:
Homelab(shown in authenticator app) - Digits:
6(standard)
- Name:
- Save
Step 2: Add TOTP to Authentication Flow
- Navigate to: Flows & Stages → Flows
- Find and Edit:
default-authentication-flow - Click “Stage Bindings”
- Click “Bind Stage”
- Select:
- Stage:
authenticator-validation-setup - Order:
25(after password validation) - Evaluate on plan:
false
- Stage:
- Click “Create”
Step 3: Configure Authentication Validation
- In same flow, find:
default-authentication-mfa-validation - Edit this stage
- Configure:
- Device classes: Select “TOTP” and “Static”
- Configuration stage: Select your “TOTP Authenticator” stage
- Save
Step 4: Test TOTP Enrollment
- Logout and login with your admin account
- You’ll be prompted: “Configure Authenticator”
- Scan QR code with authenticator app:
- Google Authenticator (iOS/Android)
- Authy (iOS/Android/Desktop)
- 1Password (if you have it)
- Microsoft Authenticator
- Enter verification code from app
- Save backup codes (print or store securely)
- 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:
Create WebAuthn Stage:
- Flows & Stages → Stages → Create
- Type: “WebAuthn Authenticator Setup Stage”
- Name:
Security Keys - User verification:
preferred
Add to Authentication Flow:
- Edit
default-authentication-flow - Add WebAuthn validation alongside TOTP
- Edit
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
- In Authentik: Applications → Create with Provider
- Application Details:
- Name:
Proxmox - Slug:
proxmox
- Name:
- Provider Configuration:
- Type: OAuth2/OpenID
- Name:
Proxmox OAuth2 - Redirect URIs:
https://proxmox.example.com/auth-verify.html?realm=authentik - Scopes:
openid profile email
- Save and copy Client ID and Secret
Step 2: Configure Proxmox OpenID Realm
Via Web UI:
- Datacenter → Permissions → Realms → Add
- Select: OpenID Connect Server
- 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
- Issuer URL:
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
- Logout from Proxmox
- On login screen, select Realm:
authentik - Click Login → Redirected to Authentik
- Enter credentials (with MFA if enabled)
- 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
- Login to Nextcloud as admin
- Apps → Search “OpenID”
- Install: “OpenID Connect Login” (by DAITA)
- Enable the app
Step 2: Create OAuth2 Provider in Authentik
- Applications → Create with Provider
- Application Details:
- Name:
Nextcloud - Slug:
nextcloud
- Name:
- Provider Configuration:
- Type: OAuth2/OpenID
- Redirect URIs:
https://nextcloud.example.com/apps/user_oidc/code - Scopes:
openid profile email
- Copy Client ID and Secret
Step 3: Configure Nextcloud OIDC
- Settings → OpenID Connect
- Click “Add provider”
- 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
- Save
Step 4: Test Nextcloud Login
- Logout from Nextcloud
- Login page shows “Login with Authentik”
- Click the button → Authenticate via Authentik
- First login creates user automatically
- 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
- Providers → Create → LDAP Provider
- 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”)
- Name:
- Generate and save Bind password
Step 2: Create LDAP Outpost
Outposts handle protocol-specific functionality:
- System → Outposts → Create
- Configure:
- Name:
LDAP Outpost - Type:
LDAP - Integration:
Docker - Provider: Select your “Internal LDAP” provider
- Name:
- 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)
- In Jellyfin: Dashboard → Plugins → Catalog
- Install: “LDAP Authentication” plugin
- 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})
- LDAP Server:
- 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
- Flows & Stages → Policies → Create → Expression Policy
- Name:
Risk Assessment - 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 < 8 or now.hour > 18 or now.weekday() > 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 - Save
Step 2: Create Conditional MFA Stage
- 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 - Name:
High Risk MFA Required
Step 3: Build Custom Flow
- Flows & Stages → Flows → Create
- Name:
Risk-Based Authentication - Designation: Authentication
- 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
- Stage 1:
Step 4: Assign Flow to Application
- Edit an application (e.g., Portainer)
- Set Authentication flow: Select “Risk-Based Authentication”
- 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:
- Edit application
- Policy Bindings tab
- Bind this policy
- 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 > 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 < 90:
report += "⚠️ MFA enrollment below 90% - Consider mandatory MFA policy\n"
if len(inactive_users) > 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:
- System → Webhooks → Create
- Configure:
- Name:
User Creation Webhook - URL:
https://your-automation-server.com/webhook/user-created - Events:
User Created - Authentication: Bearer token
- Name:
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:
Check exact redirect URI in Authentik:
- Applications → Your App → Provider
- Note exact URI:
https://portainer.example.com/auth/oauth/callback
Compare with application configuration:
- Verify protocol (http vs https)
- Verify domain exactly matches
- Verify port (if specified)
- Verify path exactly matches
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:
- Verify LDAP credentials are correct
- Check service account has read permissions
- Validate object filters match your directory
- Review property mappings
- 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:
- Verify SMTP credentials
- Check SMTP port (587 for STARTTLS, 465 for implicit TLS)
- Ensure firewall allows outbound SMTP
- Test with different SMTP provider
- Check spam folder
Issue 4: High Memory Usage
Symptoms: Authentik consuming excessive RAM
Solutions:
- Increase container limits:
# In docker-compose.yml
services:
authentik_server:
deploy:
resources:
limits:
memory: 4G - 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')); - 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
- Integrate More Applications: Use the same patterns for Home Assistant, Grafana, Jellyfin, etc.
- Implement High Availability: Set up PostgreSQL replication and multiple Authentik instances
- Advanced Policies: Create time-based access, IP restrictions, and risk-based authentication
- Monitoring: Set up Prometheus metrics and Grafana dashboards
- 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
- Official Documentation: https://docs.goauthentik.io/
- Integration Database: https://integrations.goauthentik.io/
- GitHub Repository: https://github.com/goauthentik/authentik
- Community Forums: GitHub Discussions, r/selfhosted
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!