Hi everyone,
I’ve been implementing a bulk + JIT migration flow from Azure AD B2C to Auth0, and I’ve hit a strange issue where Auth0 creates a duplicate user profile on the first login, even though my custom DB login script correctly returns the existing user_id.
I’ve spent the whole day reproducing this, and I wanted to document everything clearly for you.
Environment
-
Auth0 tenant:
dev-cnhsg0roe6qny5na.us.auth0.com -
Connection name:
b2c-migrated-users -
Connection type: Custom Database
-
Migration model: Bulk import (via
/api/v2/jobs/users-imports) + JIT password validation + pure JIT account creation- Bulk file schema:
{
“email”: “billy@contoso.com”,
“password”: “BULK_INTERNAL_ONLY__aA1!”,
“email_verified”: true,
“app_metadata”: {
“migration_status”: “pending”,
“healthcare_status”: “provider”
}
}
- Bulk file schema:
Migration flow testing steps
-
Keep “Import users to Auth0” turned on for successful bulk migration. Run bulk import (.net console app)→ user lands in directory with
migration_status = pendingand user_id =auth0|692e5d4f9ffe27709aae8990. -
Turn OFF “Import users to Auth0” in connection settings.
(“Disable signups” also ON, though preferably I believe we would want to keep this toggle off, so that we can allow new users (non b2c) to sign up in auth0 even during migration.) -
Login as that user through the hosted login page.
testing url for now:
https://dev-cnhsg0roe6qny5na.us.auth0.com/authorize?client_id=vbgZM0DB2eq73qXxGNdV2LDJEHy8j9sa&response_type=token&redirect_uri=https://jwt.io&scope=openid%20profile%20email&connection=b2c-migrated-users
→ Custom DBloginscript runs.
→ Script finds the same Auth0 user by email, validates credentials against legacy API, and PATCHes via Management API to mark migration_status =completed. -
Auth0 creates a second user instead of reusing the existing one.
Observed results
After the first successful login:
auth0|692e5d4f9ffe27709aae8990 <- bulk-imported record (status=completed)
auth0|auth0|692e5d4f9ffe27709aae8990 <- duplicate created by Auth0 core
Both have the same email and same connection (b2c-migrated-users).
Key log excerpts
LOGIN FIND_USER chosen user_id=auth0|692e5d4f9ffe27709aae8990 connection=b2c-migrated-users app_metadata={"migration_status":"pending"}
LOGIN AUTH (bulk pending) status=200 body={"success":true,"user":{"user_id":"b2c|9d38edcd-c563-4eff-b749-4cf958474e79",...}}
LOGIN MGMT PATCH response status=200 ... user_id=auth0|692e5d4f9ffe27709aae8990
LOGIN RETURNING MIGRATED USER: auth0|692e5d4f9ffe27709aae8990
LOGIN NORMALIZED returning user_id=auth0|692e5d4f9ffe27709aae8990 connection=b2c-migrated-users
Everything looks perfect – yet Auth0 still spawns a second profile.
Login script summary
-
Finds Auth0 user by email via Management API
/users-by-email. -
If
migration_status=pending:-
Calls legacy
/authAPI. -
On success, PATCHes Auth0 user (password +
migration_status=completed). -
Returns the same user_id and connection:
-
const normalizedUser = { // Correct pattern for Auth0 DB connection users user_id: auth0User.user_id.replace(/^auth0\|auth0\|/, 'auth0|'), email: auth0User.email, email_verified: true, name: auth0User.name, given_name: auth0User.given_name, family_name: auth0User.family_name, app_metadata: auth0User.app_metadata || {}, connection: dbConnection };
-
-
No
createorverifylogic runs. -
I have tried testing this with this setting as well: “Disable signups” is ON.
-
“Import users to Auth0” is OFF during login.
-
What I expected
Auth0 should recognize the existing imported record (auth0|GUID) and reuse it after the password is patched, resulting in a single user entry.
What actually happens
Auth0 keeps the original record and creates a second one (with a double-prefixed auth0|auth0|GUID or similar variant).
This occurs even though:
-
The returned profile’s
user_idmatches the existing record exactly. -
The connection is specified.
-
The password and metadata are patched successfully (for both user record profiles) before the callback.
Troubleshooting already done
-
Verified that
createscript never runs. -
Tried all toggle combinations:
-
“Import users to Auth0” ON/OFF.
-
“Disable signups” ON/OFF.
-
-
Confirmed Management API PATCH executes and returns 200.
-
Confirmed the returned profile matches the existing Auth0 ID.
Questions
-
Why would Auth0 still auto-create a new profile when the custom DB login returns an existing
user_id? -
Is there a hidden setting or expected flag to tell Auth0 not to create a new record* after a successful login in a custom DB connection with bulk migration?
-
Is this a known issue with bulk + JIT hybrid flows?
Any official insight from the Auth0 engineering or community team would be hugely appreciated, we’re building a full migration demo and want to make sure we’re following best practice.
Thanks in advance,
– Ray Garg