feat(fga): implement effective-permissions listing endpoints - #84
Merged
Conversation
The Node SDK's listEffectivePermissionsByExternalId() and listEffectivePermissions() 404ed unconditionally because the emulator served no effective-permissions routes. Add the production routes: - GET /authorization/organization_memberships/:id/resources/:resource_type_slug/:external_id/permissions - GET /authorization/organization_memberships/:id/resources/:resource_id/permissions - GET /authorization/resources/:resource_id/organization_memberships/:membership_id/permissions The emulator has no resource-scoped role assignments or ancestor inheritance, so the effective set is the membership's full permission set; the resource path segments gate 404s (unknown resource, unknown membership, cross-org access). Fixes #81
Greptile SummaryThe PR adds three effective-permissions routes and aligns membership-role resolution across authorization checks, role assignments, and token minting.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
|
…zation Address review findings on the effective-permissions endpoints: - The external-id route matched the first resource with the given type and external id across all organizations, so a cross-org collision could 404 a valid request. The lookup now requires the membership's organization. - getPermissionsForMembership picked whichever same-slug role was inserted first; an organization role now shadows an environment role with the same slug. Adds regression tests for both cases.
Review follow-ups on the effective-permissions endpoints: - The resource-centric route swaps parameter order relative to the membership-centric routes but had no error-path coverage; add 404 specs for an unknown resource and an unknown membership. - The org-over-environment role shadowing lives in the shared getPermissionsForMembership helper, so assert it through /check as well, not just the new listing route.
…tion The access token's permissions claim resolved the membership role with first-match logic, so an environment role inserted before a same-slug organization role won — while /check and the effective-permissions endpoints resolve the organization role. Extract resolvePrimaryRole into role-helpers and use it in both places so the token claim always agrees with the authorization endpoints. The resolver matches on organization_id rather than role type because seeded roles can carry an organization_id while type defaults to EnvironmentRole.
…sions # Conflicts: # src/workos/routes/authorization-checks.ts
Seed normalization lets a role carry an organization_id while its type defaults to EnvironmentRole. The primary-role fallback matched on type alone, so another organization's seeded role could satisfy it by insertion order and leak its permissions into effective-permission responses, checks, and token claims. The fallback now also requires a null organization_id.
…resolution The role_slug path of role-assignment creation fell back to an ownership-agnostic findEnvRole, so a foreign organization's seeded role whose type defaulted to EnvironmentRole could be assigned to a local membership, leaking its permissions into checks and effective permissions. Route the lookup through resolvePrimaryRole, whose environment fallback requires a null organization_id.
The back-compat role_id branch of role-assignment creation loaded any role by id without checking ownership, so another organization's role could be assigned to a local membership and leak its permissions into checks and effective permissions. Assigned roles must now belong to the membership's organization or the environment, however addressed.
An environment role is unowned by definition, but findEnvRole matched on type alone, so a seeded role carrying an organization_id with its type defaulted to EnvironmentRole was addressable as an environment role from any organization. Require a null organization_id at the helper so every caller inherits the boundary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #81.
workos.authorization.listEffectivePermissionsByExternalId()(andlistEffectivePermissions()) 404ed unconditionally against the emulator because no effective-permissions route existed.Changes
Three routes sharing one handler, verified against the paths
@workos-inc/nodeactually builds:GET /authorization/organization_memberships/:id/resources/:resource_type_slug/:external_id/permissions— used bylistEffectivePermissionsByExternalId()GET /authorization/organization_memberships/:id/resources/:resource_id/permissions— production controller's by-id variantGET /authorization/resources/:resource_id/organization_memberships/:membership_id/permissions— used bylistEffectivePermissions()(note: the SDK builds this resource-centric path, not the membership-centric by-id shape)Responses are standard paginated lists of permission objects (
formatListResponse+formatPermission), compatible with the SDK'sAutoPaginatable.Semantics: the emulator has no resource-scoped role assignments or ancestor inheritance, so the effective set is the membership's full permission set (primary role + role assignments, same as
.../check). The resource segments gate 404s: unknown membership, unknown resource, and cross-org resources all 404.Testing
typecheck,lint,fmt:checkclean.gen:supported: FGA endpoint coverage 13/19 → 15/19 (overall 143 → 145 of 212).