Skip to content

Commit 1c7b504

Browse files
authored
Merge pull request #740 from Iterable/jwt/master
[MOB-9064] JWT
2 parents 5b2ab42 + 2e975e2 commit 1c7b504

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+4166
-577
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ ios/generated
8585
android/generated
8686

8787
# Iterable
88+
.env.local
8889
.env
8990
.xcode.env.local
9091
coverage/

CHANGELOG.md

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,37 @@
1-
##2.1.0
1+
## 2.2.0
2+
23
### Updates
3-
* SDK is now compatible with both New Architecture and Legacy Architecture. Fix
4-
for #691, #602, #563.
4+
- Updated Android SDK version to [3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2)
5+
- Updated iOS SDK version to [6.6.3](https://github.com/Iterable/swift-sdk/releases/tag/6.6.3)
6+
- Added JWT Capabilities:
7+
- Added `Iterable.authhManager`, which manages the authentication flow
8+
- Added `IterableRetryBackoff` and `IterableAuthFailureReason` enums
9+
- Added `onJwtError` and `retryPolicy` for control over JWT flow
10+
- Moved all native calls to `IterableApi.ts`
11+
- Added JWT example to our example app
12+
- Changed `onJWTError` to `onJwtError`
13+
- Changed `IterableRetryBackoff` enum keys to be lowercase for consistency
14+
across application
15+
- [SDK-149] Added logout functionality
516

617
### Fixes
7-
* Dependencies update
18+
- Created a standalone `IterableLogger` to avoid circular dependencies
19+
- [SDK-151] Fixed "cannot read property authtoken of undefined" error
20+
- Fixed Android `retryInterval` not being updated on re-initialization.
821

9-
## 2.1.0-beta.1
22+
## 2.1.0
23+
### Updates
24+
- SDK is now compatible with both New Architecture and Legacy Architecture. Fix
25+
for #691, #602, #563.
1026

1127
### Fixes
12-
- Add Temporary fix for circular paths, which break expo ([9c09743](https://github.com/Iterable/react-native-sdk/commit/9c09743))
13-
14-
## 2.1.0-beta.0
15-
16-
### Updates
28+
- Dependencies update
1729
- Update SDK so that it has full support for [React Native New Architecture](https://reactnative.dev/architecture/landing-page)
30+
- Add Temporary fix for circular paths, which break expo ([9c09743](https://github.com/Iterable/react-native-sdk/commit/9c09743))
1831

1932
### Chores
2033
- Update dependencies for React Navigation and related packages ([95053bb](https://github.com/Iterable/react-native-sdk/commit/95053bb))
2134

22-
2335
## 2.0.4
2436

2537
### Updates

Iterable-React-Native-SDK.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Pod::Spec.new do |s|
1717
s.private_header_files = "ios/**/*.h"
1818

1919
# Load Iterables iOS SDK as a dependency
20-
s.dependency "Iterable-iOS-SDK", "6.5.4.1"
20+
s.dependency "Iterable-iOS-SDK", "6.6.3"
2121

2222
# Basic Swift support
2323
s.pod_target_xcconfig = {

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,13 @@ For quick reference, the following table lists the versions of the [Android SDK]
119119
120120
| RN SDK Version | Android SDK Version | iOS SDK Version |
121121
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------- |
122-
| [2.1.0-beta.0](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.1.0-beta.0) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
122+
| [2.2.0](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.2.0) | [3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2) | [6.6.3](https://github.com/Iterable/swift-sdk/releases/tag/6.6.3)
123+
| [2.1.0](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.1.0) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
124+
| [2.0.4](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.0.4) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
123125
| [2.0.3](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.0.3) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
124126
| [2.0.2](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.0.2) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
125127
| [2.0.1](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.0.1) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
126128
| [2.0.0](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.0.0) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
127-
| [2.0.0-beta.1](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.0.0-beta.1) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
128-
| [2.0.0-beta](https://www.npmjs.com/package/@iterable/react-native-sdk/v/2.0.0-beta) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
129129
| [1.3.21](https://www.npmjs.com/package/@iterable/react-native-sdk/v/1.3.20) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
130130
| [1.3.20](https://www.npmjs.com/package/@iterable/react-native-sdk/v/1.3.20) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.4](https://github.com/Iterable/swift-sdk/releases/tag/6.5.4)
131131
| [1.3.19](https://www.npmjs.com/package/@iterable/react-native-sdk/v/1.3.19) | [3.5.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.5.2) | [6.5.3](https://github.com/Iterable/swift-sdk/releases/tag/6.5.3)

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
105105
dependencies {
106106
implementation "com.facebook.react:react-android"
107107
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
108-
api "com.iterable:iterableapi:3.5.2"
108+
api "com.iterable:iterableapi:3.6.2"
109109
// api project(":iterableapi") // links to local android SDK repo rather than by release
110110
}
111111

android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import com.facebook.react.bridge.WritableMap;
1919
import com.facebook.react.modules.core.DeviceEventManagerModule;
2020

21+
import com.iterable.iterableapi.AuthFailure;
2122
import com.iterable.iterableapi.InboxSessionManager;
2223
import com.iterable.iterableapi.IterableAction;
2324
import com.iterable.iterableapi.IterableActionContext;
2425
import com.iterable.iterableapi.IterableApi;
2526
import com.iterable.iterableapi.IterableAuthHandler;
27+
import com.iterable.iterableapi.IterableAuthManager;
2628
import com.iterable.iterableapi.IterableConfig;
2729
import com.iterable.iterableapi.IterableCustomActionHandler;
2830
import com.iterable.iterableapi.IterableAttributionInfo;
@@ -87,7 +89,36 @@ public void initializeWithApiKey(String apiKey, ReadableMap configReadableMap, S
8789
configBuilder.setAuthHandler(this);
8890
}
8991

90-
IterableApi.initialize(reactContext, apiKey, configBuilder.build());
92+
IterableConfig config = configBuilder.build();
93+
IterableApi.initialize(reactContext, apiKey, config);
94+
95+
// Update retry policy on existing authManager if it was already created
96+
// This fixes the issue where retryInterval is not respected after
97+
// re-initialization
98+
// TODO [SDK-197]: Fix the root cause of this issue, instead of this hack
99+
try {
100+
// Use reflection to access package-private fields and methods
101+
java.lang.reflect.Field configRetryPolicyField = config.getClass().getDeclaredField("retryPolicy");
102+
configRetryPolicyField.setAccessible(true);
103+
Object retryPolicy = configRetryPolicyField.get(config);
104+
105+
if (retryPolicy != null) {
106+
java.lang.reflect.Method getAuthManagerMethod = IterableApi.getInstance().getClass().getDeclaredMethod("getAuthManager");
107+
getAuthManagerMethod.setAccessible(true);
108+
IterableAuthManager authManager = (IterableAuthManager) getAuthManagerMethod.invoke(IterableApi.getInstance());
109+
110+
if (authManager != null) {
111+
// Update the retry policy field on the authManager
112+
java.lang.reflect.Field authRetryPolicyField = authManager.getClass().getDeclaredField("authRetryPolicy");
113+
authRetryPolicyField.setAccessible(true);
114+
authRetryPolicyField.set(authManager, retryPolicy);
115+
IterableLogger.d(TAG, "Updated retry policy on existing authManager");
116+
}
117+
}
118+
} catch (Exception e) {
119+
IterableLogger.e(TAG, "Failed to update retry policy: " + e.getMessage());
120+
}
121+
91122
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);
92123

93124
IterableApi.getInstance().getInAppManager().addListener(this);
@@ -121,7 +152,36 @@ public void initialize2WithApiKey(String apiKey, ReadableMap configReadableMap,
121152
// override in the Android SDK. Check with @Ayyanchira and @evantk91 to
122153
// see what the best approach is.
123154

124-
IterableApi.initialize(reactContext, apiKey, configBuilder.build());
155+
IterableConfig config = configBuilder.build();
156+
IterableApi.initialize(reactContext, apiKey, config);
157+
158+
// Update retry policy on existing authManager if it was already created
159+
// This fixes the issue where retryInterval is not respected after
160+
// re-initialization
161+
// TODO [SDK-197]: Fix the root cause of this issue, instead of this hack
162+
try {
163+
// Use reflection to access package-private fields and methods
164+
java.lang.reflect.Field configRetryPolicyField = config.getClass().getDeclaredField("retryPolicy");
165+
configRetryPolicyField.setAccessible(true);
166+
Object retryPolicy = configRetryPolicyField.get(config);
167+
168+
if (retryPolicy != null) {
169+
java.lang.reflect.Method getAuthManagerMethod = IterableApi.getInstance().getClass().getDeclaredMethod("getAuthManager");
170+
getAuthManagerMethod.setAccessible(true);
171+
IterableAuthManager authManager = (IterableAuthManager) getAuthManagerMethod.invoke(IterableApi.getInstance());
172+
173+
if (authManager != null) {
174+
// Update the retry policy field on the authManager
175+
java.lang.reflect.Field authRetryPolicyField = authManager.getClass().getDeclaredField("authRetryPolicy");
176+
authRetryPolicyField.setAccessible(true);
177+
authRetryPolicyField.set(authManager, retryPolicy);
178+
IterableLogger.d(TAG, "Updated retry policy on existing authManager");
179+
}
180+
}
181+
} catch (Exception e) {
182+
IterableLogger.e(TAG, "Failed to update retry policy: " + e.getMessage());
183+
}
184+
125185
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);
126186

127187
IterableApi.getInstance().getInAppManager().addListener(this);
@@ -572,19 +632,33 @@ public String onAuthTokenRequested() {
572632
}
573633
}
574634

635+
@Override
636+
public void onAuthFailure(AuthFailure authFailure) {
637+
// Create a JSON object for the authFailure object
638+
JSONObject messageJson = new JSONObject();
639+
try {
640+
messageJson.put("userKey", authFailure.userKey);
641+
messageJson.put("failedAuthToken", authFailure.failedAuthToken);
642+
messageJson.put("failedRequestTime", authFailure.failedRequestTime);
643+
messageJson.put("failureReason", authFailure.failureReason.name());
644+
WritableMap eventData = Serialization.convertJsonToMap(messageJson);
645+
sendEvent(EventName.handleAuthFailureCalled.name(), eventData);
646+
} catch (JSONException e) {
647+
IterableLogger.v(TAG, "Failed to set authToken");
648+
}
649+
}
650+
651+
public void pauseAuthRetries(boolean pauseRetry) {
652+
IterableApi.getInstance().pauseAuthRetries(pauseRetry);
653+
}
654+
575655
@Override
576656
public void onTokenRegistrationSuccessful(String authToken) {
577657
IterableLogger.v(TAG, "authToken successfully set");
578658
// MOB-10422: Pass successhandler to event listener
579659
sendEvent(EventName.handleAuthSuccessCalled.name(), null);
580660
}
581661

582-
@Override
583-
public void onTokenRegistrationFailed(Throwable object) {
584-
IterableLogger.v(TAG, "Failed to set authToken");
585-
sendEvent(EventName.handleAuthFailureCalled.name(), null);
586-
}
587-
588662
public void addListener(String eventName) {
589663
// Keep: Required for RN built in Event Emitter Calls.
590664
}

android/src/main/java/com/iterable/reactnative/Serialization.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.iterable.iterableapi.IterableInboxSession;
2525
import com.iterable.iterableapi.IterableLogger;
2626
import com.iterable.iterableapi.RNIterableInternal;
27+
import com.iterable.iterableapi.RetryPolicy;
2728

2829
import org.json.JSONArray;
2930
import org.json.JSONException;
@@ -94,7 +95,7 @@ static CommerceItem commerceItemFromMap(JSONObject itemMap) throws JSONException
9495
categories[i] = categoriesArray.getString(i);
9596
}
9697
}
97-
98+
9899
return new CommerceItem(itemMap.getString("id"),
99100
itemMap.getString("name"),
100101
itemMap.getDouble("price"),
@@ -216,9 +217,17 @@ static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableConte
216217

217218
configBuilder.setDataRegion(iterableDataRegion);
218219
}
219-
220-
if (iterableContextJSON.has("encryptionEnforced")) {
221-
configBuilder.setEncryptionEnforced(iterableContextJSON.optBoolean("encryptionEnforced"));
220+
221+
if (iterableContextJSON.has("retryPolicy")) {
222+
JSONObject retryPolicyJson = iterableContextJSON.getJSONObject("retryPolicy");
223+
int maxRetry = retryPolicyJson.getInt("maxRetry");
224+
long retryInterval = retryPolicyJson.getLong("retryInterval");
225+
String retryBackoff = retryPolicyJson.getString("retryBackoff");
226+
RetryPolicy.Type retryPolicyType = RetryPolicy.Type.LINEAR;
227+
if (retryBackoff.equals("EXPONENTIAL")) {
228+
retryPolicyType = RetryPolicy.Type.EXPONENTIAL;
229+
}
230+
configBuilder.setAuthRetryPolicy(new RetryPolicy(maxRetry, retryInterval, retryPolicyType));
222231
}
223232

224233
return configBuilder;
@@ -257,7 +266,13 @@ static JSONObject actionContextToJson(IterableActionContext iterableActionContex
257266
}
258267

259268
static IterableInboxSession.Impression inboxImpressionFromMap(JSONObject impressionMap) throws JSONException {
260-
return new IterableInboxSession.Impression(impressionMap.getString("messageId"),
269+
// Add null check for messageId to prevent NullPointerException
270+
String messageId = impressionMap.optString("messageId", null);
271+
if (messageId == null || messageId.isEmpty()) {
272+
throw new JSONException("messageId is null or empty");
273+
}
274+
275+
return new IterableInboxSession.Impression(messageId,
261276
impressionMap.getBoolean("silentInbox"),
262277
impressionMap.optInt("displayCount", 0),
263278
(float) impressionMap.optDouble("duration", 0)
@@ -271,8 +286,13 @@ static List<IterableInboxSession.Impression> impressionsFromReadableArray(Readab
271286
JSONArray impressionJsonArray = convertArrayToJson(array);
272287

273288
for (int i = 0; i < impressionJsonArray.length(); i++) {
274-
JSONObject impressionObj = impressionJsonArray.getJSONObject(i);
275-
list.add(inboxImpressionFromMap(impressionObj));
289+
try {
290+
JSONObject impressionObj = impressionJsonArray.getJSONObject(i);
291+
list.add(inboxImpressionFromMap(impressionObj));
292+
} catch (JSONException e) {
293+
// Skip invalid entries instead of failing completely
294+
IterableLogger.w(TAG, "Skipping invalid impression at index " + i + ": " + e.getLocalizedMessage());
295+
}
276296
}
277297
} catch (JSONException e) {
278298
IterableLogger.e(TAG, "Failed converting to JSONObject");
@@ -286,7 +306,7 @@ static List<IterableInboxSession.Impression> impressionsFromReadableArray(Readab
286306
// ---------------------------------------------------------------------------------------
287307
// region React Native JSON conversion methods
288308
// obtained from https://gist.github.com/viperwarp/2beb6bbefcc268dee7ad
289-
309+
290310
static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONException {
291311
WritableMap map = new WritableNativeMap();
292312

android/src/newarch/java/com/RNIterableAPIModule.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.facebook.react.bridge.ReactApplicationContext;
88
import com.facebook.react.bridge.ReadableArray;
99
import com.facebook.react.bridge.ReadableMap;
10+
import com.iterable.iterableapi.AuthFailure;
11+
import com.iterable.iterableapi.IterableLogger;
1012

1113
public class RNIterableAPIModule extends NativeRNIterableAPISpec {
1214
private final ReactApplicationContext reactContext;
@@ -217,6 +219,11 @@ public void passAlongAuthToken(@Nullable String authToken) {
217219
moduleImpl.passAlongAuthToken(authToken);
218220
}
219221

222+
@Override
223+
public void pauseAuthRetries(boolean pauseRetry) {
224+
moduleImpl.pauseAuthRetries(pauseRetry);
225+
}
226+
220227
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
221228
moduleImpl.sendEvent(eventName, eventData);
222229
}

android/src/oldarch/java/com/RNIterableAPIModule.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ public void passAlongAuthToken(@Nullable String authToken) {
223223
moduleImpl.passAlongAuthToken(authToken);
224224
}
225225

226+
@ReactMethod
227+
public void pauseAuthRetries(boolean pauseRetry) {
228+
moduleImpl.pauseAuthRetries(pauseRetry);
229+
}
230+
226231

227232
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
228233
moduleImpl.sendEvent(eventName, eventData);

example/.env.example

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@
99
# 4. Fill in the following fields:
1010
# - Name: A descriptive name for the API key
1111
# - Type: Mobile
12-
# - JWT authentication: Leave **unchecked** (IMPORTANT)
12+
# - JWT authentication: Whether or not you want to use JWT
1313
# 5. Click "Create API Key"
14-
# 6. Copy the generated API key
15-
# 7. Replace the placeholder text next to `ITBL_API_KEY=` with the copied API key
14+
# 6. Copy the generated API key and replace the placeholder text next to
15+
# `ITBL_API_KEY=` with the copied API key
16+
# 7. If you chose to enable JWT authentication, copy the JWT secret and and
17+
# replace the placeholder text next to `ITBL_JWT_SECRET=` with the copied
18+
# JWT secret
1619
ITBL_API_KEY=replace_this_with_your_iterable_api_key
20+
# Your JWT Secret, created when making your API key (see above)
21+
ITBL_JWT_SECRET=replace_this_with_your_jwt_secret
22+
# Is your api token JWT Enabled?
23+
# Must be set to 'true' to enable JWT authentication
24+
ITBL_IS_JWT_ENABLED=true
1725

1826
# Your Iterable user ID or email address
19-
ITBL_ID=replace_this_with_your_user_id_or_email
27+
ITBL_ID=replace_this_with_your_user_id_or_email

0 commit comments

Comments
 (0)