* 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)
* Fix project name overflow and add validation
This commit addresses issue #971 by implementing both UI fixes and
validation to prevent excessively long project names.
Changes:
1. Add word-break and line-clamp to ProjectBanner.tsx to handle
overflow gracefully with line-clamp-3 for names
2. Add frontend validation in ProjectModal.tsx limiting names to
6 words maximum
3. Add backend validation in project.js model with custom wordCount
validator
4. Show user-friendly error messages when validation fails
This ensures project names remain concise and UI-friendly while
preventing the extreme overflow cases that were possible before.
Fixes#971
* Add overflow-hidden to make line-clamp work properly
The line-clamp utility requires explicit overflow-hidden to function
correctly. Without it, the text continues to display in full rather
than being truncated with ellipsis.
* Fix line-clamp using inline CSS styles
Tailwind's line-clamp utilities weren't working, so switched to using
inline styles with the standard CSS approach:
- display: -webkit-box
- -webkit-line-clamp: 3
- -webkit-box-orient: vertical
This ensures the text truncation works reliably across browsers.
* Use Tailwind line-clamp utilities (already defined in CSS)
The project already has line-clamp-1/2/3 utilities properly defined
in tailwind.css with all the necessary webkit properties. Simplified
the component to use these existing utilities instead of inline styles.
* Add dedicated CSS classes with !important for line-clamp
Created custom project-name-clamp and project-desc-clamp classes
with !important flags to ensure they override any conflicting styles.
This should finally fix the text truncation issue.
* Use component-scoped styles for line-clamp
Adding inline style tag in the component to ensure the line-clamp
CSS is definitely loaded and applied. This bypasses any potential
issues with external CSS compilation or loading order.
* Change project name line-clamp from 3 to 2 lines
Limiting project name display to 2 lines with ellipsis for better
visual density and cleaner appearance.
* Increase line-height for project name in banner
Added line-height: 1.3 to project name for better readability
and visual spacing between lines.
Fixes issue where Sequelize wasn't detecting changes to the
channel_sent_at JSON field, causing markChannelAsSent() to not
persist updates to the database.
This caused the same notification to be sent via Telegram every
15 minutes (on each scheduler run) because the rate limiting
timestamp was never saved.
The fix adds this.changed('channel_sent_at', true) before save()
to explicitly mark the field as modified, which is required for
Sequelize to detect changes to JSON fields.
Impact: Reduces duplicate Telegram notifications from every 15min
to at most once per 24 hours per task.
* Fix Telegram notification spam with channel-level rate limiting
Addresses issue #950 where Telegram notifications were sent excessively
(96-288 messages per day per task) due to the delete-and-recreate pattern
added in commit 105a913a to fix navbar notification pile-up.
Changes:
- Add channel_sent_at JSON field to notifications table to track when
each channel (telegram, email, push) was last sent
- Add helper methods to notification model:
- markChannelAsSent(channel): Records send timestamp
- wasChannelRecentlySent(channel, threshold): Checks if sent within 24h
- Modify sendTelegramNotification() to check rate limit before sending
- Update service layer (dueTaskService, deferredTaskService,
dueProjectService) to preserve channel_sent_at when recreating
notifications
- Add comprehensive unit and integration tests (20 tests, all passing)
Impact:
- Reduces Telegram notifications from 96-288/day to 1/day per item
- Preserves in-app notification refresh behavior (every 5-15 min)
- Maintains navbar pile-up fix from original commit
- Rate limit configurable (default: 24 hours)
Fixes#950
* Fix linting and formatting issues
* Fix integration test that was trying to access private function
* Fix prettier formatting in integration test
* Break down TaskModal
* Fix an issue with counting past times
* Add daily reccurence on week
* fixup! Add daily reccurence on week
* fixup! fixup! Add daily reccurence on week
* Improve recurring widget on task page
* fixup! Improve recurring widget on task page
* Global search scaffold
* Add search preview text
* Add generic fallback for preview text in search
* fixup! Add generic fallback for preview text in search
* Add more tweaks
* fixup! Add more tweaks
* Fix an issue with criteria
* fixup! Fix an issue with criteria
* fixup! fixup! Fix an issue with criteria
* fixup! fixup! fixup! Fix an issue with criteria
* Fix an issue with priority filter
* fixup! Fix an issue with priority filter
* Add sortable pins
* fixup! Add sortable pins
* Make options collapsed by default
* Tweak UI
* Add tests
* Add translations
* Add more translations
* fixup! Add more translations
* Add minor tweaks
* Scaffold project states
* fixup! Scaffold project states
* Fix blinking project modal
* fixup! Fix blinking project modal
* fixup! fixup! Fix blinking project modal
* Fix an issue with the tag input autosuggest
* fixup! Fix an issue with the tag input autosuggest
* fixup! fixup! Fix an issue with the tag input autosuggest
* Add state to project details
* fixup! Add state to project details
* Add state indicator on project cards
* fixup! Add state indicator on project cards
- Add unified UID column migration for all entities
- Create centralized UID generation utility
- Update all models to use standardized UID hooks
- Fix route handlers to support UID-based lookups
- Update slug utilities for consistent UID extraction
- Fix tag tests to use query parameters instead of path params
- Configure Jest for better TypeScript support