Files
rick-infra/docs/authentication-architecture.md
Joakim e8b76c6a72 Update authentication documentation to reflect OAuth/OIDC as primary method
- Update architecture-decisions.md: Change decision to OAuth/OIDC primary, forward auth fallback
  - Add comprehensive OAuth/OIDC and forward auth flow diagrams
  - Add decision matrix comparing both authentication methods
  - Include real examples: Nextcloud/Gitea OAuth configs, whoami forward auth
  - Update rationale to emphasize OAuth/OIDC security and standards benefits

- Update authentication-architecture.md: Align with new OAuth-first approach
  - Add 'Choosing the Right Pattern' section with clear decision guidance
  - Swap pattern order: OAuth/OIDC (Pattern 1), Forward Auth (Pattern 2)
  - Update Example 1: Change Gitea from forward auth to OAuth/OIDC integration
  - Add emphasis on primary vs fallback methods throughout

- Update authentik-deployment-guide.md: Reflect OAuth/OIDC preference
  - Update overview to mention OAuth2/OIDC provider and forward auth fallback
  - Add decision guidance to service integration examples
  - Reorder examples: Nextcloud OAuth (primary), forward auth (fallback)
  - Clarify forward auth should only be used for services without OAuth support

This update ensures all authentication documentation consistently reflects the
agreed architectural decision: use OAuth/OIDC when services support it
(Nextcloud, Gitea, modern apps), and only use forward auth as a fallback for
legacy applications, static sites, or simple tools without OAuth capabilities.
2025-12-15 00:25:24 +01:00

30 KiB

Authentication Architecture

This document describes the comprehensive authentication and authorization strategy implemented in rick-infra, focusing on centralized SSO with Authentik and forward authentication integration.

Overview

Rick-infra implements a modern, security-focused authentication architecture that provides:

  • Centralized SSO: Single sign-on across all services via Authentik
  • OAuth2/OIDC Integration: Industry-standard authentication for services that support it (primary method)
  • Forward Authentication: Transparent protection for legacy applications and services without OAuth support (fallback method)
  • Zero Network Exposure: Database and cache communication via Unix sockets
  • Granular Authorization: Fine-grained access control through groups and policies
  • Standards Compliance: OAuth2, OIDC, SAML support for enterprise integration

Architecture Components

┌─────────────────────────────────────────────────────────────────┐
│ rick-infra Authentication Architecture                          │
│                                                                 │
│ ┌─────────────┐    ┌─────────────┐    ┌─────────────────────┐   │
│ │    Users    │    │   Caddy     │    │     Authentik       │   │
│ │             │───▶│  (Proxy)    │───▶│   (Auth Server)     │   │ 
│ │ Web Browser │    │ Forward Auth│    │ OAuth2/OIDC/SAML    │   │
│ │ Mobile Apps │    │ TLS Term    │    │ User Management     │   │
│ │ API Clients │    │ Load Balance│    │ Policy Engine       │   │
│ └─────────────┘    └─────────────┘    └─────────────────────┘   │
│                            │                       │           │
│                            ▼                       ▼           │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Protected Services                                          │ │
│ │                                                             │ │
│ │ ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐   │ │
│ │ │    Gitea    │  │   Gallery   │  │   Custom Services   │   │ │
│ │ │ (Git Repos) │  │   (Media)   │  │  (Applications)     │   │ │
│ │ │ Receives:   │  │ Receives:   │  │   Receives:         │   │ │
│ │ │ Remote-User │  │ Remote-User │  │   Remote-User       │   │ │
│ │ │ Remote-Email│  │ Remote-Name │  │   Remote-Groups     │   │ │
│ │ └─────────────┘  └─────────────┘  └─────────────────────┘   │ │
│ └─────────────────────────────────────────────────────────────┘ │
│                                                                 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Infrastructure (Unix Sockets)                               │ │
│ │ PostgreSQL Database • Valkey Cache • systemd Services      │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Authentication Flow

Standard User Authentication Flow

