feat: change a user's email from the WorkOS Users admin page#33
Merged
Conversation
The `profile` REST arg registered `sanitize_title` directly as its sanitize_callback. WordPress invokes sanitize callbacks as `call_user_func( $cb, $value, $request, $key )`, so the request object landed in sanitize_title's `$fallback_title` parameter. When the input sanitizes to an empty string — as it does for the common `profile: ""` payload — sanitize_title returns the fallback, handing back the WP_REST_Request object. The subsequent `(string)` cast in send_reset() then fataled with "Object of class WP_REST_Request could not be converted to string", surfacing as a 500. Wrap the sanitizer in a closure so only the value reaches sanitize_title. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a "Change email" action to admin.php?page=workos-users, alongside "Open in WorkOS" and "Send password reset". The change-email REST flow now branches on who is acting. A privileged admin acting on another account (edit_users && initiator !== target) commits the change immediately to WorkOS + WordPress, with no token and no verification email; self-service callers keep the emailed-token verified flow unchanged. The shared commit logic (WorkOS update, local mirror, rollback, webhook race-guard, sync-hash refresh) is extracted from confirm() into commit_change() so both paths share one implementation. Admins acting on another account also see the real "already in use" conflict instead of the enumeration-safe response, since they can already enumerate accounts; self-service stays enumeration-safe. Forced admin changes are recorded as a distinct email_change.admin_changed audit event and send no user notification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The admin Users page assumed the change-email response was always an immediate commit, but an admin acting on their *own* account falls into the verified (emailed-token) flow and returns no `committed` flag. The page would rewrite the row and report "Email changed" for a change that hadn't landed yet. Branch on `committed`/`no_op` instead, and only mutate the row on an actual commit; otherwise report that verification was sent. Also announce error notices assertively (role="alert") so screen readers catch a failed action, and add wpunit coverage for the admin-acting-on- self verified path and the admin-direct commit on a WorkOS-unlinked user. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
wp_update_user() mails WordPress core's "Notice of Email Change" to the old address whenever the email changes. The admin-direct path skipped our own notifier but not this one, so the "silent" admin change wasn't silent — and the test asserting no mail failed. Suppress it via the send_email_change_email filter, scoped to the admin commit so the self-service confirm path keeps its core notice. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the initiate() intro comment that restated is_admin_action()'s own docblock, and tighten the commit_admin_change docblock and the React discriminator comment. Keep the comments that carry non-obvious why — the commit_change invariants, the rate-limit bypass, and the WP-core email-notice suppression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bordoni
requested changes
Jun 30, 2026
bordoni
left a comment
Owner
There was a problem hiding this comment.
Can I get this to include some documentation and readme updates based on the changes?
Also changelog
…change-email-action
….com:bordoni/integration-workos into feature/workos-users-change-email-action
Add documentation, README/AGENTS, and changelog coverage for the WorkOS-Users-page change-email action and the admin-direct immediate-commit behavior, and bump to 1.0.8. - change-email.md: new "Admin-direct vs. self-service" matrix; admin response shape and 409 on initiate; Users-page surface; admin_changed event; drop the removed admin_bypass setting; test count 13 -> 19. - README.md / AGENTS.md: reflect the new surface, admin-direct path, and admin_changed event. - CHANGELOG.md / readme.txt: 1.0.8 entries for the change-email work (#33) and the password-reset empty-profile 500 fix (#32). - Remove the now-unused change_email_admin_bypass_verification option. - Bump Version / Stable tag / package.json to 1.0.8. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Trim the change-email and password-reset entries to a lead line plus a sentence or two, matching the surrounding changelog style; the PR carries the full detail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bordoni
approved these changes
Jun 30, 2026
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
[CONS-553]
Adds a Change email action to the WorkOS Users admin page (
admin.php?page=workos-users), alongside the existing "Open in WorkOS" and "Send password reset". An admin can change a user's email and have it flow to WorkOS and mirror into WordPress.The behavior depends on who's acting: an admin acting on another account commits the change immediately (no verification round-trip), while self-service email changes keep the existing emailed-token verification flow. Since all admin surfaces (
users.phprow action and the profile panel) hit the same endpoint, they get the immediate behavior too.Artifact
https://www.loom.com/share/40d99f675e3c4ed198e517fe34951eb6
What changed
Backend
RestApi.php: extracted the commit logic (WorkOS update →wp_update_user→ rollback → webhook race-guard → sync-hash refresh) out ofconfirm()into a sharedcommit_change(). ~Half of this file's diff is that extraction (moved, not new).RestApi.php:initiate()branches onis_admin_action()(edit_users && initiator !== target) — admins commit immediately viacommit_admin_change(); self-service runs the verified flow. Admins see the real409 "already in use"conflict; self-service stays enumeration-safe. Forced changes log a distinctemail_change.admin_changedevent and send no user notification.AdminPage.php: localizeschangeEmailUrl+changeEmailEnabledto the React page.Assets.php: flow-agnostic modal strings + asuccessImmediatestring for the shared handler.Frontend
admin-users/index.tsx: native "Change email" button — own modal, immediate POST, in-place row refresh, success/error notice, per-row busy state.admin-change-email/index.ts: response-driven success messaging (immediate vs. pending verification) for theusers.php/profile surfaces.Tests
ChangeEmailRestApiTest.php: repointed two initiate tests to self-service actors; added an admin-immediate-commit test and the admin-reveal vs. self-service-enumeration-safe conflict pair.🤖 Generated with Claude Code