Files
homelab-optimized/docs/troubleshooting/authentik-sso-rebuild.md
Gitea Mirror Bot 731d3b209e
Some checks failed
Documentation / Deploy to GitHub Pages (push) Has been cancelled
Documentation / Build Docusaurus (push) Has been cancelled
Sanitized mirror from private repository - 2026-03-21 09:16:04 UTC
2026-03-21 09:16:04 +00:00

18 KiB

Authentik SSO Disaster Recovery & Rebuild Guide

Last Updated: 2026-01-31
Tested On: Authentik 2024.12.x on Calypso (Synology DS723+)

This guide documents the complete process to rebuild Authentik SSO and reconfigure OAuth2 for all homelab services from scratch.


Table of Contents

  1. Prerequisites
  2. Deploy Authentik
  3. Initial Configuration
  4. Configure OAuth2 Providers
  5. Configure Forward Auth Providers
  6. Service-Specific Configuration
  7. NPM Integration
  8. Troubleshooting
  9. Recovery Procedures

Prerequisites

Infrastructure Required

  • Docker host (Calypso NAS or equivalent)
  • PostgreSQL database
  • Redis
  • Nginx Proxy Manager (NPM) for reverse proxy
  • Domain with SSL (e.g., sso.vish.gg via Cloudflare)

Network Configuration

Service Host Port
Authentik Server 192.168.0.250 9000
Authentik Worker 192.168.0.250 (internal)
PostgreSQL 192.168.0.250 5432
Redis 192.168.0.250 6379

Credentials to Have Ready

  • Admin email (e.g., admin@example.com)
  • Strong admin password
  • SMTP settings (optional, for email notifications)

Deploy Authentik

Docker Compose File

Location: hosts/synology/calypso/authentik/docker-compose.yaml

version: '3.9'

services:
  postgresql:
    image: postgres:16-alpine
    container_name: Authentik-DB
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 5s
    volumes:
      - database:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: "REDACTED_PASSWORD" password required}
      POSTGRES_USER: ${PG_USER:-authentik}
      POSTGRES_DB: ${PG_DB:-authentik}
    networks:
      - authentik

  redis:
    image: redis:alpine
    container_name: Authentik-REDIS
    command: --save 60 1 --loglevel warning
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
      - redis:/data
    networks:
      - authentik

  server:
    image: ghcr.io/goauthentik/server:2024.12
    container_name: Authentik-SERVER
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: "REDACTED_PASSWORD"
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
    ports:
      - "9000:9000"
      - "9443:9443"
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - authentik

  worker:
    image: ghcr.io/goauthentik/server:2024.12
    container_name: Authentik-WORKER
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: "REDACTED_PASSWORD"
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - authentik

volumes:
  database:
  redis:

networks:
  authentik:
    driver: bridge

Environment File (.env)

PG_PASS="REDACTED_PASSWORD"
PG_USER=authentik
PG_DB=authentik
AUTHENTIK_SECRET_KEY=<generate-with-openssl-rand-base64-60>

Generate Secret Key

openssl rand -base64 60

Deploy

cd /volume1/docker/authentik
docker-compose up -d

Verify Deployment

docker ps | grep -i authentik
# Should show: Authentik-SERVER, Authentik-WORKER, Authentik-DB, Authentik-REDIS

Initial Configuration

First-Time Setup

  1. Navigate to https://sso.vish.gg/if/flow/initial-setup/
  2. Create admin account:
    • Username: akadmin
    • Email: admin@example.com
    • Password: (use password manager)

Post-Setup Configuration

  1. Admin Interface: https://sso.vish.gg/if/admin/
  2. User Portal: https://sso.vish.gg/if/user/

Navigate to: Admin → Directory → Groups

Group Name Purpose
Grafana Admins Admin access to Grafana
Grafana Editors Editor access to Grafana
Homelab Users General homelab access

Configure OAuth2 Providers

Critical: Scope Mappings

EVERY OAuth2 provider MUST have these scope mappings configured, or logins will fail with "InternalError":

  1. Go to: Admin → Customization → Property Mappings
  2. Note these default mappings exist:
    • authentik default OAuth Mapping: OpenID 'openid'
    • authentik default OAuth Mapping: OpenID 'email'
    • authentik default OAuth Mapping: OpenID 'profile'

