Skip to content

Fix: Refresh WorkOS memberships and roles during manual re-sync - #93

Merged
defunctl merged 8 commits into
mainfrom
fix/refresh-workos-memberships
Oct 28, 2026
Merged

defunctl merged 8 commits into
mainfrom
fix/refresh-workos-memberships

Conversation

@defunctl

@defunctl defunctl commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Main Changes

Administrators cannot currently repair stale WorkOS membership roles through the Users screen. Re-sync from WorkOS refreshes profile fields only, while Sync Roles from WorkOS reapplies the locally stored role. If a role-change webhook was missed, both actions can leave WordPress using the wrong role.

Both actions now fetch the user's current membership for the configured WorkOS organization. For example, a user changed from Admin to Member in WorkOS can be refreshed from WordPress administrator to subscriber using the configured role mapping, without waiting for another webhook.

What changes

  • Full re-sync refreshes membership, mapped WordPress role, and profile fields. Role-only sync refreshes membership and role. Existing CLI pull commands use the same full refresh.
  • A repeated refresh can repair local drift even when the WorkOS timestamp is unchanged. The linked identity and latest stored timestamp are checked again after the API request. Successful refreshes persist their timestamp to reject older webhook events delivered afterward.
  • Membership and profile caches are invalidated. Logins that omit role data preserve the stored membership role instead of overwriting it with member.
  • Admin actions require permission to edit each target user, plus permission to promote them when roles can change. Without a configured organization, full re-sync remains profile-only and does not require promotion permission.
  • Full refresh is an instance operation: admin and CLI callers receive UserSync, which receives MembershipSync and the API client through constructors. MembershipSync coordinates injected MembershipReader, OrganizationResolver, MembershipState, and MembershipWriter objects using an immutable MembershipSnapshot.

Failure handling

API failures, missing/inactive or ambiguous memberships, and unmapped roles return errors without assigning a fallback role. Missing or inactive membership is reported for review, not treated as an instruction to deprovision. The saved membership, mapped WordPress role, and applied membership timestamp are verified before reporting success; unresolved extra WordPress roles are reported rather than silently counted as synced. A later failure can leave earlier membership or role writes applied, so an error does not imply that every change was rolled back.

Use it

On Users, click Re-sync from WorkOS under a linked user's WorkOS ID. For several users, select them and choose Re-sync from WorkOS or Sync Roles from WorkOS from Bulk actions.

With WP-CLI and the plugin configured, the same full refresh is available below. Replace 42 with the linked WordPress user's ID:

wp workos user sync 42 --direction=pull

Validation

  • PHP 8.2, slic: 796 regular tests and 15 constant-configuration tests passed, including 35 added regression cases. The 39 full-refresh, membership, and admin cases also passed in isolation.
  • Full PHPCS and PHPStan passed; frontend assets built for static analysis. GitHub CI also passed on the current PR head, including PHP tests on 8.0, 8.1, 8.2, and 8.3, PHPCS, PHPStan, and the JS build.
  • Real WP-CLI help commands for workos user sync and workos sync pull loaded successfully. An admin-context bootstrap check confirmed exactly one shared UserSync and RoleMapper hook registration.
  • Architecture, behavior/test, and dedicated security reviews completed with subagents. Findings were fixed and covered by regressions, including failed timestamp writes and identity or version changes during profile and membership API requests.
  • Tests use mocked WorkOS HTTP responses with real WordPress users, roles, custom tables, and caches. This branch has not been deployed or verified against live WorkOS through the browser.

The existing webhook handlers and manual refresh are not serialized by a shared lock; these checks do not provide atomic ordering across simultaneous writers.

This provides manual repair when automatic delivery was missed. Each environment still needs its own correctly configured WorkOS webhook endpoint and signing secret.

@defunctl defunctl self-assigned this Oct 27, 2026
@defunctl defunctl added this to the 1.0.11 milestone Oct 27, 2026
@defunctl
defunctl marked this pull request as ready for review October 27, 2026 18:06
@defunctl
defunctl requested review from bordoni and johnhooks October 27, 2026 18:06
@defunctl defunctl changed the title Refresh WorkOS memberships and roles during manual re-sync Fix: Refresh WorkOS memberships and roles during manual re-sync Oct 27, 2026
@bordoni

bordoni commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Before

93-resync-before.mp4

After

93-resync-after.mp4
93-bulk-roles-after.mp4

@defunctl
defunctl merged commit 10a0e8b into main Oct 28, 2026
7 checks passed
@defunctl
defunctl deleted the fix/refresh-workos-memberships branch October 28, 2026 18:00
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