Commit 99e86b6
Add OIDC authentication support with multi-provider configuration (#691)
* Add comprehensive OIDC integration with role mapping
- Add OIDC authentication endpoints (login, callback, config)
- Implement flexible role mapping from OIDC groups/roles to Gramps roles
- Add configurable OIDC scopes and role claim sources
- Support environment-driven configuration with GRAMPSWEB_ prefix
- Add fallback to guest role when no role claims found
- Include option to disable local authentication when OIDC enabled
- Add auto-redirect functionality for OIDC-only deployments
- Update token endpoint to respect OIDC authentication settings
- Add comprehensive test suite for OIDC functionality
- Add Authlib dependency for OpenID Connect support
* Enhance OIDC integration: configurable username claims, flexible role management, security improvements
- Add OIDC_USERNAME_CLAIM config for customizable username extraction
- Implement optional role mapping: preserve existing roles when no OIDC_GROUP_* env vars set
- Remove client_id exposure from public OIDC config endpoint
- Upgrade Authlib dependency to 1.6.4 for security fixes and improvements
* Implement multi-provider OIDC support with auto-detection
- Add support for Google, Microsoft, and GitHub built-in providers
- Auto-detect available providers from environment variables pattern OIDC_{PROVIDER}_CLIENT_ID
- Remove dependency on primary provider, make all providers truly optional
- Add provider-specific endpoints with validation and routing
- Implement username prefixing to prevent conflicts between providers
- Update configuration API to return list of available providers
- Handle GitHub OAuth 2.0 flow alongside standard OIDC providers
- Smart auto-redirect only when single provider is configured
* Remove unnecessary gunicorn dependency
The gunicorn package is not required for OIDC functionality.
The project already uses waitress as its production WSGI server.
The wsgi.py file supports gunicorn logging if available, but doesn't require it as a dependency.
* Fix Flask application context issues in OIDC provider detection
- Add app parameter to get_available_oidc_providers() function
- Add app parameter to get_provider_config() function
- Use Flask app config instead of os.getenv() for configuration access
- Update init_oidc() to pass app instance to provider functions
- Fixes RuntimeError when accessing current_app during app initialization
This resolves the 'Working outside of application context' errors
and enables proper OIDC provider detection during Flask startup.
* Fix built-in OIDC providers to use Flask config instead of os.getenv
* Add configurable custom OIDC provider name
- Allow setting custom display name via GRAMPSWEB_OIDC_NAME environment variable
- Fallback to 'OIDC' if no custom name is provided
- Add OIDC configuration documentation to README
* Move OIDC documentation to gramps-web-docs
Removes OIDC configuration documentation from README.md as it should be
documented in the main gramps-web-docs repository instead.
* Update apispec.yaml with OIDC endpoints
Adds OpenAPI documentation for the new OIDC authentication endpoints:
- /oidc/config: Get OIDC configuration and available providers
- /oidc/login/: Initiate OIDC login flow with provider
- /oidc/callback/: Handle OIDC callback and create JWT tokens
Includes schema definitions for OIDCConfig and OIDCProvider.
* Addressed main concerns of @DavidMStraub's review
- Fixed copyright headers to show only Alexander Bocken as author
- Changed HTTP status codes from 404 to 405 for disabled OIDC endpoints
- Updated error logging to use logger.exception() for better stack traces
- Fixed module docstring from "blueprint" to "resources"
- Moved current_app imports to top-level instead of local imports
- Removed GitHub email fallback that generated fake noreply addresses
- Updated log message to reference config options instead of env vars
- Implemented FRONTEND_URL with BASE_URL fallback using get_config()
- Added FRONTEND_URL to devcontainer environment for development
- Added FRONTEND_URL to allowed database config keys
- Removed default parameters from create_or_update_oidc_user function
* Fix OIDC endpoint status codes from 404 to 405 in API spec
* Improve OIDC token handover security with HTTP-only cookies
Replaces insecure URL-based token passing with a secure cookie-based architecture:
**Security Improvements:**
- Tokens no longer exposed in browser URLs or history
- HTTP-only cookies prevent XSS token access
- Automatic cookie cleanup after token exchange
- CORS credentials support for cross-origin cookie handling
**Architecture Changes:**
- Browser requests: Backend sets HTTP-only cookies → redirects to /oidc/complete
- Frontend calls new /api/oidc/tokens/ endpoint to exchange cookies for localStorage tokens
- API requests: Direct JSON response (backward compatible)
- Development/production cookie security auto-detection
**New Components:**
- OIDCTokenExchangeResource: Secure cookie-to-token exchange endpoint
- Enhanced CORS configuration with credentials support
- Smart browser vs API request detection
- Comprehensive error handling and logging
The flow eliminates token exposure while maintaining compatibility with both browser and API clients.
* Implement account source tracking and enhanced OIDC account matching
- Add account_source field to user details showing authentication source
- Create OIDC accounts table for secure account linking and tracking
- Add database migration for OIDC accounts table structure
- Enhance user API endpoints to include OIDC account information
- Implement comprehensive account matching for multi-provider support
- Update API specification to document new account_source field
* Remove backwards compatibility from OIDC callback endpoint
- Simplify OIDC callback to only handle browser redirects with cookies
- Remove unnecessary API request detection and JSON response fallback
- Update tests to verify redirect and cookie behavior instead of JSON
- Improve OIDC standard compliance and security
* Address @DavidMStraub's security and configuration concerns
- Set GRAMPSWEB_FRONTEND_URL to empty by default in dev container
- Change default OIDC user role from ROLE_GUEST to ROLE_DISABLED
- Add email notification to admins for new OIDC users requiring approval
This prevents automatic guest access for all users with external accounts
and ensures proper admin oversight of new registrations.
* add myself to copyright for files with signifcant changes, fix README newline
* Improve OIDC authentication implementation and security
Key changes:
• Align OIDC user experience with local registration flow
• Remove email matching feature to prevent privilege escalation across trees
• Remove unnecessary last_login field from oidc_accounts table
• Modernize Flask response handling (remove make_response wrapper)
• Fix security vulnerability: ROLE_DISABLED users now get confirmation page instead of tokens
• Improve code organization: move all imports to top-level
• Extract development environment detection to utility function
• Remove sensitive information from logging (no longer log all OIDC claims)
• Use current_app.debug instead of environment variable checking
• Simplify database schema by removing unused tracking fields
Security improvements:
• Prevent cross-tree privilege escalation via email matching
• Default new OIDC users to ROLE_DISABLED requiring admin approval
• Block token generation for disabled accounts
• Remove potential PII exposure in debug logs
Code quality improvements:
• Consolidate duplicate environment detection logic
• Organize imports consistently across OIDC modules
• Remove unused functions and database fields
• Modernize Flask patterns and response handling
* Update gramps_webapi/api/resources/oidc.py
Co-authored-by: David Straub <[email protected]>
* Refactor: Extract "custom" provider ID to constant
Replace hardcoded "custom" string with PROVIDER_CUSTOM constant for better maintainability and consistency throughout the OIDC authentication module.
* Refactor and expand OIDC unit tests
- Convert test_oidc_auth.py from unittest to pytest style
- Remove if __name__ == "__main__" blocks (not needed for pytest)
- Fix outdated function reference: get_role_from_groups -> get_role_from_claims
- Add comprehensive test coverage for new OIDC features:
* Multi-provider support (Google, Microsoft, GitHub, custom)
* get_available_oidc_providers() function
* get_provider_config() function
* PROVIDER_CUSTOM constant usage
* Custom and nested role claim support (e.g., realm_access.roles)
* OIDC account table lookups via sub claim mapping
* Username generation logic for different providers
* Username conflict resolution with counter suffix
* Role preservation when no mapping configured
- Improve test organization with dedicated test classes per function
- Add error handling tests for missing sub claim and invalid providers
* Fix circular imports and update OIDC tests
- Create oidc_helpers.py to break circular import chain
* Moved is_oidc_enabled() to separate module with no api imports
* Updated token.py, user.py, and oidc.py to import from oidc_helpers
- Use lazy imports in oidc.py for api module functions
* Import run_task, send_email_new_user, get_tree_id inside function
* Added comment explaining this intentional exception to top-level import standard
- Fix pre-existing syntax errors in oidc.py callback validation
* Added missing if-statement body for invalid tree check
* Fixed invalid '!tree' syntax to 'not tree'
- Update OIDC unit tests to work without Flask app context
* Mock current_app with context managers instead of decorators
* Mock get_tree_id and run_task to avoid database/task dependencies
* Remove run_task patches (no longer at module level)
* Fix test parameter ordering after removing current_app patches
All 34 OIDC unit tests now passing successfully.
* Fix OIDC_AUTO_REDIRECT default to False
Change default from True to False to match expected behavior where
users should explicitly opt-in to auto-redirecting to OIDC login.
* Add OIDC logout and backchannel logout support
This commit implements comprehensive OIDC logout functionality including
frontend SSO logout and OpenID Connect Back-Channel Logout support.
Backend changes:
- Add token blocklist system for JWT revocation (token_blocklist.py)
- Add /api/oidc/logout/ endpoint to retrieve provider logout URLs
- Add /api/oidc/backchannel-logout/ endpoint for OIDC Back-Channel Logout
- Include oidc_provider claim in JWT tokens to track auth method
- Integrate token blocklist with JWT manager for revocation checks
- Fix OIDC_AUTO_REDIRECT default to False
API changes:
- Update apispec.yaml with new logout endpoint documentation
- Add proper error handling and graceful degradation
Tests:
- Add 17 comprehensive tests for logout functionality
- Test OIDC logout endpoint (disabled, invalid provider, missing client)
- Test backchannel logout validation (all OIDC spec requirements)
- Test token blocklist operations and cleanup
- Test OIDC provider claim in tokens
- All tests passing (17/17)
* Fix OIDC server metadata URL construction and loading
Fixed two issues preventing OIDC authentication from working:
1. Fixed server_metadata_url construction: When OIDC_OPENID_CONFIG_URL
is not set, it now properly falls back to constructing the URL from
the issuer. The previous .get() with default was not working because
None is a valid value that prevented the fallback.
2. Added explicit metadata loading at startup: Call load_server_metadata()
immediately after registration to ensure the authorize_url and other
endpoints are available when needed, preventing 'Missing authorize_url'
runtime errors.
* Fix type annotation for oidc_provider parameter
* Fix OIDC tests for Python 3.13 compatibility and update test assertions
* Fix OIDC and JWT authentication tests
- Import get_tokens from correct module (api.resources.token)
- Patch is_oidc_enabled at usage locations, not definition
- Use app.config instead of environment variables for runtime config
- Fix expected status codes (405 for disabled OIDC endpoints)
- Fix error response JSON structure assertions
* last unit test fix, I swear
* Add issue templates
* Unpin sentence transformers (#695)
---------
Co-authored-by: David Straub <[email protected]>1 parent a8329f2 commit 99e86b6
File tree
19 files changed
+2956
-10
lines changed- .devcontainer
- alembic_users/versions
- gramps_webapi
- api
- resources
- auth
- data
- tests
- test_endpoints
19 files changed
+2956
-10
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| |||
Lines changed: 39 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
92 | 92 | | |
93 | 93 | | |
94 | 94 | | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
95 | 103 | | |
96 | 104 | | |
97 | 105 | | |
| |||
148 | 156 | | |
149 | 157 | | |
150 | 158 | | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
151 | 166 | | |
152 | 167 | | |
153 | 168 | | |
| |||
0 commit comments