sequenceDiagram
    participant U as User
    participant C as Caddy
    participant A as Authentik
    participant S as Protected Service
    
    U->>C: GET /dashboard
    C->>A: Forward Auth Request
    A->>C: 401 Unauthorized
    C->>U: 302 Redirect to Login
    
    U->>A: Login Form Request
    A->>U: Login Form
    U->>A: Credentials
    A->>U: Set Session Cookie
    
    U->>C: GET /dashboard (with cookie)
    C->>A: Forward Auth Request (with cookie)
    A->>C: 200 OK + User Headers
    C->>S: GET /dashboard + Headers
    S->>C: Dashboard Content
    C->>U: Dashboard Content

API Authentication Flow

sequenceDiagram
    participant API as API Client
    participant A as Authentik
    participant C as Caddy
    participant S as API Service
    
    API->>A: POST /application/o/token/
    Note over API,A: OAuth2 Client Credentials
    A->>API: Access Token
    
    API->>C: GET /api/data
    Note over API,C: Authorization: Bearer {token}
    C->>A: Token Validation
    A->>C: Token Valid + Claims
    C->>S: GET /api/data + Headers
    S->>API: API Response

Service Integration Patterns

Choosing the Right Pattern

Use OAuth2/OIDC (Primary Method) when:

  • Your service/application natively supports OAuth2/OIDC
  • Examples: Nextcloud, Gitea, Grafana, modern web applications
  • Provides better security, standard protocols, and proper token management

Use Forward Authentication (Fallback Method) when:

  • ⚠️ Service doesn't support OAuth2/OIDC integration
  • ⚠️ Legacy applications that cannot be modified
  • ⚠️ Static sites requiring authentication
  • ⚠️ Simple internal tools without authentication capabilities

Pattern 1: OAuth2/OIDC Integration (Primary Method)

Use Case: Applications with native OAuth2/OIDC support (Nextcloud, Gitea, Grafana, etc.)

Benefits:

  • Industry-standard authentication protocol (RFC 6749, RFC 7636)
  • Secure token-based authentication with JWT
  • Proper session management with refresh tokens
  • Native application integration
  • Support for API access tokens
  • Better logout handling and token refresh

Implementation:

# Example OAuth2 configuration
OAUTH2_CONFIG = {
    'client_id': 'your-client-id',
    'client_secret': 'your-client-secret',
    'server_metadata_url': 'https://auth.jnss.me/application/o/your-app/.well-known/openid_configuration',
    'client_kwargs': {
        'scope': 'openid email profile groups'
    }
}

# OAuth2 flow implementation
from authlib.integrations.flask_client import OAuth

oauth = OAuth(app)
oauth.register('authentik', **OAUTH2_CONFIG)

@app.route('/login')
def login():
    redirect_uri = url_for('callback', _external=True)
    return oauth.authentik.authorize_redirect(redirect_uri)

@app.route('/callback')
def callback():
    token = oauth.authentik.authorize_access_token()
    user_info = oauth.authentik.parse_id_token(token)
    # Store user info in session
    session['user'] = user_info
    return redirect('/dashboard')

Real-World Example: See Example 1 below for Gitea OAuth/OIDC configuration.


Pattern 2: Forward Authentication (Fallback Method)

Use Case: Existing HTTP services that don't support OAuth2/OIDC

Benefits:

  • No application code changes required
  • Consistent authentication across all services
  • Service receives authenticated user information via headers
  • Centralized session management
  • Works with any HTTP application

Limitations:

  • ⚠️ Non-standard authentication approach
  • ⚠️ All requests must flow through authenticating proxy
  • ⚠️ Limited logout functionality
  • ⚠️ Backend must trust proxy-provided headers

Implementation:

# Caddy configuration
myservice.jnss.me {
    forward_auth https://auth.jnss.me {
        uri /outpost.goauthentik.io/auth/caddy
        copy_headers Remote-User Remote-Name Remote-Email Remote-Groups
    }
    
    reverse_proxy localhost:8080
}

Service Code Example (Python Flask):

from flask import Flask, request

app = Flask(__name__)