When creating providers, you MUST add these to the "Scopes" field.

Provider 1: Grafana OAuth2

Admin → Providers → Create → OAuth2/OpenID Provider

Setting Value
Name Grafana OAuth2
Authentication flow default-authentication-flow
Authorization flow default-provider-authorization-implicit-consent
Client type Confidential
Client ID (auto-generated, save this)
Client Secret (auto-generated, save this)
Redirect URIs https://gf.vish.gg/login/generic_oauth
Signing Key authentik Self-signed Certificate
Scopes Select: openid, email, profile ⚠️ CRITICAL

Create Application:

  • Admin → Applications → Create
  • Name: Grafana
  • Slug: grafana
  • Provider: Grafana OAuth2
  • Launch URL: https://gf.vish.gg

Provider 2: Gitea OAuth2

Admin → Providers → Create → OAuth2/OpenID Provider

Setting Value
Name Gitea OAuth2
Authorization flow default-provider-authorization-implicit-consent
Client type Confidential
Redirect URIs https://git.vish.gg/user/oauth2/authentik/callback
Scopes Select: openid, email, profile ⚠️ CRITICAL

Create Application:

  • Name: Gitea
  • Slug: gitea
  • Provider: Gitea OAuth2
  • Launch URL: https://git.vish.gg

Provider 3: Portainer OAuth2

Admin → Providers → Create → OAuth2/OpenID Provider

Setting Value
Name Portainer OAuth2
Authorization flow default-provider-authorization-implicit-consent
Client type Confidential
Redirect URIs http://vishinator.synology.me:10000
Scopes Select: openid, email, profile ⚠️ CRITICAL

Create Application:

  • Name: Portainer
  • Slug: portainer
  • Provider: Portainer OAuth2
  • Launch URL: http://vishinator.synology.me:10000

Provider 4: Seafile OAuth2

Admin → Providers → Create → OAuth2/OpenID Provider

Setting Value
Name Seafile OAuth2
Authorization flow default-provider-authorization-implicit-consent
Client type Confidential
Redirect URIs https://sf.vish.gg/oauth/callback/
Scopes Select: openid, email, profile ⚠️ CRITICAL

Create Application:

  • Name: Seafile
  • Slug: seafile
  • Launch URL: https://sf.vish.gg

Configure Forward Auth Providers

Forward Auth is used for services that don't have native OAuth support. Authentik intercepts all requests and requires login first.

Provider: vish.gg Domain Forward Auth

Admin → Providers → Create → Proxy Provider

Setting Value
Name vish.gg Domain Forward Auth
Authorization flow default-provider-authorization-implicit-consent
Mode Forward auth (single application)
External host https://sso.vish.gg

Create Application:

  • Name: vish.gg Domain Auth
  • Slug: vishgg-domain-auth
  • Provider: vish.gg Domain Forward Auth

Create/Update Outpost

Admin → Applications → Outposts

  1. Edit the embedded outpost (or create one)
  2. Add all Forward Auth applications to it
  3. The outpost will listen on port 9000

Service-Specific Configuration

Grafana Configuration

Environment variables (in docker-compose or Portainer):

environment:
  # OAuth2 SSO
  - GF_AUTH_GENERIC_OAUTH_ENABLED=true
  - GF_AUTH_GENERIC_OAUTH_NAME=Authentik
  - GF_AUTH_GENERIC_OAUTH_CLIENT_ID=<client_id>
  - GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=<client_secret>
  - GF_AUTH_GENERIC_OAUTH_SCOPES=openid profile email
  - GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://sso.vish.gg/application/o/authorize/
  - GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://sso.vish.gg/application/o/token/
  - GF_AUTH_GENERIC_OAUTH_API_URL=https://sso.vish.gg/application/o/userinfo/
  - GF_AUTH_SIGNOUT_REDIRECT_URL=https://sso.vish.gg/application/o/grafana/end-session/
  
  # CRITICAL: Attribute paths
  - GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_PATH=email
  - GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH=preferred_username
  - GF_AUTH_GENERIC_OAUTH_NAME_ATTRIBUTE_PATH=name
  
  # Role mapping
  - GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(groups[*], 'Grafana Admins') && 'Admin' || contains(groups[*], 'Grafana Editors') && 'Editor' || 'Viewer'
  
  # Additional settings
  - GF_AUTH_GENERIC_OAUTH_USE_PKCE=true
  - GF_AUTH_GENERIC_OAUTH_ALLOW_ASSIGN_GRAFANA_ADMIN=true
  - GF_SERVER_ROOT_URL=https://gf.vish.gg

