Identity & Access Management

Enterprise SSO Implementation

Completion: February 2026
Time Investment: ~15 hours
Status: Production-Ready
TL;DR - Why This Matters

Built enterprise-grade Single Sign-On protecting multiple applications using forward authentication at the reverse proxy layer. This pattern works with ANY web application - even those without native SSO support - by intercepting requests at the network edge and validating sessions before proxying to backends.

Key Achievement: Universal authentication pattern that eliminated per-application credential management and provided centralized access control.

The Problem

Initial State

Business Impact

Enterprise Parallel: This mirrors the challenge companies face when they have dozens of SaaS apps (Salesforce, Jira, GitHub, etc.) - without SSO, users create weak passwords and IT has no centralized control.

The Solution

Architecture Overview

┌─────────────┐
│  End User   │
│   Browser   │
└──────┬──────┘
       │ 1. HTTPS request
       ▼
┌─────────────────────────┐
│  Nginx Proxy Manager    │ ← SSL termination, initial request
│  (Reverse Proxy)        │
└──────┬──────────────────┘
       │ 2. auth_request (nginx subrequest)
       ▼
┌─────────────────────────┐
│  Authentik Outpost      │ ← Session validation
│  (Forward Auth)         │
└──────┬──────────────────┘
       │ 3. If no session → redirect to login
       │    If valid session → return 200 + headers
       ▼
┌─────────────────────────┐
│  Authentik Server       │ ← Login flows, LDAP queries
│  (Identity Provider)    │
└──────┬──────────────────┘
       │ 4. LDAP authentication
       ▼
┌─────────────────────────┐
│  Active Directory       │ ← User/group directory
│  (Identity Source)      │
└─────────────────────────┘

How It Works (Step-by-Step)

Unauthenticated Request:

  1. User navigates to https://docs.homelab.local
  2. Nginx receives request, makes internal auth_request to Authentik outpost
  3. Outpost checks for valid session cookie → None found → Returns 401 Unauthorized
  4. Nginx catches the 401, redirects browser to Authentik login page
  5. User enters credentials (validated against Active Directory via LDAP)
  6. Authentik creates session, sets cookie, redirects back to original URL
  7. Request repeats (now with session cookie) → Outpost validates → Returns 200 OK
  8. Nginx proxies request to backend application with user identity headers
  9. User sees the protected application

Authenticated Request:

Technical Implementation

Component Stack

Identity Layer:

Network Layer:

Application Layer:

Key Technical Decisions

Why Forward Auth vs Native OIDC/SAML
  • Forward auth works with any HTTP(S) application (no app-level changes needed)
  • Application never sees credentials (security)
  • Centralized at network edge (defense in depth)
  • Single pattern scales to unlimited apps
  • Trade-off: Double authentication if app has native auth (acceptable in homelab)
Why LDAP + StartTLS vs LDAPS
  • StartTLS uses standard port 389 (no firewall rule changes)
  • Encrypts after initial connection (opportunistic TLS)
  • Simpler certificate management for internal CA
  • Enterprise standard for internal LDAP
Why Distributed Authentik (Server + Outpost)
  • Outpost co-located with applications (low latency auth checks)
  • Server handles heavy lifting (login flows, LDAP queries, UI)
  • Separation of concerns (auth validation vs identity management)
  • Mirrors enterprise IdP architecture (e.g., Okta agents)

The Challenges

Challenge 1: LDAP Bind Authentication Failure

Symptom:

Error initializing SSL/TLS
LDAP bind failed with error: Invalid credentials

Root Cause Discovery:

  • Initially used User Principal Name (UPN) format: svc-authentik@homelab.local
  • LDAP bind operations require Distinguished Name (DN) format
  • UPN is Windows/Kerberos-specific, not universal LDAP

Investigation Process:

  1. Verified service account password in AD (correct)
  2. Tested network connectivity to DC (reachable)
  3. Read Microsoft LDAP documentation on bind requirements
  4. Discovered DN vs UPN distinction
  5. Changed bind credential format

Solution:

❌ Wrong: svc-authentik@homelab.local (UPN)
✅ Correct: CN=svc-authentik,CN=Users,DC=homelab,DC=local (DN)
Why This Matters: This is a common integration pitfall. Many authentication systems accept UPN for user login convenience, but backend LDAP bind operations are protocol-specific and require DN format. Understanding this distinction is critical for troubleshooting any LDAP-based integration.
Challenge 2: nginx 500 Errors (auth_request Status Codes)

Symptom:

