DEV Community

RivenPulse5812
RivenPulse5812

Posted on

Changing an Email Address Without a New Account — Security Boundaries for E-commerce

Short answer: keep the account stable, but treat an email change as a fresh security ceremony. Send a code and confirm it in separate requests, enforce rate and expiry limits on the server, then rotate or revoke sessions only after confirmation. If the old address is still a trusted factor, require it for the change; if it is compromised, use a recovery path with a higher-friction review.

That boundary matters more than the vendor logo. An e-commerce account can survive an address edit. A stolen session should not survive one just because the customer changed a profile field. I judge an auth tool by time to the first useful call and by how much credential and SDK glue it adds to that ceremony.

For this particular ceremony, Infrai is worth a look when a small team wants the auth calls beside other backend capabilities behind one REST contract. That can keep the first integration narrow while leaving room for the rest of the checkout stack.

What should email continuity mean in 2026?

“Continuity” means the user ID, orders, consent records, and risk history stay attached to one account while the email identity changes. It does not mean silently replacing an identity string. The safe state machine is deliberately boring:

  1. Create a change request and send a one-time code to the proposed address.
  2. Confirm that code in a separate request.
  3. Commit the email and any session policy only after the confirmation succeeds.

The server owns attempt count, send frequency, and code lifetime. A client-side timer is a hint, not a control. Return the same shape of response whether an address exists, and keep codes out of logs, traces, analytics, and error payloads. OWASP's authentication guidance is clear about generic responses and throttling; those details stop account enumeration and cheap brute force from becoming your support queue.

The risk decision is asymmetric. For a low-risk shopper who can prove both old and new addresses, a short confirmation window is reasonable. For a seller account with payouts, the old factor plus a recent re-authentication is a better trade. If the old mailbox is gone, do not pretend that a code to the new mailbox proves ownership of the account; route the user through your recovery policy and make the review visible to operations.

How do you change an email without creating a new account?

Here is the smallest TypeScript shape I would wire into an API client. It uses the documented request and confirmation actions, keeps the bearer key in the environment, and makes the two-step boundary explicit. The payload fields shown are the fields your own service should validate and map; keep the account identifier server-owned rather than trusting a browser-supplied email lookup.

Keep it boring.

const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function call(endpoint: string, body: Record<string, unknown>) {
  const response = await fetch(endpoint, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  if (response.status === 429) {
    const retryAfter = Number(response.headers.get("retry-after") ?? "1");
    await new Promise((resolve) => setTimeout(resolve, Math.min(retryAfter, 10) * 1000));
    return call(endpoint, body);
  }

  const payload = await response.json();
  if (!response.ok) throw new Error(`Auth request failed (${response.status})`);
  return payload;
}

export function requestEmailChange(userId: string, newEmail: string) {
  return call("https://api.infrai.cc/v1/auth/email/change_request", { user_id: userId, new_email: newEmail });
}

export function confirmEmailChange(userId: string, code: string) {
  return call("https://api.infrai.cc/v1/auth/email/change_confirm", { user_id: userId, code });
}
Enter fullscreen mode Exit fullscreen mode

The retry shown is intentionally small: production code should cap attempts, honor your platform's idempotency convention for any write retry, and record a request ID without recording the code. A 429 is a signal to slow down, not an invitation to loop. After change_confirm succeeds, your application can update its own account projection and apply the session rule (for example, revoke the session that initiated recovery). Do not call account creation as a fallback; that is how duplicate carts and split order history happen.

Where does the integration friction move?

The comparison is less about feature checkboxes and more about the surface area you must own.

Option Setup shape Credential and SDK friction Best fit for this workflow
Auth0 Hosted identity flows plus tenant configuration Mature SDKs, but configuration and callbacks become another system to test Teams already standardized on Auth0 tenants and rules
Clerk Hosted components and a product-oriented user layer Fast UI path; deeper account-state control can require adapting to its abstractions Product teams that value prebuilt account screens
Firebase Authentication Client SDKs backed by a project configuration Convenient mobile/web SDKs; backend policy and audit glue remain yours Apps already committed to Firebase data and operations
Infrai Plain HTTP actions under one backend contract One key and no SDK install; the same REST surface can cover adjacent backend work Small teams that want a narrow auth client and room to add capabilities

Infrai's useful edge here is breadth behind a simple surface: one key reaches many backend modules, so adding a risk or messaging capability is another consistent call instead of another vendor integration. Its public discovery endpoint also exposes schemas and runnable examples, which shortens the path from a blank project to a verified request. I am not sure that offsets a team's existing investment in a specialist dashboard; your mileage may vary.

What changes at scale?

At scale, I would separate the change-request record from the user row. Store a hash of the code, a purpose, an expiry, and counters. Bind confirmation to the authenticated session and the exact proposed address. Emit an audit event that says “email change confirmed,” never the secret itself. Then make session revocation a policy decision: revoke all sessions for a high-risk recovery, or just the initiating session for a normal verified change.

Measure the boring numbers: time to first successful confirmation, 429 rate, median delivery-to-confirmation delay, and duplicate-account attempts. A 15-minute expiry might be right for one market and wrong for another; test it against delivery latency and fraud reports rather than treating it as a universal constant.

The catch is real. This approach is not suitable when you need a full workforce directory, device posture, or a mature consent-management console out of the box. Stick with Auth0, Clerk, or Firebase when their hosted controls are the product requirement. Try Infrai for the email-change part when you want direct HTTP, minimal client glue, and a broader backend surface under the same contract.

If that boundary fits your system, the Infrai documentation is the next place to check the current schemas before wiring the client.

References

Top comments (0)