Gitea Configuration

Configure via Site Administration → Authentication Sources → Add OAuth2:

Setting Value
Authentication Name authentik
OAuth2 Provider OpenID Connect
Client ID (from Authentik)
Client Secret (from Authentik)
OpenID Connect Auto Discovery URL https://sso.vish.gg/application/o/gitea/.well-known/openid-configuration

Portainer Configuration

Configure via Settings → Authentication → OAuth:

Setting Value
Client ID (from Authentik)
Client Secret (from Authentik)
Authorization URL https://sso.vish.gg/application/o/authorize/
Access Token URL https://sso.vish.gg/application/o/token/
Resource URL https://sso.vish.gg/application/o/userinfo/
Redirect URL http://vishinator.synology.me:10000
User Identifier email
Scopes openid profile email

Seafile Configuration

Add to /volume1/docker/seafile/data/seafile/conf/seahub_settings.py:

ENABLE_OAUTH = True
OAUTH_ENABLE_INSECURE_TRANSPORT = False
OAUTH_CLIENT_ID = "<client_id>"
OAUTH_CLIENT_SECRET = "<client_secret>"
OAUTH_REDIRECT_URL = "https://sf.vish.gg/oauth/callback/"
OAUTH_PROVIDER_DOMAIN = "sso.vish.gg"
OAUTH_AUTHORIZATION_URL = "https://sso.vish.gg/application/o/authorize/"
OAUTH_TOKEN_URL = "https://sso.vish.gg/application/o/token/"
OAUTH_USER_INFO_URL = "https://sso.vish.gg/application/o/userinfo/"
OAUTH_SCOPE = ["openid", "profile", "email"]
OAUTH_ATTRIBUTE_MAP = {
    "email": (True, "email"),
    "name": (False, "name"),
}

Then restart Seafile: docker restart Seafile


NPM Integration

For OAuth2 Services (Grafana, Gitea, etc.)

DO NOT add Forward Auth config! These services handle OAuth themselves.

NPM proxy host should be simple:

  • Forward host: service IP
  • Forward port: service port
  • SSL: enabled
  • Advanced config: EMPTY

For Forward Auth Services (Paperless, Actual, etc.)

Add this to NPM Advanced Config:

# Authentik Forward Auth Configuration
proxy_buffers 8 16k;
proxy_buffer_size 32k;

auth_request /outpost.goauthentik.io/auth/nginx;
error_page 401 = @goauthentik_proxy_signin;

auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;

auth_request_set $authentik_username $upstream_http_x_authentik_username;
auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
auth_request_set $authentik_email $upstream_http_x_authentik_email;
auth_request_set $authentik_name $upstream_http_x_authentik_name;
auth_request_set $authentik_uid $upstream_http_x_authentik_uid;

proxy_set_header X-authentik-username $authentik_username;
proxy_set_header X-authentik-groups $authentik_groups;
proxy_set_header X-authentik-email $authentik_email;
proxy_set_header X-authentik-name $authentik_name;
proxy_set_header X-authentik-uid $authentik_uid;

location /outpost.goauthentik.io {
    proxy_pass http://192.168.0.250:9000/outpost.goauthentik.io;
    proxy_set_header Host $host;
    proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
    add_header Set-Cookie $auth_cookie;
    auth_request_set $auth_cookie $upstream_http_set_cookie;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}