500 Internal Server Error
nginx error log: "auth request unexpected status: 302"

Root Cause:

  • nginx auth_request directive expects either 2xx (authenticated) or 401/403 (not authenticated)
  • Authentik outpost was returning 302 Redirect for unauthenticated users
  • nginx treats unexpected status codes as errors

Solution:

Added error_page handler to nginx configuration:

location / {
    auth_request /outpost.goauthentik.io/auth/nginx;
    error_page 401 = @goauthentik_proxy_signin;  # Catch 401, redirect gracefully
    # ... rest of config
}

location @goauthentik_proxy_signin {
    internal;
    return 302 /outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}
Why This Matters: nginx's auth_request is powerful but opinionated. Understanding its behavior model (status code expectations, subrequest mechanics, error handling) is essential for implementing reverse proxy authentication patterns.
Challenge 3: Authentication Loop (Cookie Management)

Symptom:

User successfully authenticated in Authentik, but kept getting redirected back to login page infinitely.

Root Cause:

  • Authentik session cookie was being set with SameSite=Strict
  • Redirect from Authentik back to protected app was cross-domain (different subdomain)
  • Browser blocked cookie transmission due to SameSite policy
  • Each request appeared unauthenticated, triggering new login redirect

Solution:

Changed Authentik cookie settings to SameSite=Lax:

  • Allows cookies on same-site navigation (subdomain redirects)
  • Still protects against CSRF attacks
  • Balances security and functionality
Why This Matters: Modern browsers enforce strict cookie policies for security. Understanding SameSite attributes (None, Lax, Strict) is essential when implementing authentication flows that involve redirects across subdomains.

Outcomes & Impact

Measurable Results

Skills Demonstrated

Enterprise Identity Architecture

  • LDAP protocol and directory services
  • Modern IdP patterns (Okta, Auth0, Azure AD)
  • Forward auth vs native SSO trade-offs
  • Distributed system design

Authentication Protocols

  • LDAP bind operations (DN vs UPN)
  • TLS/SSL for LDAP traffic (StartTLS)
  • Session management and cookies
  • OAuth2/OIDC concepts

Reverse Proxy Engineering

  • nginx auth_request subrequest model
  • HTTP status code handling
  • Header manipulation
  • Buffer sizing for large headers

Systematic Troubleshooting

  • Log analysis across distributed systems
  • Protocol-level debugging
  • Root cause analysis
  • Documentation-driven problem solving

Reusable Pattern

The power of this implementation is the reusable pattern. Adding SSO to a new application takes ~5 minutes:

  1. In Authentik: Create Proxy Provider + Application (3 clicks)
  2. In Authentik: Assign application to outpost (1 click)
  3. In NPM: Add auth_request location blocks to proxy host (paste config)
  4. Test: Access app → redirect to login → authenticate → access granted

This pattern has been applied to:

  • Documentation platform (Bookstack)
  • Dashboard (Glance)
  • Web IDE (code-server)
  • Monitoring (Uptime Kuma)
  • Backup tool (Duplicati)

And can protect ANY future HTTP(S) application without modification.

Interview Story (STAR Format)

Situation

"I was managing a homelab with 8 self-hosted applications, each with independent user management. This created password fatigue, security risks from reused credentials, and no centralized access control or audit trail."

Task

"Implement enterprise-grade Single Sign-On that would work across all applications - including those without native SSO support - while maintaining security and providing a foundation for future access policies."

Action

"I implemented forward authentication using Authentik as the identity provider, backed by Active Directory as the user directory. The architecture places authentication at the reverse proxy layer, so every HTTP request is validated before reaching the application. This required:

  • Setting up a 2-DC Active Directory environment with LDAP
  • Deploying Authentik with a distributed architecture (main server + outpost)
  • Configuring nginx to intercept requests and validate sessions via subrequests
  • Troubleshooting LDAP bind failures (DN vs UPN format issue)
  • Debugging nginx auth_request status code handling
  • Resolving session cookie propagation through proxy layers

The key technical challenges were understanding LDAP protocol requirements, nginx's auth_request model, and HTTP cookie flow through multi-layer proxies."

Result

"Successfully implemented SSO protecting 8 applications with a reusable pattern that takes ~5 minutes to apply to new apps. Eliminated password sprawl, centralized authentication logging, and built a foundation for future enhancements like MFA and group-based access policies. The implementation mirrors enterprise patterns used by companies like Cloudflare Access or Okta + reverse proxy combinations."

What's Next

Immediate Improvements

Future Exploration