Best Practice to Verify User Identity Before Email Update in Auth0

Hello Auth0 Support Team,

We have a question regarding email update verification in Auth0.

Scenario

A user signs up using Email/Password with the email:
nihal@myworth.ai

Later, the user attempts to update their email address to:
nihal@abc.ai

Question

What is the recommended and secure approach in Auth0 to verify that the user requesting the email change is a valid and authenticated user before updating the email on their account?

Specifically, we would like guidance on:

Whether Auth0 supports email change verification flows out of the box

Best practices to:

Confirm the user’s identity (e.g., re-authentication, MFA, password re-entry)

Verify ownership of the new email address

How to prevent unauthorised email updates if a session is compromised

Our Goal

We want to ensure that:

Only the rightful account owner can update the email

The new email address is verified before becoming active

The process aligns with Auth0 security best practices

Any documentation references or recommended implementation patterns would be very helpful.

Thank you for your support.

Best regards,
Team myworth.ai

1 Like

Hi @nihalm5930,

Welcome back to the Auth0 Community!

Allowing an email change based solely on an active session is risky because if a user leaves their browser unattended or if a session is hijacked, an attacker can “take over” the account by changing the email and then triggering a password reset.

A secure flow must solve three problems:

  • Authentication Freshness: Ensuring the person at the keyboard is still the owner (preventing session hijacking).
  • Proof of Control: Ensuring the new email address is actually reachable and owned by the user.
  • Account Recovery: Ensuring the original owner is notified so they can revert the change if it was unauthorized.

Here’s a 3-step solution for your needs:

1. Confirm Identity (Step-up Authentication)

Before you even show the “New Email” input field, you should require the user to re-verify. In Auth0, you can trigger this by redirecting the user to /authorize with specific parameters:

  • max_age=0: This forces the user to log in again, even if they have an active session.
  • MFA Challenge: If the user has MFA enabled, you should specifically trigger an MFA challenge.

2. Update the Email via Management API

Once the user has re-authenticated, your backend should call the Auth0 Management API PATCH /api/v2/users/{id} endpoint.

Security Best Practice Payload:

{
  "email": "nihal@abc.ai",
  "verify_email": true,
  "email_verified": false,
  "client_id": "YOUR_CLIENT_ID"
}
  • verify_email: true: Auth0 will automatically send a “Verification Email” to the new address.
  • email_verified: false: This ensures the user cannot use features of your app that require a verified email until they click the link in their new inbox.

3. Prevent Unauthorized Access (Post-Login Action)

To ensure the user doesn’t gain full access with an unverified “new” email, use an Auth0 Action to block them until the verification is complete.

exports.onExecutePostLogin = async (event, api) => {
  if (!event.user.email_verified) {
    api.access.deny('Please verify your new email address before continuing.');
  }
};

You will likely want to customize the Email Verification template in the Auth0 Dashboard (Branding > Email Templates). Ensure the “Redirect To” URL points to a success page in your application where you can instruct the user to log in again with their new credentials.

If you have any further questions, please don’t hesitate to reach out.

Have a good one,
Vlad

1 Like

Hi Team,

Thank you for the explanation — it was very helpful.

I’m looking for a bit more clarity around our specific use case to ensure we’re implementing this securely and correctly.

Use case:

  • A user with an existing, verified email address (e.g., user@existing-domain) is already active.

  • The user initiates an email update to a new address (e.g., user@new-domain).

  • We want to ensure that the existing verified email is not permanently replaced in Auth0 unless the new email address user@new-domainis successfully verified.

From our current understanding, updating the email via the Management API immediately replaces the existing email address, even when email_verified is set to false. This introduces a risk if the new email is never verified.

Questions / Scenarios:

  1. If a verification email is sent to the new email address user@new-domain but the user never completes the verification, does Auth0 still treat the new email as the primary email, effectively replacing the previously verified one user@existing-domain?

  2. In such a case, is there any supported way to roll back or recover the account if the new email is never verified?

Our goal is to ensure that:

  • The original verified email always provides a recovery path, and

  • Email ownership is fully proven before the change becomes effective.

Apologies for revisiting this topic, and I really appreciate your help in explaining the exact steps required to achieve this use case safely.

Looking forward to your response.

