Commit graph

5 commits

Author SHA1 Message Date
Chris
0440f46645
fix(ui): Auto-focus project search field when opened (Issue #992) (#1038)
* fix(oidc): Add logging to help debug missing OIDC button (Issue #1036)

When OIDC is configured but the login button doesn't appear, users
had no way to debug what was wrong. This adds comprehensive logging:

- Log when OIDC is disabled with helpful message
- Log when providers are successfully loaded
- Warn when numbered providers are skipped due to missing fields
- Error when single provider config is incomplete with specific missing fields
- Warn when OIDC is enabled but no valid providers found

This helps users quickly identify configuration issues like:
- Typos in environment variable names
- Missing required fields (issuer, clientId, clientSecret)
- Incorrect OIDC_ENABLED value

Fixes #1036

* fix(ui): Auto-focus project search field when opened (Issue #992)

When clicking on the project field in a task to add or change the project,
the search input field now automatically receives focus, eliminating the
need to manually click on it.

This improves the user experience by reducing friction in the project
assignment workflow.

Fixes #992
2026-04-18 00:29:27 +03:00
Chris
ca77222eae
fix: resolve OIDC session loss and migration failures (#1023)
* fix: resolve OIDC session loss and migration failures

This commit fixes three critical issues affecting OIDC/SSO authentication:

1. Session Not Saved Before Redirect
   - Added explicit req.session.save() callback in OIDC callback handler
   - Ensures session is persisted before redirecting to /today
   - Prevents 401 errors after successful SSO authentication

2. Migration Resilience
   - Added DROP TABLE IF EXISTS users_new in migration
   - Prevents "table already exists" errors from failed migrations
   - Created cleanup script for orphaned migration tables

3. Trust Proxy Documentation
   - Documented TUDUDI_TRUST_PROXY requirement for reverse proxy deployments
   - Added troubleshooting guide for session loss issues
   - Updated .env.example with OIDC configuration examples

Fixes session loss when deployed behind reverse proxies (nginx, Traefik, etc.)

Changes:
- backend/modules/oidc/controller.js: Add session.save() before redirect
- backend/migrations/20260420000004-make-password-optional.js: Add DROP TABLE IF EXISTS
- backend/scripts/cleanup-failed-migration.js: New cleanup utility
- backend/.env.example: Add OIDC and trust proxy examples
- docs/10-oidc-sso.md: Add trust proxy configuration and troubleshooting
- docs/feature-plans/00-oidc-sso.md: Document required environment variables

* fix: prettier formatting in cleanup script
2026-04-14 07:53:55 +03:00
Chris
b0321b02fa
fix: resolve OIDC authentication error with existing identities (#1021)
Fixed "Cannot read properties of undefined (reading 'id')" error that occurred
when existing OIDC users attempted to log in. The issue was a case mismatch in
the Sequelize association accessor.

Changes:
- Fixed case mismatch: identity.user -> identity.User (matches association alias)
- Removed invalid username field that doesn't exist in User model
- Fixed admin role assignment to use Role model instead of User model
- Improved transaction error handling to prevent double rollbacks
- Removed unused generateUsername function

Tests:
- Added comprehensive test suite with 12 tests covering all provisioning scenarios
- All tests passing including the previously failing login scenario
2026-04-14 00:11:32 +03:00
Chris
34dc0373fb
fix: correct Sequelize alias case for OIDCIdentity-User association (#1015)
* fix: use nullish coalescing for recurrence weekday to allow Sunday selection

Fixes #812

When creating a "Monthly on weekday" recurring task, the selector would
jump back to Monday when trying to select Sunday. This was caused by using
the logical OR operator (||) instead of the nullish coalescing operator (??)
when handling the recurrence_weekday value.

Since Sunday is represented as 0, the || operator treated it as falsy and
defaulted to null/undefined, which then defaulted to 1 (Monday).

Changes:
- Replace || with ?? for recurrence_weekday in TaskRecurrenceCard.tsx
- Replace || with ?? for recurrence_weekday in TaskDetails.tsx
- Also fix recurrence_week_of_month and recurrence_month_day for consistency

* fix: correct Sequelize alias case for OIDCIdentity-User association

Fixes #1013

Changed all instances of lowercase 'user' to 'User' to match the
association defined in models/index.js. This resolves the Sequelize
error during OIDC callback:
"User is associated to OIDCIdentity using an alias. You've included
an alias (user), but it does not match the alias(es) defined in your
association (User)."

Changes:
- oidcIdentityService.js: 'user' -> 'User'
- provisioningService.js: 'user' -> 'User' (2 instances)
2026-04-13 19:29:50 +03:00
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