-
Notifications
You must be signed in to change notification settings - Fork 39
[SDK-136] Add Jwt Generator to Example app #767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ation in React Native
…andling in IterableAppProvider
…o clean up the implementation
…in IterableAppProvider
| setLoginInProgress(false); | ||
| return Promise.reject(err); | ||
| }) | ||
| .finally(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realized if you set the userid/email before calling initialize then you don't get this error.
This is super unintuitive. Do you know if it's documented anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could also put this in a separate ticket if you'd like. It would probably be cleaner.
…ove old utility index
4 new issues
This is from Qlty Cloud, the successor to Code Climate Quality. Learn more. |
example/android/app/src/main/java/iterable/reactnativesdk/example/IterableJwtGenerator.java
Show resolved
Hide resolved
example/android/app/src/main/java/iterable/reactnativesdk/example/JwtTokenModule.kt
Show resolved
Hide resolved
example/android/app/src/main/java/iterable/reactnativesdk/example/IterableJwtGenerator.java
Show resolved
Hide resolved
example/android/app/src/main/java/iterable/reactnativesdk/example/JwtTokenModule.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds JWT token generation capabilities to the example app, enabling authentication with JWT-enabled API keys. The implementation includes native modules for both iOS and Android platforms that generate JWT tokens client-side, along with configuration updates to support JWT-enabled workflows.
Key changes:
- Native JWT token generation modules for iOS (Swift) and Android (Kotlin/Java)
- Integration of JWT authentication handler in the example app's Iterable configuration
- Documentation updates with setup instructions for JWT-enabled API keys
Reviewed Changes
Copilot reviewed 15 out of 17 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/core/classes/Iterable.ts | Removed unused timeout reference cleanup |
| ios/RNIterableAPI/ReactIterableAPI.swift | Code formatting improvements and explicit property access |
| example/src/hooks/useIterableApp.tsx | Added JWT token generation logic and auth handler configuration |
| example/src/NativeJwtTokenModule.ts | New module interface for JWT token generation with TurboModule support |
| example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj | Added Swift JWT generator files to Xcode project |
| example/ios/ReactNativeSdkExample-Bridging-Header.h | Added React Native bridge imports |
| example/ios/JwtTokenModule.swift | Swift implementation of JWT token generation module |
| example/ios/JwtTokenModule.mm | Objective-C++ bridge for JWT token module |
| example/ios/IterableJwtGenerator.swift | Core JWT generation logic for iOS |
| example/android/app/src/main/java/iterable/reactnativesdk/example/MainApplication.kt | Registered JWT token package |
| example/android/app/src/main/java/iterable/reactnativesdk/example/JwtTokenPackage.kt | Android package definition for JWT module |
| example/android/app/src/main/java/iterable/reactnativesdk/example/JwtTokenModule.kt | Kotlin bridge for JWT token generation |
| example/android/app/src/main/java/iterable/reactnativesdk/example/IterableJwtGenerator.java | Core JWT generation logic for Android |
| example/README.md | Updated setup instructions with JWT configuration |
| example/.env.example | Added JWT-related environment variables |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| var iat = Int(Date().timeIntervalSince1970) | ||
| var exp = Int(Date().timeIntervalSince1970) + 60 |
Copilot
AI
Oct 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Payload struct defines hardcoded default values for iat and exp that are overridden by the initializer parameters. These default values should be removed since they are misleading and never used (the struct is always initialized with explicit values from the function parameters). The same issue exists in the generateJwtForUserId function.
example/.env.example
Outdated
| # Your JWT Secret, created when making your API key (see above) | ||
| ITBL_JWT_SECRET=replace_this_with_your_jwt_secret | ||
| # Is your api token JWT Enabled? | ||
| # Defaults to true |
Copilot
AI
Oct 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment 'Defaults to true' is misleading. According to the code in useIterableApp.tsx, JWT is only enabled when explicitly set to the string 'true', not by default. This comment should be removed or clarified to state that this value must be explicitly set.
| # Defaults to true | |
| # Must be set to 'true' to enable JWT authentication |
example/src/hooks/useIterableApp.tsx
Outdated
| }); | ||
| }, | ||
| [apiKey, getUserId, login] | ||
| [getUserId, apiKey, login, getJwtToken] |
Copilot
AI
Oct 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency array is missing userId which is used inside getJwtToken. While getJwtToken is in the array, its own dependency on userId means that userId should also be included to ensure proper memoization.
| [getUserId, apiKey, login, getJwtToken] | |
| [getUserId, apiKey, login, getJwtToken, userId] |
example/android/app/src/main/java/iterable/reactnativesdk/example/IterableJwtGenerator.java
Show resolved
Hide resolved
example/android/app/src/main/java/iterable/reactnativesdk/example/JwtTokenModule.kt
Show resolved
Hide resolved
example/android/app/src/main/java/iterable/reactnativesdk/example/IterableJwtGenerator.java
Show resolved
Hide resolved
example/android/app/src/main/java/iterable/reactnativesdk/example/JwtTokenModule.kt
Show resolved
Hide resolved
| public static String generateToken( | ||
| String secret, Duration duration, String email, String userId) { | ||
|
|
||
| if (duration.compareTo(maxTokenLifetime) > 0) | ||
| throw new IllegalArgumentException( | ||
| "Duration must be one year or less." | ||
| ); | ||
|
|
||
| if ((userId != null && email != null) || (userId == null && email == null)) | ||
| throw new IllegalArgumentException( | ||
| "The token must include a userId or email, but not both." | ||
| ); | ||
|
|
||
| long now = millisToSeconds(System.currentTimeMillis()); | ||
|
|
||
| String payload; | ||
| if (userId != null) | ||
| payload = String.format( | ||
| "{ \"userId\": \"%s\", \"iat\": %d, \"exp\": %d }", | ||
| userId, now, now + millisToSeconds(duration.toMillis()) | ||
| ); | ||
| else | ||
| payload = String.format( | ||
| "{ \"email\": \"%s\", \"iat\": %d, \"exp\": %d }", | ||
| email, now, now + millisToSeconds(duration.toMillis()) | ||
| ); | ||
|
|
||
| return generateToken(secret, payload); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| fun generateJwtToken( | ||
| secret: String, | ||
| durationMs: Double, | ||
| email: String?, | ||
| userId: String?, | ||
| promise: Promise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Omg! This is one of RN's greated PR. Should bookmark this to show how a new module is created, implemented on both native layer, bridged and also used in the TS layer!
Wow fantastic!
| try { | ||
| JwtTokenModule = isTurboModuleEnabled | ||
| ? TurboModuleRegistry.getEnforcing<Spec>('JwtTokenModule') | ||
| : NativeModules.JwtTokenModule; | ||
| } catch { | ||
| // Module not available - will throw error when used |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems interesting. Will look up during testing how turbo module works!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah.. this is for backwards compatibility. If they have new architecture enabled, they'd use TurboModuleRegistry.getEnforcing<Spec>('JwtTokenModule'). Otherwise they'd fall back to NativeModules.JwtTokenModule
| const getJwtToken = useCallback(async () => { | ||
| const id = userId ?? process.env.ITBL_ID; | ||
| const idType = getIsEmail(id as string) ? 'email' : 'userId'; | ||
| const secret = process.env.ITBL_JWT_SECRET ?? ''; | ||
| const duration = 1000 * 60 * 60 * 24; // 1 day in milliseconds | ||
| const jwtToken = await NativeJwtTokenModule.generateJwtToken( | ||
| secret, | ||
| duration, | ||
| idType === 'email' ? (id as string) : null, // Email (can be null if userId is provided) | ||
| idType === 'userId' ? (id as string) : null // UserId (can be null if email is provided) | ||
| ); | ||
|
|
||
| return jwtToken; | ||
| }, [userId]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the main code! nice!
| // expose to Swift. | ||
| // | ||
|
|
||
| #import <React/RCTBridgeModule.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great to know its going to be using native layer components.
| import CryptoKit | ||
| import Foundation | ||
|
|
||
| @objcMembers public final class IterableJwtGenerator: NSObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until I saw this, I was under the impression it will be using native layer's sample app code. Then I realized how! This makes sense. So now sample app in RN has its own native layer implementation! End to end native to TS layer work is implemented here! Great one Loren!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
YAY! So glad you approve 😁
| PackageList(this).packages.apply { | ||
| // Packages that cannot be autolinked yet can be added manually here, for example: | ||
| // add(MyReactNativePackage()) | ||
| add(JwtTokenPackage()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know this is how packages are added
| import com.facebook.react.module.model.ReactModuleInfo | ||
| import com.facebook.react.module.model.ReactModuleInfoProvider | ||
|
|
||
| class JwtTokenPackage : BaseReactPackage() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Package creation example! Great for team to understand how its done!
🔹 JIRA Ticket(s) if any
✏️ Description
Adds JWT Generator to the example app
Testing
example/.env, add the following:yarn startyarn iosyarn android