@app.route('/dashboard')
def dashboard():
    # Authentication handled by Caddy/Authentik
    username = request.headers.get('Remote-User')
    user_name = request.headers.get('Remote-Name')
    user_email = request.headers.get('Remote-Email')
    user_groups = request.headers.get('Remote-Groups', '').split(',')
    
    # Authorization based on groups
    if 'admins' not in user_groups:
        return "Access denied", 403
        
    return f"Welcome {user_name} ({username})"

Real-World Example: See Example 3 below for static site protection with forward auth.


Pattern 3: API-Only Authentication

Use Case: REST APIs, mobile app backends, microservices

Benefits:

  • Stateless authentication via tokens
  • Machine-to-machine authentication support
  • Fine-grained API scope control
  • Easy integration with mobile/SPA applications

Implementation:

# Authentik Provider Configuration
name: "API Provider"
authorization_flow: "default-provider-authorization-explicit-consent"
client_type: "confidential"
client_id: "api-client-id"
redirect_uris: ["http://localhost:8080/callback"]
# API service with token validation
import requests
from flask import Flask, request, jsonify

def validate_token(token):
    """Validate token with Authentik introspection endpoint"""
    response = requests.post(
        'https://auth.jnss.me/application/o/introspect/',
        headers={'Authorization': f'Bearer {token}'},
        data={'token': token}
    )
    return response.json() if response.status_code == 200 else None

@app.route('/api/data')
def api_data():
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({'error': 'Missing or invalid authorization header'}), 401
    
    token = auth_header.split(' ')[1]
    token_info = validate_token(token)
    
    if not token_info or not token_info.get('active'):
        return jsonify({'error': 'Invalid or expired token'}), 401
    
    # Extract user information from token
    username = token_info.get('username')
    scope = token_info.get('scope', '').split()
    
    if 'read:data' not in scope:
        return jsonify({'error': 'Insufficient permissions'}), 403
    
    return jsonify({'message': f'Hello {username}', 'data': 'sensitive-data'})

Pattern 4: Service-to-Service Authentication

Use Case: Backend services communicating with each other

Benefits:

  • Machine-to-machine authentication
  • Service identity and authorization
  • API rate limiting and monitoring
  • Secure inter-service communication

Implementation:

# Authentik Service Account Configuration
name: "Backend Service Account"
username: "backend-service"
groups: ["services", "backend-access"]
token_validity: "8760h"  # 1 year
# Service-to-service authentication
import os
import requests

class ServiceClient:
    def __init__(self):
        self.client_id = os.getenv('SERVICE_CLIENT_ID')
        self.client_secret = os.getenv('SERVICE_CLIENT_SECRET')
        self.token_url = 'https://auth.jnss.me/application/o/token/'
        self.access_token = None
    
    def get_access_token(self):
        if not self.access_token:
            response = requests.post(self.token_url, data={
                'grant_type': 'client_credentials',
                'client_id': self.client_id,
                'client_secret': self.client_secret,
                'scope': 'service:read service:write'
            })
            self.access_token = response.json()['access_token']
        return self.access_token
    
    def call_service(self, endpoint):
        token = self.get_access_token()
        response = requests.get(
            f'https://api.jnss.me{endpoint}',
            headers={'Authorization': f'Bearer {token}'}
        )
        return response.json()

User Management Strategy

User Lifecycle Management

User Creation and Onboarding

# Authentik User Configuration
email: "user@company.com"
username: "user.name"
name: "User Full Name"
is_active: true
groups: ["employees", "developers"]  # Assigned based on role
attributes:
  department: "Engineering"
  team: "Backend"
  hire_date: "2024-01-15"

Group-Based Authorization

# Group Structure
groups:
  - name: "employees"
    description: "All company employees"
    permissions: ["basic_access"]
    
  - name: "developers" 
    description: "Software development team"
    permissions: ["git_access", "deploy_staging"]
    parent_group: "employees"
    
  - name: "admins"
    description: "System administrators"  
    permissions: ["admin_access", "deploy_production"]
    parent_group: "employees"
    
  - name: "contractors"
    description: "External contractors"
    permissions: ["limited_access"]

