* 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)
750 lines
20 KiB
Markdown
750 lines
20 KiB
Markdown
# OIDC/SSO Authentication
|
|
|
|
This guide explains how to configure and use OpenID Connect (OIDC) Single Sign-On (SSO) authentication in Tududi.
|
|
|
|
**Related:** [User Management](08-user-management.md), [Architecture Overview](architecture.md)
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
- [Overview](#overview)
|
|
- [Why Use OIDC/SSO](#why-use-oidcsso)
|
|
- [Supported Providers](#supported-providers)
|
|
- [Configuration](#configuration)
|
|
- [Single Provider Setup](#single-provider-setup)
|
|
- [Multiple Providers Setup](#multiple-providers-setup)
|
|
- [Environment Variables Reference](#environment-variables-reference)
|
|
- [Provider Setup Guides](#provider-setup-guides)
|
|
- [Google](#google)
|
|
- [Okta](#okta)
|
|
- [Keycloak](#keycloak)
|
|
- [Authentik](#authentik)
|
|
- [PocketID](#pocketid)
|
|
- [Azure AD](#azure-ad)
|
|
- [User Features](#user-features)
|
|
- [Logging In with SSO](#logging-in-with-sso)
|
|
- [Account Linking](#account-linking)
|
|
- [Managing Connected Accounts](#managing-connected-accounts)
|
|
- [Advanced Topics](#advanced-topics)
|
|
- [Auto-Provisioning](#auto-provisioning)
|
|
- [Admin Role Assignment](#admin-role-assignment)
|
|
- [Hybrid Authentication](#hybrid-authentication)
|
|
- [Troubleshooting](#troubleshooting)
|
|
- [Security Considerations](#security-considerations)
|
|
|
|
---
|
|
|
|
## 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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:
|
|
```bash
|
|
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](https://console.cloud.google.com/)
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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](https://pocketid.app/developer)
|
|
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**
|
|
|
|
```bash
|
|
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](https://portal.azure.com/)
|
|
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**
|
|
|
|
```bash
|
|
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:**
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
### Can't Unlink Last Auth Method
|
|
|
|
**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:**
|
|
|
|
```bash
|
|
GET /api/oidc/providers
|
|
```
|
|
|
|
Response:
|
|
```json
|
|
[
|
|
{
|
|
"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:**
|
|
|
|
```bash
|
|
GET /api/oidc/identities
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
See [Swagger API docs](http://localhost:3002/api-docs) for full API reference.
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
**Issues:** [GitHub Issues](https://github.com/chrisvel/tududi/issues)
|
|
**Discussions:** [GitHub Discussions](https://github.com/chrisvel/tududi/discussions)
|
|
**Discord:** [Join our community](https://discord.gg/fkbeJ9CmcH)
|
|
|
|
**Related Documentation:**
|
|
- [User Management](08-user-management.md)
|
|
- [Architecture Overview](architecture.md)
|
|
- [Development Workflow](development-workflow.md)
|
|
|
|
---
|
|
|
|
**Document Version:** 1.0.0
|
|
**Last Updated:** 2026-04-20
|
|
**Maintainer:** Update when OIDC features change
|