location @goauthentik_proxy_signin {
    internal;
    add_header Set-Cookie $auth_cookie;
    return 302 https://sso.vish.gg/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}

Services with Forward Auth Configured

Domain Backend Port
paperless.vish.gg 192.168.0.250 8777
docs.vish.gg 192.168.0.250 8777
actual.vish.gg 192.168.0.250 8304
npm.vish.gg 192.168.0.250 81

Troubleshooting

"InternalError" After OAuth Login

Root Cause: Missing scope mappings in Authentik provider.

Fix:

  1. Admin → Providers → Edit the OAuth2 provider
  2. Scroll to "Scopes" section
  3. Add: openid, email, profile
  4. Save

Verify:

curl https://sso.vish.gg/application/o/<app-slug>/.well-known/openid-configuration | jq '.scopes_supported'

Redirect Loop Between Service and Authentik

Root Cause: Forward Auth configured in NPM for a service that uses native OAuth.

Fix:

  1. NPM → Proxy Hosts → Edit the affected host
  2. Go to Advanced tab
  3. Clear all content from the Advanced Config box
  4. Save

"User not found" or "No email" Errors

Root Cause: Missing attribute paths in service config.

Fix for Grafana:

GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_PATH=email
GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH=preferred_username

OAuth Works But User Gets Wrong Permissions

Root Cause: Missing group claim or incorrect role mapping.

Fix:

  1. Ensure user is in correct Authentik group
  2. Verify groups scope is included
  3. Check role mapping expression in service config

Can't Access Authentik Admin

Create recovery token via Portainer or SSH:

docker exec -it Authentik-SERVER ak create_recovery_key 10 akadmin

This generates a one-time URL valid for 10 minutes.


Recovery Procedures

Scenario: Complete Authentik Loss

  1. Restore from backup (if available):

    # Restore PostgreSQL database
    docker exec -i Authentik-DB psql -U authentik authentik < backup.sql
    
    # Restore media files
    rsync -av backup/media/ /volume1/docker/authentik/media/
    
  2. Or redeploy from scratch:

    • Follow this entire guide from Deploy Authentik
    • You'll need to reconfigure all OAuth providers
    • Services will need their OAuth credentials updated

Scenario: Locked Out of Admin

# Via SSH to Calypso or Portainer exec
docker exec -it Authentik-SERVER ak create_recovery_key 10 akadmin

Navigate to the URL it outputs.

Scenario: Service OAuth Broken After Authentik Rebuild

  1. Create new OAuth2 provider in Authentik (same settings)
  2. Note new Client ID and Secret
  3. Update service configuration with new credentials
  4. Restart service
  5. Test login

Scenario: Forward Auth Not Working

  1. Verify Authentik outpost is running:

    docker logs Authentik-SERVER | grep -i outpost
    
  2. Verify outpost includes the application:

    • Admin → Outposts → Edit → Check application is selected
  3. Test outpost endpoint:

    curl -I http://192.168.0.250:9000/outpost.goauthentik.io/ping
    
  4. Check NPM Advanced Config has correct Authentik IP


Quick Reference

Authentik Endpoints

Endpoint URL
Admin UI https://sso.vish.gg/if/admin/
User Portal https://sso.vish.gg/if/user/
Authorization https://sso.vish.gg/application/o/authorize/
Token https://sso.vish.gg/application/o/token/
User Info https://sso.vish.gg/application/o/userinfo/
OpenID Config https://sso.vish.gg/application/o/<slug>/.well-known/openid-configuration
End Session https://sso.vish.gg/application/o/<slug>/end-session/

Service Status Checklist

After rebuilding, verify each service:

# OAuth2 Services
curl -sI https://gf.vish.gg | head -1      # Should be 302
curl -sI https://git.vish.gg | head -1     # Should be 200
curl -sI https://sf.vish.gg | head -1      # Should be 302

# Forward Auth Services
curl -sI https://paperless.vish.gg | head -1  # Should be 302 to SSO
curl -sI https://actual.vish.gg | head -1     # Should be 302 to SSO

# Authentik itself
curl -sI https://sso.vish.gg | head -1     # Should be 302

Change Log

  • 2026-01-31: Initial creation based on live rebuild/verification session
  • 2026-01-31: Documented scope mappings fix (critical for OAuth2)
  • 2026-01-31: Added NPM Forward Auth vs OAuth2 distinction
  • 2026-01-31: Added all service-specific configurations