Access Control Policies

# Authentik Policy Configuration
policies:
  - name: "Business Hours Access"
    type: "time"
    parameters:
      start_time: "08:00"
      end_time: "18:00"
      days: ["monday", "tuesday", "wednesday", "thursday", "friday"]
    
  - name: "Admin IP Restriction"
    type: "source_ip"
    parameters:
      cidr: "10.0.0.0/8"
      
  - name: "MFA Required for Admin"
    type: "group_membership" 
    parameters:
      group: "admins"
      require_mfa: true

Multi-Factor Authentication

MFA Configuration

# Authentik MFA Settings
mfa_policies:
  - name: "Admin MFA Requirement"
    bound_to: "group:admins"
    authenticators: ["totp", "webauthn"]
    enforce: true
    
  - name: "Developer Optional MFA"  
    bound_to: "group:developers"
    authenticators: ["totp", "sms"]
    enforce: false

Supported MFA Methods

  1. TOTP (Time-based One-Time Passwords)

    • Google Authenticator, Authy, 1Password
    • RFC 6238 compliant
    • Configurable validity period
  2. WebAuthn/FIDO2

    • Hardware security keys (YubiKey, etc.)
    • Biometric authentication
    • Passwordless authentication support
  3. SMS Authentication

    • SMS-based verification codes
    • International phone number support
    • Rate limiting and abuse protection
  4. Email Authentication

    • Email-based verification codes
    • Fallback authentication method
    • Configurable code validity

Security Considerations

Session Management

Session Configuration

# Authentik Session Settings
session_settings:
  cookie_age: 3600  # 1 hour
  cookie_secure: true  # HTTPS only
  cookie_samesite: "Strict"
  remember_me_duration: 86400  # 24 hours
  concurrent_sessions: 3  # Max simultaneous sessions

Session Security Features

  • Session Fixation Protection: New session ID on authentication
  • Concurrent Session Control: Limit simultaneous sessions per user
  • Session Timeout: Automatic logout after inactivity
  • Secure Cookies: HTTP-only, secure, SameSite attributes
  • Session Invalidation: Logout invalidates all sessions

Authorization Security

Policy-Based Access Control

# Example Authorization Policy
policy_bindings:
  - application: "admin_dashboard"
    policies:
      - "group_membership:admins"
      - "mfa_required" 
      - "business_hours_access"
      - "ip_whitelist:office"
    order: 0
    
  - application: "developer_tools"
    policies:
      - "group_membership:developers"
      - "rate_limiting:api_calls"
    order: 1

Security Headers

# Headers passed to protected services
Remote-User: john.doe
Remote-Name: John Doe  
Remote-Email: john.doe@company.com
Remote-Groups: employees,developers
Remote-Authenticated: true
Remote-Auth-Time: 2025-12-11T17:52:31Z
Remote-Session-ID: sess_abc123def456

Audit and Logging

Authentication Events

{
  "timestamp": "2025-12-11T17:52:31Z",
  "event_type": "authentication_success",
  "user_id": "user123", 
  "username": "john.doe",
  "source_ip": "192.168.1.100",
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
  "application": "dashboard.jnss.me",
  "auth_method": "password+totp",
  "session_id": "sess_abc123def456"
}

Authorization Events

{
  "timestamp": "2025-12-11T17:52:45Z", 
  "event_type": "authorization_denied",
  "user_id": "user456",
  "username": "contractor.user", 
  "source_ip": "203.0.113.100",
  "application": "admin_dashboard",
  "reason": "insufficient_privileges",
  "required_groups": ["admins"],
  "user_groups": ["contractors"]
}

System Events

{
  "timestamp": "2025-12-11T17:53:00Z",
  "event_type": "policy_violation", 
  "user_id": "user789",
  "username": "admin.user",
  "source_ip": "198.51.100.50",
  "policy": "ip_whitelist:office", 
  "violation": "access_from_unauthorized_ip",
  "action": "access_denied"
}

Integration Examples

Example 1: Gitea with OAuth2/OIDC Integration

Objective: Git repository access with native OAuth2/OIDC authentication

Why OAuth for Gitea: Gitea has native OAuth2/OIDC support, providing better security and user experience than forward auth.

Step 1: Configure Authentik OAuth2 Provider

# Authentik Provider Configuration
name: "Gitea OAuth Provider"
type: "OAuth2/OpenID Provider"
authorization_flow: "default-provider-authorization-implicit-consent"
client_type: "confidential"
client_id: "gitea"
redirect_uris: "https://git.jnss.me/user/oauth2/Authentik/callback"
signing_key: "authentik-default-key"
scopes: ["openid", "profile", "email", "groups"]
property_mappings:
  - "authentik default OAuth Mapping: OpenID 'openid'"
  - "authentik default OAuth Mapping: OpenID 'email'"
  - "authentik default OAuth Mapping: OpenID 'profile'"

Step 2: Caddy Configuration

# Caddy configuration for Gitea - No forward auth needed
git.jnss.me {
    reverse_proxy localhost:3000
}

Step 3: Gitea Configuration

# app.ini - Gitea OAuth2 configuration
[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false

[oauth2_client]
REGISTER_EMAIL_CONFIRM = false
OPENID_CONNECT_SCOPES = openid email profile groups
ENABLE_AUTO_REGISTRATION = true
USERNAME = preferred_username
EMAIL = email
ACCOUNT_LINKING = auto

Step 4: Add OAuth Source in Gitea Web UI

  1. Navigate to Site Administration → Authentication Sources
  2. Click Add Authentication Source
  3. Authentication Type: OAuth2
  4. Authentication Name: Authentik
  5. OAuth2 Provider: OpenID Connect
  6. Client ID: gitea (from Authentik provider)
  7. Client Secret: (from Authentik provider)
  8. OpenID Connect Auto Discovery URL: https://auth.jnss.me/application/o/gitea/.well-known/openid-configuration
  9. Additional Scopes: profile email groups
  10. Enable: Skip local 2FA, Automatically create users

Group Mapping:

# Authentik group configuration for Gitea
groups:
  - name: "git_users"
    description: "Git repository users"
    applications: ["gitea"]
    
  - name: "git_admins" 
    description: "Git administrators"
    applications: ["gitea"]
    permissions: ["admin"]

Result: Users see "Sign in with Authentik" button on Gitea login page with full OAuth flow.

Example 2: API Service with Scoped Access

Objective: REST API with OAuth2 authentication and scoped permissions

# Caddy configuration for API
api.jnss.me {
    # No forward auth - API handles OAuth2 directly
    reverse_proxy localhost:8080
}

API Service Configuration:

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

def verify_token_scope(token, required_scope):
    """Verify token has required scope"""
    response = requests.post(
        'https://auth.jnss.me/application/o/introspect/',
        data={
            'token': token,
            'client_id': 'api-client-id',
            'client_secret': 'api-client-secret'
        }
    )
    
    if response.status_code != 200:
        return False
    
    token_data = response.json()
    if not token_data.get('active'):
        return False
    
    scopes = token_data.get('scope', '').split()
    return required_scope in scopes

@app.route('/api/public')
def public_endpoint():
    """Public endpoint - no authentication required"""
    return jsonify({'message': 'This is public data'})

@app.route('/api/user/profile')
def user_profile():
    """User profile endpoint - requires user:read scope"""
    auth_header = request.headers.get('Authorization', '')
    
    if not auth_header.startswith('Bearer '):
        return jsonify({'error': 'Missing authorization header'}), 401
    
    token = auth_header[7:]  # Remove 'Bearer ' prefix
    
    if not verify_token_scope(token, 'user:read'):
        return jsonify({'error': 'Insufficient permissions'}), 403
    
    return jsonify({'message': 'User profile data'})

@app.route('/api/admin/users')  
def admin_users():
    """Admin endpoint - requires admin:read scope"""
    auth_header = request.headers.get('Authorization', '')
    
    if not auth_header.startswith('Bearer '):
        return jsonify({'error': 'Missing authorization header'}), 401
    
    token = auth_header[7:]
    
    if not verify_token_scope(token, 'admin:read'):
        return jsonify({'error': 'Admin privileges required'}), 403
    
    return jsonify({'message': 'Admin user data'})

OAuth2 Scopes Configuration:

# Authentik OAuth2 Provider scopes
scopes:
  - name: "user:read"
    description: "Read user profile information"
    
  - name: "user:write"  
    description: "Modify user profile information"
    
  - name: "admin:read"
    description: "Read administrative data"
    
  - name: "admin:write"
    description: "Modify administrative settings"

Example 3: Static Site with Selective Protection

Objective: Protect portions of a static site while keeping some content public

# Caddy configuration for mixed static/protected site
docs.jnss.me {
    # Public documentation - no authentication
    handle /public/* {
        root * /var/www/docs  
        file_server
    }
    
    # Public API documentation
    handle /api-docs {
        root * /var/www/docs
        file_server
    }
    
    # Protected internal documentation
    handle /internal/* {
        forward_auth https://auth.jnss.me {
            uri /outpost.goauthentik.io/auth/caddy
            copy_headers Remote-Groups
        }
        
        # Only employees can access internal docs
        @not_employee {
            not header Remote-Groups "*employees*"
        }
        respond @not_employee "Access denied" 403
        
        root * /var/www/docs
        file_server
    }
    
    # Admin documentation - requires admin group
    handle /admin/* {
        forward_auth https://auth.jnss.me {
            uri /outpost.goauthentik.io/auth/caddy  
            copy_headers Remote-Groups
        }
        
        # Only admins can access admin docs
        @not_admin {
            not header Remote-Groups "*admins*"  
        }
        respond @not_admin "Admin access required" 403
        
        root * /var/www/docs
        file_server
    }
    
    # Default: Require authentication for everything else
    handle {
        forward_auth https://auth.jnss.me {
            uri /outpost.goauthentik.io/auth/caddy
            copy_headers Remote-User Remote-Groups
        }
        
        root * /var/www/docs
        file_server
    }
}

Performance Considerations

Authentication Performance

Caching Strategy

  • Session Caching: Redis-backed session storage for fast lookup
  • Token Caching: Cache valid tokens to reduce introspection calls
  • User Information Caching: Cache user attributes and group memberships
  • Policy Evaluation Caching: Cache authorization decision results

Performance Optimization

# Authentik Performance Settings
cache_settings:
  default_timeout: 300  # 5 minutes
  session_timeout: 1800  # 30 minutes  
  token_introspection_cache: 60  # 1 minute
  user_info_cache: 600  # 10 minutes

Load Balancing Considerations

  • Session Affinity: Not required - sessions stored in shared cache
  • Health Checks: Monitor authentik container health
  • Failover: Graceful degradation strategies for auth service outages
  • Rate Limiting: Protect against authentication abuse

Database Performance

Connection Optimization

# PostgreSQL connection settings for Authentik
database_settings:
  max_connections: 100
  connection_timeout: 30
  idle_timeout: 300
  query_timeout: 60
  connection_pool_size: 20

Index Optimization

-- Recommended PostgreSQL indexes for Authentik performance
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_authentik_core_user_email ON authentik_core_user(email);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_authentik_core_token_key ON authentik_core_token(key);  
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_authentik_sessions_expire_date ON django_session(expire_date);

Troubleshooting Guide

Common Authentication Issues

Issue: Users Cannot Access Protected Services

Symptoms:

  • 401 Unauthorized errors
  • Redirect loops to login page
  • "Access denied" messages

Diagnostic Steps:

# Check authentik service status
ssh root@your-vps "systemctl --user -M authentik@ status authentik-server"

# Test authentik HTTP endpoint
curl -I https://auth.jnss.me/

# Check forward auth endpoint
curl -I https://auth.jnss.me/outpost.goauthentik.io/auth/caddy

# Verify Caddy configuration
ssh root@your-vps "caddy validate --config /etc/caddy/Caddyfile"

Issue: Group-Based Authorization Not Working

Symptoms:

  • Users with correct groups denied access
  • Group headers not passed to services
  • Authorization policies not applied

Diagnostic Steps:

# Check user group membership
curl -H "Authorization: Bearer $TOKEN" https://auth.jnss.me/api/v3/core/users/me/

# Test header passing
curl -H "Cookie: authentik_session=$SESSION" https://auth.jnss.me/outpost.goauthentik.io/auth/caddy -v

# Verify Caddy header configuration
ssh root@your-vps "grep -A5 'copy_headers' /etc/caddy/sites-enabled/*.caddy"

Issue: OAuth2 Token Validation Failing

Symptoms:

  • "Invalid token" errors for valid tokens
  • Token introspection returning false
  • API calls failing with 401

Diagnostic Steps:

# Test token introspection directly
curl -X POST https://auth.jnss.me/application/o/introspect/ \
  -H "Authorization: Bearer $CLIENT_TOKEN" \
  -d "token=$USER_TOKEN"

# Check client credentials
curl -X POST https://auth.jnss.me/application/o/token/ \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET"

Performance Issues

Issue: Slow Authentication Response

Symptoms:

  • Long delays on login
  • Timeouts during authentication
  • Poor user experience

Diagnostic Steps:

# Check authentik container resources
ssh root@your-vps "sudo -u authentik podman stats authentik-server"

# Monitor database performance
ssh root@your-vps "sudo -u postgres psql -h /var/run/postgresql -c 'SELECT * FROM pg_stat_activity;'"

# Check network latency
curl -w "@curl-format.txt" -o /dev/null https://auth.jnss.me/

Issue: High Database Load

Symptoms:

  • Slow database queries
  • High CPU usage on database
  • Authentication timeouts

Solutions:

-- Optimize authentication queries
VACUUM ANALYZE authentik_core_user;
VACUUM ANALYZE authentik_core_token;
VACUUM ANALYZE django_session;

-- Check for missing indexes
SELECT schemaname, tablename, attname, n_distinct, correlation 
FROM pg_stats 
WHERE schemaname = 'public' 
AND tablename LIKE 'authentik_%';

Future Considerations

Scalability Planning

Horizontal Scaling

  • Load Balancer: Multiple authentik instances behind load balancer
  • Database Clustering: PostgreSQL replication for read scaling
  • Cache Clustering: Valkey cluster for session storage
  • Geographic Distribution: Regional authentik deployments

Performance Monitoring

# Monitoring metrics to track
metrics:
  authentication:
    - login_success_rate
    - login_response_time
    - failed_login_attempts
    - concurrent_sessions
    
  authorization:
    - authorization_success_rate  
    - policy_evaluation_time
    - access_denied_rate
    
  system:
    - database_connection_pool_usage
    - cache_hit_rate
    - container_memory_usage
    - response_time_p95

Enterprise Integration

SAML Federation

# SAML Provider configuration for enterprise SSO
saml_provider:
  name: "Corporate SAML"
  acs_url: "https://auth.jnss.me/source/saml/corporate/acs/"
  issuer: "https://auth.jnss.me"
  signing_certificate: "{{ saml_signing_cert }}"
  encryption_certificate: "{{ saml_encryption_cert }}"

LDAP/Active Directory Integration

# LDAP source configuration
ldap_source:
  name: "Corporate LDAP"
  server_uri: "ldaps://ldap.company.com:636" 
  bind_dn: "CN=authentik,OU=Service Accounts,DC=company,DC=com"
  bind_password: "{{ ldap_bind_password }}"
  base_dn: "DC=company,DC=com"
  user_search: "(&(objectClass=user)(sAMAccountName=%(user)s))"
  group_search: "(&(objectClass=group)(member=%(user_dn)s))"

This comprehensive authentication architecture provides a robust, scalable, and secure foundation for managing access to all services in the rick-infra environment, with emphasis on security, performance, and operational excellence.

References