Skip to content

fix(oidc): apply groupsFilter to single-string groups claim - #4920

Open
vidigoat wants to merge 1 commit into
dexidp:masterfrom
vidigoat:fix/oidc-groups-filter-string-claim
Open

fix(oidc): apply groupsFilter to single-string groups claim#4920
vidigoat wants to merge 1 commit into
dexidp:masterfrom
vidigoat:fix/oidc-groups-filter-string-claim

Conversation

@vidigoat

Copy link
Copy Markdown

Overview

Apply the configured groupsFilter to the groups claim even when the upstream IdP returns it as a single string instead of an array.

What this PR does / why we need it

createIdentity in the OIDC connector applies groupsFilter (from claimMutations.filterGroupClaims.groupsFilter) inside the loop that handles the array form of the groups claim. The single-string fallback, however, assigned the group directly:

// Fallback when claims[groupsKey] is a string instead of an array of strings.
if g, b := claims[groupsKey].(string); b {
    groups = []string{g}   // groupsFilter never applied
}

Some IdPs deliver the groups claim as a single string (which is exactly why this fallback exists). For those providers groupsFilter was silently a no-op: a group the operator configured the filter to exclude still landed in identity.Groups, and therefore in the ID token and every downstream authorization decision — failing open, with no signal to the operator.

This folds the single-string case into the array path (vs = []interface{}{g}; found = true) so groupsFilter — and the existing per-group prefix/map handling below it — applies uniformly. Behaviour is unchanged when no filter is configured (the existing singularGroupResponseAsString test still yields ["group1"]).

Special notes for your reviewer

Added a regression test (filterGroupClaimsString) to TestHandleCallback: a single-string groups claim of "groupA" with filter ^.*\d$ must be filtered out. It fails on master (leaks ["groupA"]) and passes with this change. go test ./connector/oidc/..., gofmt, and go vet are clean.

Disclosure: I used an LLM to help find and draft this fix; I reproduced the bug, wrote the test, and verified everything myself.

When an upstream IdP returns the groups claim as a single string rather
than an array, dex assigned it directly to the identity's groups and
skipped the configured groupsFilter entirely. Operators use groupsFilter
as an allow-list to restrict which upstream groups propagate into the ID
token (and downstream RBAC), so for those IdPs the filter silently failed
open and excluded groups leaked through.

Fold the single-string fallback into the array path so groupsFilter (and
the existing per-group handling) applies uniformly. Add a regression test
covering a filtered single-string groups claim.

Signed-off-by: Vidit Patankar <vidit.patankar16@gmail.com>
Copilot AI review requested due to automatic review settings July 22, 2026 01:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes OIDC group-claim mutation behavior so the configured groupsFilter is applied consistently whether the upstream IdP returns the groups claim as an array or a single string, preventing filtered-out groups from leaking into identity.Groups.

Changes:

  • Fold the single-string groups-claim fallback into the existing array-processing path so the same filtering and per-group handling runs for both formats.
  • Add a regression test covering the single-string groups claim with groupsFilter configured (ensuring the group is filtered out as expected).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
connector/oidc/oidc.go Routes single-string groups claim through the same per-group processing path so groupsFilter is applied uniformly.
connector/oidc/oidc_test.go Adds a regression test to ensure a string-form groups claim is filtered by groupsFilter the same way as an array-form claim.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants