Enterprise SSO Implementation
Set up a single sign-on system so I only need one password to access all my homelab applications. Instead of managing 8 different passwords, I log in once and everything just works. The system checks Active Directory (like what companies use for employee logins), then lets me through to whichever app I'm trying to use.
The Problem
What I Started With
- 8+ applications running in my homelab (documentation wiki, dashboard, code editor, etc.)
- Each app had its own login page and password
- I was either reusing passwords (bad security) or forgetting which password went with which app
- No way to see who accessed what, or block access globally if needed
Why This Sucks
- Security risk: Weak passwords get reused, or you write them down
- Time waste: Managing users in 8 different places
- No control: Can't revoke someone's access to everything at once
- Annoying: Logging in 8 times to check 8 things
The Solution
How It Works (The Simple Version)
Think of it like a security checkpoint at an airport:
You → Security Gate → Plane
If you have a boarding pass (session cookie):
Security waves you through to your gate
If you don't have a boarding pass:
Security redirects you to check-in
→ You show your ID (Active Directory login)
→ Get boarding pass (session cookie)
→ Now you can access all gates (apps)
What Actually Happens Behind the Scenes
First Time Visiting an App:
- You type in the URL:
https://docs.homelab.local - Reverse proxy (like a security guard) stops you: "Show me your session cookie"
- You don't have one yet, so it redirects you to the login page
- You enter your username/password (which gets checked against Active Directory)
- Login works → You get a session cookie → Redirected back to the app
- Now the security guard sees your cookie and lets you through
Every Time After That:
- Your browser automatically sends the session cookie with each request
- Security guard checks cookie → Valid → Lets you through
- You don't see any of this - the app just loads instantly
The Tech Stack
- Active Directory: 2 servers storing users/passwords (like a company directory)
- Authentik: The login page and session manager
- Nginx Proxy Manager: The security gate that checks cookies before letting you through
- Docker: All these pieces run in containers on my server
What I Actually Built
The Main Pieces
Active Directory (The User Database)
- Set up 2 domain controllers for redundancy (if one goes down, the other keeps working)
- This is where all usernames and passwords live
- Same tech that companies use for Windows logins
Authentik (The Login System)
- The pretty login page you see when you need to sign in
- Talks to Active Directory to check if your password is correct
- Creates and manages session cookies
Nginx Proxy Manager (The Security Gate)
- Sits in front of all applications
- Checks every request: "Do you have a valid session?"
- If yes → Let you through. If no → Send you to login.
Docker (How It All Runs)
- Each component runs in its own container (isolated environment)
- Containers talk to each other over an internal network
- Easy to update or restart individual pieces without breaking everything
Key Technical Decisions
- 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)
- Performance isolation (auth validation vs login flows, LDAP queries, UI)
- Separation of concerns (auth validation vs identity management)
- Mirrors enterprise IdP architecture (e.g., Okta agents)
The Challenges
What Went Wrong:
Error: LDAP bind failed - Invalid credentials
The Problem:
- I was using email format:
admin@domain.local - LDAP doesn't understand email format - it needs a specific path
- Required format:
CN=Admin,OU=Users,DC=domain,DC=local
The Fix:
Used the full Distinguished Name (DN) path instead of email/UPN format.
Lesson Learned:
LDAP has its own addressing format. Email-style usernames won't work for bind operations.
What Went Wrong:
401 Unauthorized (even after successful login)
The Problem:
- nginx's
auth_requestonly accepts 2xx as "allow" - Authentik was returning 401 for "not logged in" (correct)
- But nginx wasn't properly handling the redirect flow
The Fix:
Configured proper error_page 401 = @authentik handling and set up the redirect location block correctly.
Lesson Learned:
HTTP status codes have specific meanings in auth flows. Need to understand how nginx interprets subrequest responses.
What Went Wrong:
Login works, but redirected back to login immediately
The Problem:
- Cookies have domain restrictions
- Cookie set for
auth.domain.localwasn't being sent toapp.domain.local - Browser security feature, not a bug
The Fix:
Configured Authentik to set cookies for the parent domain .domain.local so they're sent to all subdomains.
Lesson Learned:
When login involves redirects between subdomains, you have to understand cookie policies. This took reading browser documentation and testing different settings.
Results
- 8+ applications protected with centralized SSO
- Zero password sprawl - users authenticate once with AD credentials
- 5-minute onboarding for new applications (reusable pattern)
- 100% authentication success rate since deployment
- All authentication events logged centrally in Authentik
Reusable Pattern
Adding SSO to a new application takes ~5 minutes:
- In Authentik: Create Proxy Provider + Application (3 clicks)
- In Authentik: Assign application to outpost (1 click)
- In NPM: Add
auth_requestlocation blocks to proxy host (paste config) - 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)
"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."
"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."
"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."
"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."
Skills Demonstrated
Enterprise Identity Architecture
Reverse Proxy Engineering
Systematic Troubleshooting
What's Next
Immediate Improvements
- MFA: Enable TOTP or WebAuthn for second factor
- LDAPS: Migrate from StartTLS to dedicated LDAPS port 636
- Monitoring: Centralize auth logs in Loki/Grafana
- Group Policies: Implement fine-grained access control per app
Future Exploration
- User Self-Service: Password reset portal via Authentik
- Device Trust: Integrate device posture checks
- OAuth2 Device Flow: CLI tool authentication
- SAML Integration: Explore SAML for apps that don't support OIDC