ADR-034: SSO Admin Synchronization and Configuration PrecedenceΒΆ
- Status: Accepted
- Date: 2026-01-17
- Deciders: Platform Team
ContextΒΆ
The EntraID role mapping feature (#2129) introduced automatic role assignment based on SSO group memberships. This raised two critical design questions:
-
Admin Status Synchronization: Should SSO logins update the
is_adminflag based on group membership, potentially revoking admin access for users who were manually granted admin via the Admin UI/API? -
Configuration Precedence: When SSO providers are bootstrapped from environment variables but also modifiable via Admin API, which source should take precedence on application restart?
Both decisions have significant security and operational implications.
DecisionΒΆ
1. Admin Status: Upgrade-Only SynchronizationΒΆ
Decision: SSO can only upgrade is_admin from False to True, never downgrade.
# Only UPGRADE is_admin via SSO, never downgrade
if should_be_admin and not user.is_admin:
user.is_admin = True
Rationale:
- The
is_adminflag is a platform-level override that grants["*"]permissions via JWT scopes - Manual admin grants via Admin UI/API are intentional decisions by platform administrators
- SSO should enhance access control, not unexpectedly revoke access
- The RBAC system (
platform_adminrole) already handles group-based role revocation with proper tracking (granted_by='sso_system') - To revoke admin access, administrators should use the Admin UI/API explicitly
Trade-offs:
| Benefit | Trade-off |
|---|---|
| Manual admin grants preserved | Users who gained admin via SSO keep it after losing the group |
| No unexpected access revocation | SSO is not fully authoritative for admin status |
| Simple, predictable behavior | Must use Admin UI to revoke admin |
Alternative Considered: Track grant source with admin_granted_by field to only revoke SSO-granted admins. Rejected due to schema change complexity and the fact that RBAC roles already provide this granularity.
2. Configuration Precedence: Smart MergeΒΆ
Decision: Use "smart merge" for provider_metadata during bootstrap: - Environment config provides defaults for keys not in database - Database values are preserved (Admin API changes survive restarts) - New environment keys introduced in upgrades apply automatically
Rationale:
- Admin API changes (like
sync_roles=false) should survive application restarts - New configuration options added in upgrades should apply without manual intervention
- Environment config establishes the baseline; Admin API provides customization
Trade-offs:
| Benefit | Trade-off |
|---|---|
| Admin API changes survive restarts | Env config changes for existing keys don't apply |
| New env keys apply automatically | Must use Admin API (or reset provider) to change existing keys |
| Predictable precedence rules | Slightly more complex mental model |
Example:
Env config: {"groups_claim": "groups", "new_feature": true}
DB config: {"groups_claim": "custom", "sync_roles": false}
Result: {"groups_claim": "custom", "new_feature": true, "sync_roles": false}
3. ID Token Trust ModelΒΆ
Decision: Trust the id_token received from the token endpoint without signature validation.
Rationale:
- The token is received directly from the IdP's token endpoint over HTTPS
- The OAuth flow (state, code exchange) has already been validated
- The token endpoint response is trusted by definition in OAuth 2.0
- Signature validation would require JWKS fetching, caching, rotation handling, and clock skew management
- The threat model (compromised IdP or MITM despite TLS) is beyond what signature validation prevents
Security Considerations:
- TLS provides transport security for the token endpoint request
- The code exchange validates the authorization code
- This approach is consistent with most OAuth client libraries
Implementation DetailsΒΆ
Admin Sync (sso_service.py)ΒΆ
Location: mcpgateway/services/sso_service.py:761-770
# Synchronize is_admin status based on current group membership
# NOTE: Only UPGRADE is_admin via SSO, never downgrade
# This preserves manual admin grants made via Admin UI/API
provider = self.get_provider(user_info.get("provider"))
if provider:
should_be_admin = self._should_user_be_admin(email, user_info, provider)
if should_be_admin and not user.is_admin:
logger.info(f"Upgrading is_admin to True for {email}")
user.is_admin = True
Config Precedence (sso_bootstrap.py)ΒΆ
Location: mcpgateway/utils/sso_bootstrap.py:329-346
# Smart merge for provider_metadata
if "provider_metadata" in provider_config and existing_provider.provider_metadata:
env_metadata = provider_config["provider_metadata"] or {}
db_metadata = existing_provider.provider_metadata or {}
merged_metadata = {**env_metadata, **db_metadata}
provider_config["provider_metadata"] = merged_metadata
ConsequencesΒΆ
PositiveΒΆ
- Predictable behavior: Administrators know that manual grants won't be revoked
- Upgrade safety: New configuration options apply without manual intervention
- Admin API respect: Customizations made via API persist across restarts
- Simple mental model: Clear rules for when each config source applies
NegativeΒΆ
- Partial SSO authority: SSO doesn't fully control admin status
- Config change complexity: Changing existing keys requires Admin API
- No signature validation: Relies on TLS and OAuth flow for token security
NeutralΒΆ
- RBAC roles (
platform_admin) still sync bidirectionally withgranted_bytracking - Environment config remains the source of truth for new deployments
- Existing deployments can reset provider to re-apply full env config
Operational GuidanceΒΆ
To Revoke Admin AccessΒΆ
Use the Admin UI or API to set is_admin=false. SSO will not revoke it automatically.
To Change provider_metadata After Initial BootstrapΒΆ
- Option A: Use Admin API to update the provider's metadata
- Option B: Delete the provider and let bootstrap recreate it from env config
To Force Env Config to WinΒΆ
Delete the provider via Admin API, then restart the application. Bootstrap will recreate it with full env config.
ReferencesΒΆ
- PR #2129: EntraID role mapping feature
- Microsoft UserInfo endpoint docs: https://learn.microsoft.com/en-us/entra/identity-platform/userinfo
mcpgateway/services/sso_service.py- Admin sync implementationmcpgateway/utils/sso_bootstrap.py- Config merge implementationdocs/docs/manage/sso-entra-role-mapping.md- Feature documentation
StatusΒΆ
Implemented in PR #2129. Monitor SSO login logs for admin upgrade events.