Best regards,
Team myworth.ai

Hi again @nihalm5930,

Yes, by default, updating the email attribute via the Management API is a destructive action that overwrites the existing value immediately. To achieve your goal—where the change only becomes “effective” once the new email is proven—use Account Linking. This allows you to treat the new email as a secondary identity until it is verified, at which point you can promote it to the primary identity.

To ensure the original email remains valid until the new one is confirmed, follow this workflow:

1. Create a “Shadow” User

When the user wants to change their email, do not update their current profile. Instead, create a new user in your database (connection) with the new email address.

  • API: POST /api/v2/users
  • Payload: Set email_verified: false and verify_email: true.

2. Link the Accounts

Link the newly created “shadow” identity to the existing, logged-in user profile.

  • API: POST /api/v2/users/{primary_id}/identities
  • The old email remains the Primary identity, and the new email becomes a Secondary identity.

3. Verification & Promotion (Auth0 Actions)

Use an Auth0 Post-Login Action to check if the secondary identity has been verified. If it has, you can then “swap” them or promote the new email to the primary spot.

exports.onExecutePostLogin = async (event, api) => {
  const secondaryEmail = event.user.identities.find(id => id.isSocial === false && id.user_id !== event.user.user_id);
  
  if (secondaryEmail && secondaryEmail.profileData && secondaryEmail.profileData.email_verified) {
    // Logic to promote the secondary email to primary
    // This usually involves a backend call to your API to swap the identities
  }
};

4. Implementation Logic for Rollback

Because the original email remains the primary identity throughout this process, the user can still log in with their old email if they lose access to the new one. If they decide to cancel the change, you will delete the secondary identity.

Have a good one,
Vlad

Hi Vlad,

Thank you for the detailed explanation - this is very helpful and aligns well with what we’re trying to achieve.

I have one clarification and a few follow-up questions around the final state of the account once the new email has been verified and promoted.

Once the secondary (new) email identity has been verified and promoted to become the primary identity:

  1. Is it supported and safe to delete the original (old) primary identity entirely?

  2. Are there any Auth0 constraints or best practices around removing the original identity after promotion (for example, impacts on user_id, logs, sessions, or refresh tokens)?

  3. Is there a recommended sequence for promotion and cleanup to avoid locking the user out (for example, promote → reauthenticate → delete old identity)?

Our intent is that after successful verification and promotion, the old email should no longer be usable for login, password resets, or recovery, while ensuring the account remains stable and auditable.

Thanks again for your guidance — this approach looks solid, and we just want to be sure we handle the final transition correctly.

Best regards,
Team myworth.ai

Hi again @nihalm5930,

To answer your specific questions regarding the “Cleanup” phase:

Is it supported and safe to delete the original (old) primary identity entirely?

It is safe, but typically you don’t delete the “user record”; you unlink the secondary identity and then update the primary identity’s email.

  1. Verify: The user clicks the link in the verification email for the secondary identity.
  2. Update Primary: Use the Management API (PATCH /users/{id}) to update the primary user’s email to the new email address and set email_verified: true.
  3. Unlink/Delete Secondary: Use the Unlink a User Identity API to remove the secondary identity. This prevents the “new” email from being associated with a separate, dangling account.

Are there any Auth0 constraints or best practices around removing the original identity after promotion (for example, impacts on user_id, logs, sessions, or refresh tokens)?

  • User ID Stability: Always try to keep the original user_id. If you delete the primary record and try to use the secondary record as the new primary, the user_id changes. This will invalidate existing Refresh Tokens and can break integrations.
  • Session State: Updating the email on the primary profile does not automatically kill the user’s current session, but the next time a token is issued (via a Refresh Token or new login), it will contain the updated email claim.

Is there a recommended sequence for promotion and cleanup to avoid locking the user out (for example, promote → reauthenticate → delete old identity)?

To ensure the user is never stuck in “limbo”, follow this logic:

  1. Promote: Update the Primary identity’s email to the new, verified email.
  2. Unlink: Remove the secondary identity record.
  3. Silent Re-auth: In your application, if you need the ID Token to reflect the new email immediately, perform a “Silent Authentication” (prompt=none) to get a fresh token without a full redirect.

Have a good one,
Vlad

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.