AUTH4D-256 browser SDK changeEmail() and removeEmail()
@auth4/sdk-browser gains two methods on Auth4BrowserClient for the change and remove actions
of AUTH4D-212 (repository file docs/api-spec/auth4d-212.md). They share the AUTH4D-149 (repository file docs/api-spec/auth4d-149.md) addEmail() flow and
add no endpoints or wire shapes.
Methods
changeEmail(options?: { signal?: AbortSignal }): Promise<ChangeEmailResult>;
removeEmail(options?: { signal?: AbortSignal }): Promise<RemoveEmailResult>;
interface ChangeEmailResult {
session: BrowserSession; // same user, session.user.email is the new address
email: string;
identity: { identityId: string; provider: "email" };
}
interface RemoveEmailResult {
session: BrowserSession; // same user, no email in session.user
}
Both follow the addEmail() rules:
- Call them synchronously from a user gesture. The window opens as
about:blankbefore the first await and is pointed at the hosted page once the intent exists. POST /t/{tenantId}/account/email/intentis sent with the in-memory access token and{ "client_id": "cli_…", "action": "change" }or"action": "remove".addEmail()now sends"action": "add", which the API treats exactly like no action.add_email_urimust be on the issuer’s origin and have the path for the action:/login/add-email,/login/change-emailor/login/remove-email. Any other page, including the page for another action, rejects with the action’s failure code before the window is navigated.- The SDK polls
/account/email/intent/tokenwith the poll secret it keeps in memory, honouringintervalandslow_down. The window never receives a token or the poll secret. - Only one email window can be pending. Calling the same method again focuses the window and
returns the same promise; calling another of the three rejects with
email_window_open. - Guests are refused with
guest_account, and an aborted signal closes the window and rejects withcancelled. - Before the tokens are swapped, the SDK checks that the local session is still the one that
created the intent (same generation and subject) and that the token set is for the same user.
Otherwise it rejects with
invalid_sessionand keeps the current session. Tokens stay in memory.
On success:
| Method | Session after | Event |
|---|---|---|
changeEmail() |
{ sub, email: <new address>, email_verified: true } |
email_changed |
removeEmail() |
{ sub } (the poll response has no identity or email) |
email_removed |
Errors
Auth4BrowserError.code values:
| Code | changeEmail() |
removeEmail() |
When |
|---|---|---|---|
invalid_session |
yes | yes | No session, the server ended it, or it changed mid-flow |
guest_account |
yes | yes | The player is a guest |
email_not_added |
yes | yes | The account has no email address |
only_sign_in_method |
yes | The address is the only way to sign in | |
email_sign_in_disabled |
yes | Email sign-in is off for the project | |
popup_blocked |
yes | yes | The browser refused the window |
email_window_open |
yes | yes | Another add, change or remove window is pending |
cancelled |
yes | yes | The player chose Not now, or the signal was aborted |
window_closed |
yes | yes | The window was closed before the player finished |
change_email_failed |
yes | Malformed response, wrong page, expiry or other failures | |
remove_email_failed |
yes | Malformed response, wrong page, expiry or other failures |
addEmail() is unchanged apart from the action field and the new email_window_open refusal.
Tests
packages/sdk-browser/src/index.test.ts(changeEmail and removeEmail (AUTH4D-256)): the intent body, page-path and origin checks, the change and remove results and events, the creation refusals, Not now, a closed window,slow_down, guests, a session change between polls and during the final poll, the single pending window andAbortSignal. Run withnpx vitest run --config vitest.config.tsinpackages/sdk-browser.tests/e2e/auth4d-256.browser-change-email.spec.ts(port 5356): a routed game page runs the SDK against the built/login/change-emailand/login/remove-emailpages with a mocked API. Run withpnpm exec playwright test -c tests/e2e/auth4d-256.playwright.config.ts.
Source: docs/api-spec/auth4d-256.md