Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions __tests__/setUserJWT.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Tests for setUserJWT functionality
*/

const { NativeModules } = require('react-native');
const Intercom = require('../src/index').default;

// Mock the native module
jest.mock('react-native', () => ({
NativeModules: {
IntercomModule: {
setUserJwt: jest.fn(),
},
IntercomEventEmitter: {
UNREAD_COUNT_CHANGE_NOTIFICATION:
'IntercomUnreadCountDidChangeNotification',
WINDOW_DID_HIDE_NOTIFICATION: 'IntercomWindowDidHideNotification',
WINDOW_DID_SHOW_NOTIFICATION: 'IntercomWindowDidShowNotification',
},
},
NativeEventEmitter: jest.fn(),
Platform: {
OS: 'ios',
select: jest.fn((config) => config.ios || config.default),
},
}));

describe('setUserJWT', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('should call native setUserJwt method with correct JWT token', async () => {
const mockJwt =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

NativeModules.IntercomModule.setUserJwt.mockResolvedValue(true);

const result = await Intercom.setUserJwt(mockJwt);

expect(NativeModules.IntercomModule.setUserJwt).toHaveBeenCalledWith(
mockJwt
);
expect(result).toBe(true);
});

test('should handle empty JWT token', async () => {
const emptyJwt = '';

NativeModules.IntercomModule.setUserJwt.mockResolvedValue(true);

const result = await Intercom.setUserJwt(emptyJwt);

expect(NativeModules.IntercomModule.setUserJwt).toHaveBeenCalledWith(
emptyJwt
);
expect(result).toBe(true);
});

test('should handle null JWT token', async () => {
const nullJwt = null;

NativeModules.IntercomModule.setUserJwt.mockResolvedValue(true);

const result = await Intercom.setUserJwt(nullJwt);

expect(NativeModules.IntercomModule.setUserJwt).toHaveBeenCalledWith(
nullJwt
);
expect(result).toBe(true);
});

test('should handle rejection from native module', async () => {
const mockJwt = 'invalid-jwt-token';
const mockError = new Error('Invalid JWT token format');

NativeModules.IntercomModule.setUserJwt.mockRejectedValue(mockError);

await expect(Intercom.setUserJwt(mockJwt)).rejects.toThrow(
'Invalid JWT token format'
);
expect(NativeModules.IntercomModule.setUserJwt).toHaveBeenCalledWith(
mockJwt
);
});

test('should handle complex JWT token structure', async () => {
const complexJwt =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE2MjQyNjIyLCJhdWQiOiJpbnRlcmNvbS1hcHAiLCJpc3MiOiJteS1hcHAifQ.K1fOjxHcxtRYFfKZPEfUCzUXLp8sSpgPbMa8t0gE6A0';

NativeModules.IntercomModule.setUserJwt.mockResolvedValue(true);

const result = await Intercom.setUserJwt(complexJwt);

expect(NativeModules.IntercomModule.setUserJwt).toHaveBeenCalledWith(
complexJwt
);
expect(result).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ public class IntercomErrorCodes {
public static final String UNIDENTIFIED_REGISTRATION = "101";
public static final String IDENTIFIED_REGISTRATION = "102";
public static final String SET_USER_HASH = "103";
public static final String UPDATE_USER_HASH = "104";
public static final String LOG_EVENT_HASH = "105";
public static final String LOGOUT = "106";
public static final String SET_LOG_LEVEL = "107";
public static final String GET_UNREAD_CONVERSATION = "108";
public static final String SET_USER_JWT = "104";
public static final String UPDATE_USER_HASH = "105";
public static final String LOG_EVENT_HASH = "106";
public static final String LOGOUT = "107";
public static final String SET_LOG_LEVEL = "108";
public static final String GET_UNREAD_CONVERSATION = "109";
public static final String DISPLAY_MESSENGER = "201";
public static final String DISPLAY_MESSENGER_COMPOSER = "202";
public static final String DISPLAY_CONTENT = "203";
Expand Down
12 changes: 12 additions & 0 deletions android/src/main/java/com/intercom/reactnative/IntercomModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,18 @@ public void setUserHash(String userHash, Promise promise) {
}
}

@ReactMethod
public void setUserJwt(String userJwt, Promise promise) {
try {
Intercom.client().setUserJwt(userJwt);
promise.resolve(true);
} catch (Exception err) {
Log.e(NAME, "setUserJwt error:");
Log.e(NAME, err.toString());
promise.reject(IntercomErrorCodes.SET_USER_JWT, err.toString());
}
}

@ReactMethod
public void updateUser(ReadableMap params, Promise promise) {
UserAttributes userAttributes = IntercomHelpers.buildUserAttributes(params);
Expand Down
2 changes: 1 addition & 1 deletion intercom-react-native.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ Pod::Spec.new do |s|
s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }

s.dependency "React-Core"
s.dependency "Intercom", '~> 18.6.1'
s.dependency "Intercom", '~> 18.2.0'
end
12 changes: 12 additions & 0 deletions ios/IntercomModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ @implementation IntercomModule
NSString *UNIDENTIFIED_REGISTRATION = @"101";
NSString *IDENTIFIED_REGISTRATION = @"102";
NSString *SET_USER_HASH = @"103";
NSString *SET_USER_JWT = @"106";
NSString *UPDATE_USER = @"104";
NSString *LOG_EVENT = @"105";
NSString *UNREAD_CONVERSATION_COUNT = @"107";
Expand Down Expand Up @@ -154,6 +155,17 @@ - (NSData *)dataFromHexString:(NSString *)string {
}
};

RCT_EXPORT_METHOD(setUserJwt:(NSString *)userJwt
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
@try {
[Intercom setUserJwt:userJwt];
resolve(@(YES));
} @catch (NSException *exception) {
reject(SET_USER_JWT, @"Error in setUserJwt", [self exceptionToError:exception :SET_USER_JWT :@"setUserJwt"]);
}
};


#pragma mark - Events

Expand Down
14 changes: 14 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,19 @@ export type IntercomType = {
*/
setUserHash(hash: string): Promise<boolean>;

/**
* Sets a JWT token for the user, necessary for using the Messenger
* when Messenger Security is enforced. This is an improvement to Identity Verification.
*
* Secure your Messenger to make sure that bad actors can't impersonate your users,
* see their conversation history or make unauthorised updates to data.
*
* This should be called before any user login takes place.
*
* @param jwt A JWT token signed with your app's secret key.
*/
setUserJwt(jwt: string): Promise<boolean>;

/**
* Update a user in Intercom with data specified in {@link UserAttributes}.
* Full details of the data data attributes that can be stored on a user can be found in {@link UserAttributes}.
Expand Down Expand Up @@ -303,6 +316,7 @@ const Intercom: IntercomType = {
IntercomModule.loginUserWithUserAttributes(userAttributes),
logout: () => IntercomModule.logout(),
setUserHash: (hash) => IntercomModule.setUserHash(hash),
setUserJwt: (jwt) => IntercomModule.setUserJwt(jwt),
updateUser: (userAttributes) => IntercomModule.updateUser(userAttributes),
isUserLoggedIn: () => IntercomModule.isUserLoggedIn(),
fetchLoggedInUserAttributes: () =>
Expand Down