Skip to content

fix: extract groups from id_token for generic OIDC providers#3597

Merged
crivetimihai merged 3 commits intoIBM:mainfrom
SharonDiskin:fix/generic-oidc-groups-claim
Mar 23, 2026
Merged

fix: extract groups from id_token for generic OIDC providers#3597
crivetimihai merged 3 commits intoIBM:mainfrom
SharonDiskin:fix/generic-oidc-groups-claim

Conversation

@SharonDiskin
Copy link
Copy Markdown
Contributor

@SharonDiskin SharonDiskin commented Mar 10, 2026

Summary

Generic OIDC providers like Okta include group/role claims in the id_token but
not in the /userinfo response. The existing code handles this for Entra ID and
Keycloak with provider-specific extraction, but the generic OIDC code path drops
groups entirely.

This PR adds two fixes:

  1. _get_user_info: Extract groups and roles claims from the verified id_token
    for generic OIDC providers (any provider not explicitly handled: Entra, Keycloak,
    GitHub, Google). Only copies claims missing from the userinfo response to avoid
    overwriting.

  2. _normalize_user_info: Include groups in the normalized return dict for the
    generic OIDC path. Supports a configurable groups_claim key via provider_metadata,
    defaulting to groups. Also merges roles into the groups list, matching the Entra ID
    behavior.

Without this fix, _apply_team_mapping always receives an empty groups list for
generic OIDC providers, so SSO-based team assignment never works.


Type of Change

  • Bug fix

Verification

Tested with Okta SSO on a live deployment:

  • Configured Okta authorization server with groups scope and claim
  • Verified the authorize URL includes scope=openid+profile+email+groups
  • Confirmed team mapping config in sso_providers table is read correctly
  • User login creates user and triggers _apply_team_mapping flow

Okta requirement: The groups claim must be configured on the ID Token (and
optionally Access Token) in the Okta Authorization Server Claims settings, with a
groups filter (e.g. Matches regex .*).


Checklist

  • No secrets or credentials committed
  • Code follows existing patterns (mirrors Entra ID and Keycloak handling)
  • No breaking changes — only adds behavior for previously unhandled providers

Notes

  • The groups_claim key is configurable via provider_metadata to support providers
    that use a non-standard claim name
  • The roles claim is merged into groups to support providers that use roles instead
    of groups for RBAC
  • The not in user_data guard ensures we never overwrite groups already returned by
    the userinfo endpoint" --jq '.html_url'

@SharonDiskin SharonDiskin reopened this Mar 11, 2026
@SharonDiskin SharonDiskin force-pushed the fix/generic-oidc-groups-claim branch from 77e1eb4 to 21e2ef6 Compare March 11, 2026 11:28
@crivetimihai crivetimihai added the bug Something isn't working label Mar 14, 2026
@crivetimihai crivetimihai added this to the Release 1.0.0 milestone Mar 14, 2026
@crivetimihai crivetimihai added the security Improves security label Mar 14, 2026
@crivetimihai
Copy link
Copy Markdown
Member

Thanks @SharonDiskin — generic OIDC group extraction from id_token is needed for proper team/role mapping. Targeting 1.0.0.

@crivetimihai crivetimihai added the SHOULD P2: Important but not vital; high-value items that are not crucial for the immediate release label Mar 20, 2026
…iders

Okta and IBM Verify providers had no groups extraction in their
_normalize_user_info handlers, and were excluded from the generic OIDC
id_token groups merge in _get_user_info. This meant _apply_team_mapping
always received an empty groups list for these providers, making SSO-based
team assignment non-functional.

Changes:
- Add groups/roles extraction to Okta and IBM Verify _normalize_user_info
  handlers (configurable groups_claim via provider_metadata, roles merged
  into groups, non-string elements filtered — same pattern as Entra ID)
- Include Okta and IBM Verify in the generic OIDC id_token groups merge
  path (previously excluded), with support for both configurable
  groups_claim and roles claims
- Add roles claim merging to the generic OIDC _normalize_user_info path
  (previously only Entra ID merged roles)
- Add sso_okta_scope and okta_group_mapping config settings for
  configurable Okta OIDC scopes and team mapping via environment
- Update bootstrap to use configurable Okta scope, parse team mapping
  from JSON env var, and preserve DB scope/team_mapping on restart
- Deduplicate groups across all generic/Okta/IBM Verify normalization

Based on PR IBM#3597 by SharonDiskin.
Closes IBM#3597

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
- Fix bootstrap scope preservation: only preserve DB scope when env
  provides the default value; a non-default env scope now correctly
  overrides a custom DB scope on restart
- Handle single-string roles claims in Okta, IBM Verify, and generic
  OIDC normalization (previously only list values were merged)
- Validate OKTA_GROUP_MAPPING is a JSON object after parsing; non-dict
  values (arrays, strings, numbers) now warn and fall back to empty
- Add SSO_OKTA_SCOPE and OKTA_GROUP_MAPPING to .env.example

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…hema

Update configuration.md table, docs/config.schema.json, and
docs/docs/config.schema.json with the two new Okta settings.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai crivetimihai self-assigned this Mar 23, 2026
@crivetimihai crivetimihai force-pushed the fix/generic-oidc-groups-claim branch from b5abac8 to 0a902f8 Compare March 23, 2026 02:03
@crivetimihai
Copy link
Copy Markdown
Member

Rebased onto current main and expanded the implementation. Here's what changed:

Core fixes:

  • Okta and IBM Verify _normalize_user_info handlers now extract groups (configurable groups_claim via provider_metadata, roles merged into groups, non-string elements filtered, deduplicated)
  • Removed Okta and IBM Verify from the exclusion list in _get_user_info so they use the generic OIDC id_token groups merge path (they have no dedicated extraction unlike Keycloak/Entra)
  • Generic OIDC _normalize_user_info path now merges roles claim into groups (previously only Entra did this)
  • Added sso_okta_scope and okta_group_mapping config settings
  • Bootstrap uses configurable Okta scope, parses team mapping from JSON env var, preserves DB scope/team_mapping on restart

Hardening (from review):

  • Single-string roles values are now handled (not just lists)
  • OKTA_GROUP_MAPPING validates parsed JSON is a dict (non-dict values warn and fall back to empty)
  • Bootstrap scope preservation only applies when env provides the default — a custom env scope correctly overrides DB

Docs:

  • .env.example, docs/docs/manage/configuration.md, docs/config.schema.json, docs/docs/config.schema.json all updated with new settings

Tests: 41 new tests, 265 total SSO tests passing.

Copy link
Copy Markdown
Member

@crivetimihai crivetimihai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased, expanded, reviewed, and hardened. All 265 SSO tests pass. LGTM.

@crivetimihai crivetimihai merged commit 00e9c21 into IBM:main Mar 23, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working security Improves security SHOULD P2: Important but not vital; high-value items that are not crucial for the immediate release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants