From 5e3c37a4f725344a5ffa793ed175b911c0afdfe8 Mon Sep 17 00:00:00 2001 From: Jena Date: Tue, 21 Jul 2026 15:04:06 -0500 Subject: [PATCH 1/2] SDK-563 pass configurable preferUserId through tryUser to setUserID/setEmail --- src/authorization/authorization.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/authorization/authorization.ts b/src/authorization/authorization.ts index 24a6bdb7..d46782ea 100644 --- a/src/authorization/authorization.ts +++ b/src/authorization/authorization.ts @@ -52,11 +52,13 @@ export interface GenerateJWTPayload { export interface WithJWT { setEmail: ( email: string, - identityResolution?: IdentityResolution + identityResolution?: IdentityResolution, + preferUserId?: boolean ) => Promise; setUserID: ( userId: string, - identityResolution?: IdentityResolution + identityResolution?: IdentityResolution, + preferUserId?: boolean ) => Promise; logout: () => void; refreshJwtToken: (authTypes: string) => Promise; @@ -72,7 +74,8 @@ export interface WithoutJWT { ) => Promise; setUserID: ( userId: string, - identityResolution?: IdentityResolution + identityResolution?: IdentityResolution, + preferUserId?: boolean ) => Promise; logout: () => void; setNewAuthToken: (newToken?: string) => void; @@ -464,12 +467,12 @@ export function initialize( const handleTokenExpiration = createTokenExpirationTimer(); - const tryUser = () => { + const tryUser = (preferUserId?: boolean) => { let createUserAttempts = 0; return async function tryUserNTimes(): Promise { try { - return await updateUser({}); + return await updateUser({ preferUserId }); } catch (e) { if (createUserAttempts < RETRY_USER_ATTEMPTS) { createUserAttempts += 1; @@ -587,7 +590,8 @@ export function initialize( }, setUserID: async ( userId: string, - identityResolution?: IdentityResolution + identityResolution?: IdentityResolution, + preferUserId?: boolean ) => { clearMessages(); try { @@ -596,7 +600,7 @@ export function initialize( // Initialize user authentication first, then create user profile initializeUserId(userId); - await tryUser()(); + await tryUser(preferUserId)(); const result = await tryMergeUser(userId, false, merge); if (result.success) { @@ -963,7 +967,8 @@ export function initialize( }, setEmail: async ( email: string, - identityResolution?: IdentityResolution + identityResolution?: IdentityResolution, + preferUserId?: boolean ) => { /* clear previous user */ clearMessages(); @@ -978,7 +983,7 @@ export function initialize( initializeEmailUser(email); // Create user profile first before attempting merge - await tryUser()(); + await tryUser(preferUserId)(); const result = await tryMergeUser(email, true, merge); if (result.success) { @@ -1014,7 +1019,8 @@ export function initialize( }, setUserID: async ( userId: string, - identityResolution?: IdentityResolution + identityResolution?: IdentityResolution, + preferUserId?: boolean ) => { clearMessages(); try { @@ -1028,7 +1034,7 @@ export function initialize( initializeUserId(userId); // Create user profile after authentication is set up - await tryUser()(); + await tryUser(preferUserId)(); const result = await tryMergeUser(userId, false, merge); if (result.success) { From aaeb652830498d19a07e1ec269c822cafadf7631 Mon Sep 17 00:00:00 2001 From: Joao Dordio Date: Fri, 7 Aug 2026 13:41:04 +0100 Subject: [PATCH 2/2] SDK-563 add tests and changelog for preferUserId on setUserID/setEmail Lock in the tryUser handoff so identify-time /users/update respects an explicit preferUserId, matching the Care.com fix path. --- CHANGELOG.md | 2 + src/authorization/authorization.test.ts | 75 +++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a93e00f..92731315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Fixes +- Pass `preferUserId` through `setUserID` and JWT `setEmail` into the identify-time `/users/update` call (`tryUser`), so callers can opt out of user creation when identifying a user (SDK-563). ## [2.2.2] ### Fixes diff --git a/src/authorization/authorization.test.ts b/src/authorization/authorization.test.ts index 4cf8bbe1..c1b57f63 100644 --- a/src/authorization/authorization.test.ts +++ b/src/authorization/authorization.test.ts @@ -378,6 +378,11 @@ describe('API Key Interceptors', () => { }); describe('User Identification', () => { + const getUsersUpdatePayloads = () => + mockRequest.history.post + .filter((e: any) => !!e.url?.match(/users\/update/gim)) + .map((e: any) => JSON.parse(e.data)); + beforeEach(() => { setTypeOfAuthForTestingOnly('userID'); @@ -807,6 +812,26 @@ describe('User Identification', () => { expect(response.config.params.email).toBeUndefined(); expect(response.config.params.userId).toBe('999'); }); + + it('defaults preferUserId to true on the identify-time users/update call', async () => { + mockRequest.onPost('/users/update').reply(200, {}); + const { setUserID } = initialize('123'); + await setUserID('999'); + + const payloads = getUsersUpdatePayloads(); + expect(payloads.length).toBeGreaterThan(0); + expect(payloads[0].preferUserId).toBe(true); + }); + + it('passes preferUserId false through tryUser to users/update', async () => { + mockRequest.onPost('/users/update').reply(200, {}); + const { setUserID } = initialize('123'); + await setUserID('999', undefined, false); + + const payloads = getUsersUpdatePayloads(); + expect(payloads.length).toBeGreaterThan(0); + expect(payloads[0].preferUserId).toBe(false); + }); }); }); @@ -1011,6 +1036,32 @@ describe('User Identification', () => { expect(response.config.params.userId).toBeUndefined(); expect(response.config.params.email).toBe('hello@gmail.com'); }); + + it('defaults preferUserId to true on the identify-time users/update call', async () => { + mockRequest.resetHistory(); + mockRequest.onPost('/users/update').reply(200, {}); + const { setEmail } = initialize('123', () => + Promise.resolve(MOCK_JWT_KEY) + ); + await setEmail('hello@gmail.com'); + + const payloads = getUsersUpdatePayloads(); + expect(payloads.length).toBeGreaterThan(0); + expect(payloads[0].preferUserId).toBe(true); + }); + + it('passes preferUserId false through tryUser to users/update', async () => { + mockRequest.resetHistory(); + mockRequest.onPost('/users/update').reply(200, {}); + const { setEmail } = initialize('123', () => + Promise.resolve(MOCK_JWT_KEY) + ); + await setEmail('hello@gmail.com', undefined, false); + + const payloads = getUsersUpdatePayloads(); + expect(payloads.length).toBeGreaterThan(0); + expect(payloads[0].preferUserId).toBe(false); + }); }); describe('setUserID', () => { @@ -1186,6 +1237,30 @@ describe('User Identification', () => { ).toBe(4); } }); + + it('defaults preferUserId to true on the identify-time users/update call', async () => { + mockRequest.onPost('/users/update').reply(200, {}); + const { setUserID } = initialize('123', () => + Promise.resolve(MOCK_JWT_KEY) + ); + await setUserID('999'); + + const payloads = getUsersUpdatePayloads(); + expect(payloads.length).toBeGreaterThan(0); + expect(payloads[0].preferUserId).toBe(true); + }); + + it('passes preferUserId false through tryUser to users/update', async () => { + mockRequest.onPost('/users/update').reply(200, {}); + const { setUserID } = initialize('123', () => + Promise.resolve(MOCK_JWT_KEY) + ); + await setUserID('999', undefined, false); + + const payloads = getUsersUpdatePayloads(); + expect(payloads.length).toBeGreaterThan(0); + expect(payloads[0].preferUserId).toBe(false); + }); }); describe('refreshJwtToken', () => {