Skip to content

Conversation

@lposen
Copy link
Contributor

@lposen lposen commented Oct 21, 2025

🔹 JIRA Ticket(s) if any

✏️ Description

Adds JWT Generator to the example app

Testing

  • Create a new mobile api key with jwt enabled
  • In example/.env, add the following:
     ITBL_API_KEY=YOUR_NEW_API_KEY
     ITBL_JWT_SECRET=YOUR_JWT_SECRET
     ITBL_IS_JWT_ENABLED=true
    
  • Run yarn start
  • In a separate tab, run yarn ios
  • In a separate tab, run yarn android
  • Try login -- login should be successful in both android and iOS

@github-actions
Copy link

github-actions bot commented Oct 21, 2025

Lines Statements Branches Functions
Coverage: 57%
57.27% (315/550) 30.45% (67/220) 55.94% (113/202)

setLoginInProgress(false);
return Promise.reject(err);
})
.finally(() => {
Copy link
Contributor Author

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?

Copy link
Contributor Author

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.

@lposen lposen changed the title Jwt/SDK-136-new-jwt-token-generator [SDK-136] Add Jwt Generator to Example app Oct 21, 2025
@qltysh
Copy link

qltysh bot commented Oct 21, 2025

4 new issues

Tool Category Rule Count
qlty Structure Function with many parameters (count = 4): generateToken 2
qlty Structure Function with high complexity (count = 7): generateToken 1
qlty Structure Function with many returns (count = 10): IterableAppProvider 1

This is from Qlty Cloud, the successor to Code Climate Quality. Learn more.

@lposen lposen added the jwt label Oct 21, 2025
@lposen lposen requested a review from Copilot October 21, 2025 03:41
@lposen lposen marked this pull request as ready for review October 21, 2025 03:41
Copy link

Copilot AI left a 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.

Comment on lines 33 to 34
var iat = Int(Date().timeIntervalSince1970)
var exp = Int(Date().timeIntervalSince1970) + 60
Copy link

Copilot AI Oct 21, 2025

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.

Copilot uses AI. Check for mistakes.
# 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
Copy link

Copilot AI Oct 21, 2025

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.

Suggested change
# Defaults to true
# Must be set to 'true' to enable JWT authentication

Copilot uses AI. Check for mistakes.
});
},
[apiKey, getUserId, login]
[getUserId, apiKey, login, getJwtToken]
Copy link

Copilot AI Oct 21, 2025

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.

Suggested change
[getUserId, apiKey, login, getJwtToken]
[getUserId, apiKey, login, getJwtToken, userId]

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +104
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 2 issues:

1. Function with high complexity (count = 7): generateToken [qlty:function-complexity]


2. Function with many parameters (count = 4): generateToken [qlty:function-parameters]

Comment on lines +17 to +22
fun generateJwtToken(
secret: String,
durationMs: Double,
email: String?,
userId: String?,
promise: Promise
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with many parameters (count = 5): generateJwtToken [qlty:function-parameters]

Copy link
Member

@Ayyanchira Ayyanchira left a 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!

Comment on lines +21 to +26
try {
JwtTokenModule = isTurboModuleEnabled
? TurboModuleRegistry.getEnforcing<Spec>('JwtTokenModule')
: NativeModules.JwtTokenModule;
} catch {
// Module not available - will throw error when used
Copy link
Member

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!

Copy link
Contributor Author

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

Comment on lines +111 to +124
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]);
Copy link
Member

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>
Copy link
Member

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 {
Copy link
Member

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!

Copy link
Contributor Author

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())
Copy link
Member

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() {
Copy link
Member

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!

@lposen lposen merged commit 9038579 into jwt/master Oct 21, 2025
8 checks passed
@lposen lposen deleted the jwt/SDK-136-new-jwt-token-generator branch October 21, 2025 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants