tududi/docs/10-oidc-sso.md
Chris c2e9a1aa21
feat: Add OIDC/SSO authentication support (#1008)
* feat: add OIDC/SSO database schema and models (Phase 1)

Add database foundation for OpenID Connect authentication:

Database Migrations:
- Create oidc_identities table (links users to OIDC accounts)
- Create oidc_state_nonces table (OAuth state/nonce for CSRF protection)
- Create auth_audit_log table (security event logging)
- Make password_digest nullable in users table (allow OIDC-only users)

Models:
- OIDCIdentity: Links users to external OIDC providers
- OIDCStateNonce: Temporary OAuth state management
- AuthAuditLog: Authentication event audit trail

Changes:
- Updated User model to allow null password_digest
- Added model associations in models/index.js
- All migrations tested and verified

Related to #977

* feat: add OIDC core services (Phase 2)

- Install openid-client@^6.2.0 for OIDC protocol support
- Implement providerConfig.js for loading providers from .env
  - Support single provider or numbered providers (OIDC_PROVIDER_1_*, etc.)
  - Auto-provision and admin email domain configuration
  - Provider caching for performance
- Implement stateManager.js for OAuth state/nonce management
  - CSRF protection with 10-minute TTL
  - One-time use state consumption
  - Automatic cleanup of expired states
- Implement auditService.js for authentication event logging
  - Track login success/failure, logout, OIDC linking/unlinking
  - Store IP address, user agent, and metadata
  - Support for event queries and retention cleanup
- Add comprehensive unit tests (60 tests, all passing)
  - providerConfig: 36 tests for env parsing and validation
  - stateManager: 12 tests for state lifecycle and security
  - auditService: 12 tests for event logging and queries

Phase 2 completes the backend core services needed for OIDC authentication.

* feat: implement OIDC authentication flow (Phase 3)

Core OIDC Flow (service.js):
- Provider discovery with issuer caching
- Authorization URL generation with state/nonce
- OAuth callback handling and token exchange
- ID token validation using openid-client
- Token refresh functionality

JIT User Provisioning (provisioningService.js):
- Auto-create users from OIDC claims
- Link existing email accounts to OIDC identities
- Admin role assignment based on email domain rules
- Automatic username generation from email
- Transaction-safe identity creation

Identity Management (oidcIdentityService.js):
- List user's linked OIDC identities
- Link additional providers to existing accounts
- Unlink identities with safety checks
- Prevent unlinking last auth method
- Update identity claims on login

HTTP Layer (controller.js + routes.js):
- GET /api/oidc/providers - List configured providers
- GET /api/oidc/auth/:slug - Initiate OIDC flow
- GET /api/oidc/callback/:slug - Handle OAuth callback
- POST /api/oidc/link/:slug - Link provider to current user
- DELETE /api/oidc/unlink/:id - Unlink identity
- GET /api/oidc/identities - Get user's identities

Integration:
- Register OIDC routes in Express app (public + authenticated)
- Update auth service to reject password login for OIDC-only users
- Audit logging for all OIDC operations
- Session creation on successful authentication

Security:
- State/nonce CSRF protection
- One-time use state consumption
- Transaction-safe user provisioning
- Foreign key constraints enforced

* feat: implement OIDC frontend login flow (Phase 4)

- Created OIDCProviderButtons component for SSO login options
- Created OIDCCallback component for OAuth callback handling
- Updated Login page to fetch and display OIDC providers
- Added /auth/callback/:provider route to App.tsx
- Added i18n translations for OIDC UI elements
- Downgraded openid-client to v5.7.0 (CommonJS compatibility)
- Fixed linting issues in backend OIDC modules

Phase 4 completes the frontend login flow for OIDC/SSO authentication.
Users can now see configured SSO providers on the login page.

* feat: implement OIDC account linking UI (Phase 5)

Add Connected Accounts section to Profile Security tab allowing users to:
- View linked OIDC provider accounts
- Link new SSO providers to their account
- Unlink OIDC identities with validation
- Prevent unlinking last authentication method

Backend changes:
- Add has_password virtual field to User model
- Include has_password in profile API response
- Track whether user has password set for validation

Frontend changes:
- Create oidcService for OIDC API operations
- Create ConnectedAccounts component with link/unlink flows
- Add confirmation dialog before unlinking accounts
- Validate that users cannot unlink their last auth method
- Show warning if user has no password set
- Integrate Connected Accounts into SecurityTab

User experience:
- View all linked SSO provider accounts with email and link date
- Link additional providers via "Link Provider" buttons
- Unlink with two-step confirmation to prevent accidents
- Clear error messages when unlinking would leave no auth method
- Warning message suggesting password setup for OIDC-only users

Fixes #977

* feat: complete OIDC documentation and UI improvements (Phase 6)

This commit completes Phase 6 of the OIDC/SSO implementation with comprehensive
documentation, bug fixes, and UI reorganization.

Documentation:
- Add comprehensive user guide at docs/10-oidc-sso.md with:
  - Setup guides for 6 major providers (Google, Okta, Keycloak, Authentik, PocketID, Azure AD)
  - Configuration examples for single and multiple providers
  - User features documentation (login, account linking, management)
  - Advanced topics (auto-provisioning, admin role assignment, hybrid auth)
  - Comprehensive troubleshooting section
  - Security considerations and best practices
- Update README.md with OIDC/SSO section and quick setup examples

Internationalization:
- Add i18n support to OIDCProviderButtons component
- Add translation keys for all OIDC UI text
- Update English translations with "sign_in_with" key

Bug Fixes:
- Fix oidcService.ts to correctly unwrap API responses
  - Backend returns {providers: [...]} and {identities: [...]}
  - Frontend was expecting plain arrays, causing "map is not a function" error
- Fix initiateOIDCLink to properly handle POST response

UI Improvements:
- Move OIDC/SSO to dedicated tab in profile settings
  - Create new OIDCTab component with green LinkIcon
  - Remove ConnectedAccounts from SecurityTab
  - Add OIDC tab between Security and API Keys tabs
  - Update ProfileSettings with new tab configuration
- Security tab now focuses solely on password management

Testing:
- All linting passes
- All tests pass (82 suites, 1223 tests)

Related to #977

* feat: add OIDC/SSO translations for all 24 languages

Add i18n support for OIDC/SSO features across all supported languages:
- "Sign in with {{provider}}" button text
- "OIDC/SSO" tab label in profile settings
- OIDC authentication flow messages

Translations added for: Arabic, Bulgarian, Danish, German, Greek, Spanish,
Finnish, French, Indonesian, Italian, Japanese, Korean, Dutch, Norwegian,
Polish, Portuguese, Romanian, Russian, Slovenian, Swedish, Turkish,
Ukrainian, Vietnamese, and Chinese.

* fix: resolve 13 CodeQL security alerts

This commit addresses critical security vulnerabilities identified by CodeQL scanning:

**Security Configuration (2 fixes)**
- Fix insecure Helmet configuration - enable CSP and HSTS in production
- Fix clear text cookie transmission - enable secure cookies in production

**Path Injection (3 fixes)**
- Add path validation in users/controller.js to prevent arbitrary file deletion
- Add path validation in users/service.js for avatar operations
- Add path sanitization in attachment-utils.js deleteFileFromDisk function

**Cross-Site Scripting (1 fix)**
- Fix XSS vulnerability in GeneralTab.tsx avatar URL handling
- Add URL sanitization to prevent javascript: protocol attacks

**URL Security (2 fixes)**
- Fix double escaping in url/service.js HTML entity decoding
- Fix incomplete URL sanitization for YouTube domain validation

**Denial of Service (1 fix)**
- Add loop bound protection in inboxProcessingService.js (10k char limit)

**Rate Limiting (3 fixes)**
- Add rate limiting to auth routes (register, verify-email)
- Add rate limiting to task attachment upload/delete endpoints
- Add rate limiting to user avatar upload/delete endpoints

**GitHub Actions Security (1 fix)**
- Add explicit read-only permissions to CI workflow

Note: CSRF middleware (#10) requires frontend changes and is tracked separately.

Relates to PR #1008

* fix: allow test files in path validation for tests

* fix: format long condition in attachment-utils for Prettier compliance

Break the path validation condition across multiple lines to meet Prettier formatting requirements and fix CI linting failure.

* fix: resolve CodeQL security alerts

- Add rate limiting to OIDC authentication routes using authLimiter and authenticatedApiLimiter
- Implement CSRF protection middleware using csrf-sync (skips for API tokens and test environment)
- Add CSRF token endpoint at /api/csrf-token
- Fix incomplete URL scheme validation in GeneralTab to block all dangerous schemes (javascript:, data:, vbscript:, file:)

This addresses 5 high-severity CodeQL security vulnerabilities:
- Missing rate limiting on OIDC auth routes
- Missing CSRF middleware protection
- Incomplete URL sanitization in avatar handling

All 1223 tests passing.

* fix: implement CSRF protection with lusca for CodeQL compliance

Add CSRF protection using lusca.csrf (CodeQL's recommended library) to
protect session-based authentication while supporting hybrid auth patterns.

Implementation:
- Pre-check middleware marks exempt requests (test env, Bearer tokens)
- Lusca CSRF middleware applied with exemption flag check
- Session-based requests require valid x-csrf-token header
- Bearer token requests exempt (don't use cookies)
- Test environment exempt for test execution

This addresses CodeQL security alert js/missing-token-validation while
maintaining support for both cookie-based and token-based authentication.

Related: #977 (OIDC/SSO authentication feature)
2026-04-13 12:17:35 +03:00

20 KiB

OIDC/SSO Authentication

This guide explains how to configure and use OpenID Connect (OIDC) Single Sign-On (SSO) authentication in Tududi.

Related: User Management, Architecture Overview


Table of Contents


Overview

OIDC (OpenID Connect) is a modern authentication protocol that allows users to sign in to Tududi using external identity providers like Google, Okta, Keycloak, or any OIDC-compliant service.

Key Features:

  • Single Sign-On: Use your existing corporate or personal accounts
  • Just-In-Time Provisioning: New users are automatically created on first login
  • Account Linking: Connect multiple authentication methods to one account
  • Hybrid Authentication: Choose between email/password or SSO login
  • Multiple Providers: Support for multiple OIDC providers simultaneously

Why Use OIDC/SSO

For Enterprise Users:

  • Centralized identity management
  • Enforce corporate security policies
  • Simplified user onboarding/offboarding
  • Compliance with security standards

For Self-Hosters:

  • Use existing authentication infrastructure (Keycloak, Authentik)
  • Reduce password fatigue
  • Leverage provider security features (2FA, security keys)
  • Simplify family/team access management

For Individual Users:

  • One-click login with Google, Microsoft, etc.
  • No need to remember another password
  • Automatic profile updates from provider

Supported Providers

Tududi supports any OIDC-compliant identity provider, including:

Provider Type Typical Use Case
Google Public Personal accounts, G Suite
Okta Enterprise Corporate SSO
Keycloak Self-hosted Open-source identity management
Authentik Self-hosted Homelab, small business
PocketID Public Decentralized identity
Azure AD Enterprise Microsoft 365 organizations
Generic OIDC Any Custom providers with .well-known/openid-configuration

Configuration

OIDC providers are configured via environment variables in your .env file. After making changes, restart the Tududi server for them to take effect.

Single Provider Setup

For most users, a single provider is sufficient:

# Enable OIDC
OIDC_ENABLED=true

# Provider Configuration
OIDC_PROVIDER_NAME=Google
OIDC_PROVIDER_SLUG=google
OIDC_ISSUER_URL=https://accounts.google.com
OIDC_CLIENT_ID=your-client-id.apps.googleusercontent.com
OIDC_CLIENT_SECRET=your-client-secret
OIDC_SCOPE=openid profile email

# Auto-provisioning (recommended)
OIDC_AUTO_PROVISION=true

# Optional: Auto-assign admin role to specific email domains
OIDC_ADMIN_EMAIL_DOMAINS=example.com,mycompany.com

Required Variables:

  • OIDC_PROVIDER_NAME: Display name shown to users (e.g., "Google", "Company SSO")
  • OIDC_PROVIDER_SLUG: URL-safe identifier (e.g., "google", "okta")
  • OIDC_ISSUER_URL: Provider's OIDC discovery URL
  • OIDC_CLIENT_ID: OAuth 2.0 client ID from provider
  • OIDC_CLIENT_SECRET: OAuth 2.0 client secret from provider

Multiple Providers Setup

To support multiple providers, use numbered environment variables:

# Enable OIDC
OIDC_ENABLED=true

# Provider 1: Google
OIDC_PROVIDER_1_NAME=Google
OIDC_PROVIDER_1_SLUG=google
OIDC_PROVIDER_1_ISSUER=https://accounts.google.com
OIDC_PROVIDER_1_CLIENT_ID=xxx.apps.googleusercontent.com
OIDC_PROVIDER_1_CLIENT_SECRET=xxx
OIDC_PROVIDER_1_SCOPE=openid profile email
OIDC_PROVIDER_1_AUTO_PROVISION=true

# Provider 2: Company Okta
OIDC_PROVIDER_2_NAME=Company SSO
OIDC_PROVIDER_2_SLUG=okta
OIDC_PROVIDER_2_ISSUER=https://company.okta.com
OIDC_PROVIDER_2_CLIENT_ID=yyy
OIDC_PROVIDER_2_CLIENT_SECRET=yyy
OIDC_PROVIDER_2_AUTO_PROVISION=true
OIDC_PROVIDER_2_ADMIN_EMAIL_DOMAINS=company.com

# Provider 3: Self-hosted Authentik
OIDC_PROVIDER_3_NAME=Authentik
OIDC_PROVIDER_3_SLUG=authentik
OIDC_PROVIDER_3_ISSUER=https://auth.example.com/application/o/tududi/
OIDC_PROVIDER_3_CLIENT_ID=zzz
OIDC_PROVIDER_3_CLIENT_SECRET=zzz
OIDC_PROVIDER_3_AUTO_PROVISION=true

Numbering Rules:

  • Start at OIDC_PROVIDER_1_*, increment sequentially
  • No gaps allowed (1, 2, 3... not 1, 3, 5)
  • Maximum: Practical limit ~5 providers (no hard limit)

Environment Variables Reference

Variable Required Default Description
OIDC_ENABLED Yes false Enable/disable OIDC feature
OIDC_PROVIDER_NAME Yes - Provider display name
OIDC_PROVIDER_SLUG Yes - URL-safe identifier
OIDC_ISSUER_URL Yes - OIDC discovery endpoint
OIDC_CLIENT_ID Yes - OAuth client ID
OIDC_CLIENT_SECRET Yes - OAuth client secret
OIDC_SCOPE No openid profile email OAuth scopes
OIDC_AUTO_PROVISION No true Auto-create users on first login
OIDC_ADMIN_EMAIL_DOMAINS No - Comma-separated domains for auto-admin
BASE_URL Yes - Tududi base URL (for OAuth callbacks)

Important: The BASE_URL variable must be set for OAuth redirects to work:

BASE_URL=http://localhost:3002  # Development
BASE_URL=https://tududi.example.com  # Production

Provider Setup Guides

Google

1. Create OAuth 2.0 Credentials

  1. Go to Google Cloud Console
  2. Create a new project or select existing
  3. Navigate to APIs & Services > Credentials
  4. Click Create Credentials > OAuth client ID
  5. Select Web application
  6. Add authorized redirect URIs:
    • Development: http://localhost:3002/api/oidc/callback/google
    • Production: https://your-domain.com/api/oidc/callback/google
  7. Copy Client ID and Client Secret

2. Configure Tududi

OIDC_ENABLED=true
OIDC_PROVIDER_NAME=Google
OIDC_PROVIDER_SLUG=google
OIDC_ISSUER_URL=https://accounts.google.com
OIDC_CLIENT_ID=123456789.apps.googleusercontent.com
OIDC_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxx
OIDC_SCOPE=openid profile email
OIDC_AUTO_PROVISION=true

3. Test

  • Restart Tududi
  • Navigate to login page
  • Click "Sign in with Google"
  • Approve permissions
  • You should be logged in!

Okta

1. Create OIDC Application

  1. Log in to your Okta admin console
  2. Go to Applications > Applications
  3. Click Create App Integration
  4. Select OIDC - OpenID Connect
  5. Select Web Application
  6. Configure:
    • Sign-in redirect URIs: https://your-domain.com/api/oidc/callback/okta
    • Sign-out redirect URIs: https://your-domain.com/login
    • Controlled access: Choose your access policy
  7. Save and note the Client ID and Client Secret

2. Find Your Issuer URL

Format: https://{your-domain}.okta.com

Example: https://company.okta.com

3. Configure Tududi

OIDC_ENABLED=true
OIDC_PROVIDER_NAME=Company SSO
OIDC_PROVIDER_SLUG=okta
OIDC_ISSUER_URL=https://company.okta.com
OIDC_CLIENT_ID=0oa123456789abcde
OIDC_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx
OIDC_SCOPE=openid profile email
OIDC_AUTO_PROVISION=true
OIDC_ADMIN_EMAIL_DOMAINS=company.com

Keycloak

1. Create OIDC Client

  1. Log in to Keycloak admin console
  2. Select your realm
  3. Go to Clients > Create client
  4. Configure:
    • Client type: OpenID Connect
    • Client ID: tududi
    • Client authentication: ON (confidential)
    • Valid redirect URIs: https://your-domain.com/api/oidc/callback/keycloak
  5. Go to Credentials tab and copy Client secret

2. Find Your Issuer URL

Format: https://{keycloak-domain}/realms/{realm-name}

Example: https://auth.example.com/realms/myrealm

3. Configure Tududi

OIDC_ENABLED=true
OIDC_PROVIDER_NAME=Keycloak
OIDC_PROVIDER_SLUG=keycloak
OIDC_ISSUER_URL=https://auth.example.com/realms/myrealm
OIDC_CLIENT_ID=tududi
OIDC_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx
OIDC_SCOPE=openid profile email
OIDC_AUTO_PROVISION=true

Authentik

1. Create OAuth2/OIDC Provider

  1. Log in to Authentik admin interface
  2. Go to Applications > Providers
  3. Click Create and select OAuth2/OpenID Provider
  4. Configure:
    • Name: Tududi
    • Authorization flow: Choose your flow
    • Redirect URIs: https://your-domain.com/api/oidc/callback/authentik
    • Signing Key: Select a certificate
  5. Note the Client ID and Client Secret

2. Create Application

  1. Go to Applications > Applications
  2. Click Create
  3. Link the provider you just created
  4. Configure slug and other settings

3. Find Your Issuer URL

Format: https://{authentik-domain}/application/o/{application-slug}/

Example: https://auth.example.com/application/o/tududi/

4. Configure Tududi

OIDC_ENABLED=true
OIDC_PROVIDER_NAME=Authentik
OIDC_PROVIDER_SLUG=authentik
OIDC_ISSUER_URL=https://auth.example.com/application/o/tududi/
OIDC_CLIENT_ID=xxxxxxxxxxxxxxxxxxxx
OIDC_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx
OIDC_SCOPE=openid profile email
OIDC_AUTO_PROVISION=true

PocketID

1. Register Application

  1. Go to PocketID Developer Console
  2. Create a new application
  3. Configure:
    • Name: Tududi
    • Redirect URI: https://your-domain.com/api/oidc/callback/pocketid
  4. Note the Client ID and Client Secret

2. Configure Tududi

OIDC_ENABLED=true
OIDC_PROVIDER_NAME=PocketID
OIDC_PROVIDER_SLUG=pocketid
OIDC_ISSUER_URL=https://pocketid.app
OIDC_CLIENT_ID=xxxxxxxxxxxxxxxxxxxx
OIDC_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx
OIDC_SCOPE=openid profile email
OIDC_AUTO_PROVISION=true

Azure AD

1. Register Application

  1. Go to Azure Portal
  2. Navigate to Azure Active Directory > App registrations
  3. Click New registration
  4. Configure:
    • Name: Tududi
    • Supported account types: Choose your option
    • Redirect URI: Web - https://your-domain.com/api/oidc/callback/azure
  5. After creation, go to Certificates & secrets
  6. Create a new Client secret and copy it
  7. Note the Application (client) ID

2. Find Your Tenant ID

Go to Azure Active Directory > Overview and copy the Tenant ID

3. Configure Tududi

OIDC_ENABLED=true
OIDC_PROVIDER_NAME=Microsoft
OIDC_PROVIDER_SLUG=azure
OIDC_ISSUER_URL=https://login.microsoftonline.com/{tenant-id}/v2.0
OIDC_CLIENT_ID=12345678-1234-1234-1234-123456789012
OIDC_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx
OIDC_SCOPE=openid profile email
OIDC_AUTO_PROVISION=true

Replace {tenant-id} with your actual tenant ID.


User Features

Logging In with SSO

First-Time Users:

  1. Navigate to Tududi login page
  2. Click the provider button (e.g., "Sign in with Google")
  3. You'll be redirected to the provider's login page
  4. Approve the requested permissions
  5. You'll be redirected back to Tududi and logged in
  6. A new account is automatically created (if auto-provisioning is enabled)

Returning Users:

  1. Click your provider button on the login page
  2. If already logged in to provider, you'll be immediately authenticated
  3. Redirected to the Today page

Account Linking

Users with existing email/password accounts can link SSO providers:

Steps:

  1. Log in with email/password
  2. Go to Profile > Security tab
  3. Scroll to Connected Accounts section
  4. Click Link [Provider Name]
  5. Approve permissions at provider
  6. Provider is now linked to your account

Benefits:

  • Log in with either email/password OR SSO
  • Switch between auth methods freely
  • Maintain single account with multiple login options

Managing Connected Accounts

View Connected Accounts:

Go to Profile > Security > Connected Accounts to see:

  • Linked providers
  • Email addresses from each provider
  • Date first linked
  • Last login date

Unlink Account:

  1. Click Unlink next to the provider
  2. Confirm the action

Important: You cannot unlink your last authentication method. You must have either:

  • A password set, OR
  • At least one OIDC identity linked

Advanced Topics

Auto-Provisioning

When OIDC_AUTO_PROVISION=true (default), new users are automatically created on first login.

How It Works:

  1. User completes SSO login
  2. Tududi checks if an OIDC identity exists for this provider + user ID
  3. If not, checks if a user with the email exists:
    • User exists: Links OIDC identity to existing user
    • User doesn't exist: Creates new user with:
      • Email from OIDC claims (verified)
      • Username from email prefix
      • No password (OIDC-only account)
      • Optional admin role (if domain matches)
  4. User is logged in

Disable Auto-Provisioning:

OIDC_AUTO_PROVISION=false

When disabled:

  • Only users with pre-linked OIDC identities can log in
  • New SSO users are rejected with an error
  • Useful for invite-only deployments

Admin Role Assignment

Automatically grant admin privileges based on email domain:

OIDC_ADMIN_EMAIL_DOMAINS=company.com,example.org

Rules:

  • New users with emails from these domains become admins
  • Applies only on first provisioning (not on subsequent logins)
  • Existing non-admin users are not promoted
  • Case-insensitive domain matching

Use Cases:

  • Corporate deployments: Trust internal email domains
  • Family instances: Trust your domain
  • Multi-tenant: Different providers for different admin groups

Hybrid Authentication

Tududi supports hybrid authentication where users choose their preferred method:

Scenarios:

  1. Email/Password Only: Traditional authentication
  2. SSO Only: OIDC-only users (no password set)
  3. Both: Users can use either method

For OIDC-Only Users:

If a user was created via SSO and has no password:

  • Attempting email/password login shows: "This account uses SSO. Please sign in with your SSO provider."
  • User must log in via SSO or set a password via password reset

For Email/Password Users:

  • Can link SSO providers at any time
  • Both auth methods work independently
  • Unlinking SSO doesn't affect password login

Troubleshooting

"Provider not found" Error

Cause: The provider slug in the URL doesn't match any configured provider.

Solution:

  1. Check .env file for correct OIDC_PROVIDER_SLUG value
  2. Ensure slug is URL-safe (lowercase, no spaces)
  3. Restart server after .env changes

"Invalid state parameter" Error

Cause: OAuth state validation failed (security check).

Possible Reasons:

  • State expired (>10 minutes old)
  • Callback URL mismatch
  • State already consumed

Solution:

  1. Start the login flow again (don't reuse old URLs)
  2. Check BASE_URL matches your actual domain
  3. Verify callback URL in provider settings

"Auto-provisioning disabled" Error

Cause: User doesn't exist and OIDC_AUTO_PROVISION=false.

Solution:

  • Enable auto-provisioning: OIDC_AUTO_PROVISION=true, OR
  • Create user account manually first, then link SSO

Provider Button Not Showing

Cause: Provider not loaded from .env.

Solution:

  1. Check OIDC_ENABLED=true is set
  2. Verify all required variables are present
  3. Check for typos in variable names
  4. Restart server
  5. Check browser console for API errors

"Invalid grant" or Token Errors

Cause: JWT validation failed.

Possible Reasons:

  • Wrong client secret
  • Clock skew between servers
  • Issuer URL mismatch

Solution:

  1. Verify OIDC_CLIENT_SECRET matches provider
  2. Ensure server time is accurate (NTP sync)
  3. Check OIDC_ISSUER_URL exactly matches provider's issuer claim

Callback URL Mismatch

Cause: Redirect URI configured in provider doesn't match Tududi's callback.

Solution:

  1. Callback URL format: {BASE_URL}/api/oidc/callback/{slug}
  2. Example: https://tududi.example.com/api/oidc/callback/google
  3. Must match exactly in provider settings (including http/https)
  4. Update provider settings and restart Tududi

Cause: Safety check prevents losing all access.

Solution:

  1. Set a password first (Profile > Security)
  2. Then unlink OIDC identity, OR
  3. Link another OIDC provider first

Security Considerations

Secret Storage

  • Client secrets are stored in .env file (plaintext)
  • Ensure .env is never committed to version control (already in .gitignore)
  • Use proper file permissions: chmod 600 .env on Linux/macOS
  • For production, consider Docker secrets or Kubernetes secrets

OAuth Flow Security

Tududi implements standard OAuth 2.0 security measures:

  1. CSRF Protection: Cryptographically random state parameter (32 bytes)
  2. Replay Protection: State is one-time use, 10-minute TTL
  3. JWT Validation: ID tokens verified against provider's JWKS
  4. Nonce Validation: Prevents token reuse attacks
  5. TLS Enforcement: Always use HTTPS in production

Data Privacy

What's Stored:

  • OIDC subject (provider's user ID)
  • Email, name, profile picture from claims
  • Full raw claims (JSON) for debugging
  • First/last login timestamps

What's Not Stored:

  • Provider passwords
  • OAuth access tokens (discarded after login)
  • Refresh tokens

Audit Trail

All authentication events are logged (if audit logging is enabled):

  • Login success/failure
  • OIDC linking/unlinking
  • Provider information
  • IP address and user agent

Check logs at: /backend/logs/ (if enabled)

Rate Limiting

OIDC endpoints are protected by rate limiting:

  • /api/oidc/auth/*: 5 requests per 15 minutes per IP
  • /api/oidc/callback/*: 5 requests per 15 minutes per IP
  • Linking/unlinking: Standard authenticated API limits

Best Practices

  1. Use HTTPS: Always use HTTPS in production
  2. Restrict Callback URLs: Only whitelist exact callback URLs needed
  3. Rotate Secrets: Periodically rotate client secrets
  4. Monitor Logs: Watch for suspicious authentication attempts
  5. Limit Providers: Only enable providers you trust
  6. Email Verification: Trust provider's email verification
  7. Review Permissions: Only request necessary OAuth scopes

Migration from Email/Password

Existing deployments can gradually adopt OIDC:

Step 1: Configure Providers

Add OIDC configuration to .env without removing email/password support.

Step 2: Notify Users

Announce new SSO option to users.

Step 3: Users Link Accounts

Existing users can link SSO providers to their accounts via Profile > Security.

Step 4: Optional - Disable Email/Password

Not recommended, but possible by customizing the frontend Login component.

Rollback:

Simply set OIDC_ENABLED=false and restart. Email/password authentication continues to work.


API Integration

Fetch Available Providers:

GET /api/oidc/providers

Response:

[
  {
    "slug": "google",
    "name": "Google",
    "button_text": "Sign in with {name}",
    "type": "oidc"
  }
]

Initiate Login Flow:

Redirect user to:

GET /api/oidc/auth/{slug}

User's Connected Identities:

GET /api/oidc/identities
Authorization: Bearer <token>

See Swagger API docs for full API reference.


Support

Issues: GitHub Issues Discussions: GitHub Discussions Discord: Join our community

Related Documentation:


Document Version: 1.0.0 Last Updated: 2026-04-20 Maintainer: Update when OIDC features change