Skip to content

Fix grant source for group-based vs direct app assignments#162

Open
c1-dev-bot[bot] wants to merge 1 commit into
mainfrom
fix/app-grant-source-group-vs-direct
Open

Fix grant source for group-based vs direct app assignments#162
c1-dev-bot[bot] wants to merge 1 commit into
mainfrom
fix/app-grant-source-group-vs-direct

Conversation

@c1-dev-bot
Copy link
Copy Markdown
Contributor

@c1-dev-bot c1-dev-bot Bot commented Apr 22, 2026

Summary

  • Add GrantExpandable annotation to group→app grants so the SDK expansion creates user-level grants with correct source tracking (pointing back to the originating group membership)
  • Filter listAppUsersGrants() to skip users with Scope == "GROUP", since those users will receive properly attributed grants via the expansion mechanism
  • Follows the same pattern already used for role group grants in role.go (roleGroupGrant)

Root Cause

The Okta connector was emitting all app user grants as direct grants regardless of whether the user was assigned directly or through a group. The Okta AppUser.Scope field (USER vs GROUP) was not being used to differentiate, and group→app grants lacked the GrantExpandable annotation needed for the SDK to properly track grant sources through permission hierarchy expansion.

Changes

pkg/connector/app.go:

  1. listAppGroupGrants(): Added GrantExpandable annotation with EntitlementIds pointing to group:{groupID}:member and Shallow: true, matching the role grant expansion pattern
  2. listAppUsersGrants(): Added filter to skip app users with Scope == "GROUP" — these users get their grants through the expansion mechanism with proper source attribution

Test plan

  • Verify against a test Okta tenant with:
    • (a) A user with only direct app access → appears as direct grant
    • (b) A user with only group-based app access → grant source points to the Okta group
    • (c) A user with both direct and group-based access → both grant types appear correctly
  • Verify that existing direct app user grants are unaffected
  • Verify grant expansion produces correct source attribution in access reviews

Fixes: CXH-1394


Automated PR Notice

This PR was automatically created by c1-dev-bot as a potential implementation.

This code requires:

  • Human review of the implementation approach
  • Manual testing to verify correctness
  • Approval from the appropriate team before merging

Group-to-app grants now include a GrantExpandable annotation pointing to
the group's membership entitlement, enabling the SDK expansion algorithm
to create user-level grants with correct source tracking.

Direct user-to-app grants now filter out users whose Scope is "GROUP"
(assigned through a group), since those users will receive properly
attributed grants via the expansion mechanism instead.

This follows the same pattern used for role group grants in role.go.

Fixes: CXH-1394
@c1-dev-bot c1-dev-bot Bot requested a review from a team April 22, 2026 13:30
@linear
Copy link
Copy Markdown

linear Bot commented Apr 22, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 22, 2026

Claude finished @c1-dev-bot[bot]'s task in 1m 30s —— View job


PR Review: Fix grant source for group-based vs direct app assignments

  • Gather context and read changed files
  • Verify the pattern against role.go reference implementation
  • Review the implementation
  • Post review feedback

Overall: This PR looks correct and follows the established pattern. The changes are small, focused, and address the root cause described in the PR body. Two minor observations below.

What's good

  1. GrantExpandable annotation (line 195-198): Correctly mirrors the pattern from roleGroupGrant() in role.go:419-423. The entitlement ID format group:{groupID}:member matches exactly what group.go:453 produces via NewAssignmentEntitlement(resource, "member", ...).

  2. Scope filter (line 235-237): Using strings.EqualFold for the comparison is defensive — Okta documents the scope as "USER" or "GROUP" (uppercase), but case-insensitive matching is the right call since the connector already sets scope with strings.ToUpper() in the Grant method (line 420), suggesting the casing isn't fully trusted.

  3. Shallow: true is correct here — app→group→user is a single-hop expansion, so deep traversal isn't needed.

Minor observations

  1. Missing shouldExpand optimization: The role grant pattern in group.go:266-270 checks if a group has zero members (usersCount == 0) and skips setting GrantExpandable for empty groups. This PR always sets the annotation unconditionally. This isn't a bug — the SDK will just do a no-op expansion for empty groups — but it's a minor divergence from the existing pattern. For app group assignments the ApplicationGroupAssignment object likely doesn't carry a member count, so this is probably the right pragmatic choice. Just calling it out for awareness.

  2. Edge case — users with both direct and group assignment: The PR description mentions this scenario (test plan item c). Since the filter skips Scope == "GROUP" users, a user with both direct and group-based access will get: (a) one direct grant from listAppUsersGrants (their Scope will be "USER" for the direct assignment), and (b) one expanded grant via the group pathway. This should work correctly because Okta returns separate AppUser records per scope. Worth confirming in testing.

Verdict

Approve-worthy. The implementation is clean, follows established patterns, and the two-pronged approach (expandable annotation + scope filter) is the correct way to fix grant source attribution. The test plan in the PR body covers the right scenarios.

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.

0 participants