JSON Web Token (JWT) authentication adds an extra layer of security to your mobile app by requiring a cryptographically signed token for each API request. This helps prevent unauthorized access to your Iterable project and protects against potential security vulnerabilities.
Iterable's React Native SDK supports authentication with JWT-enabled API keys. This article explains how to implement JWT authentication in your React Native mobile apps to enhance security and protect your user data.
IMPORTANT
Before implementing JWT authentication, remember these critical security practices:
- Never include your Iterable JWT secret in your mobile app code.
- Always generate and sign JWTs on your backend server.
- Use HTTPS for all communication between your app and server.
- Implement proper authentication for your JWT generation endpoint.
# In this article
- About JWT-enabled API keys
- Prerequisites
- JWT structure
- Configuring JWT authentication
- When the authHandler is called
- Passing pre-fetched auth tokens
- Configuring token refresh behavior
- Handling JWT errors
- Configuring retry policy
- Examples
- Migration from standard API keys
- Troubleshooting
- Best practices
- Further reading
# About JWT-enabled API keys
JWT-enabled API keys require a valid JWT to authenticate API requests from mobile SDKs. Unlike standard API keys, JWT-enabled keys provide:
- Enhanced security - Each request must include a valid, signed JWT token.
- User-specific authentication - Tokens are tied to specific users.
- Expiration controls - Tokens can expire and be automatically refreshed.
- Server-side validation - Your server controls token generation and signing.
To learn more about JWT-enabled API keys, read JWT-Enabled API Keys.
IMPORTANT
JWT authentication requires coordination between your mobile app, your backend server, and Iterable. Your server must generate and sign JWTs using your Iterable JWT secret, which should never be exposed in your mobile app.
# Prerequisites
Before implementing JWT authentication, you need:
- Iterable's React Native SDK 2.2.0+ - JWT authentication requires version 2.2.0 or later of @iterable/react-native-sdk.
- A JWT-enabled API key - Create one in your Iterable project settings (Integrations > API Keys).
- A backend server - To generate and sign JWTs for your mobile app.
- JWT implementation - Your server must generate JWTs with the required payload structure (see JWT structure).
# JWT structure
JWTs are composed of a header, a payload, and a signature. The header and payload are Base64URL-encoded, and the signature is a Base64URL-encoded HMAC SHA256 hash of the header and payload.
To be used with Iterable's React Native SDK, JWTs generated by your server must have a payload that includes the following claims:
-
emailoruserId(string) - The user identifier, which must match the user set in the SDK -
iat(number) - Issued-at time, as a Unix timestamp in seconds -
exp(number) - Expiration time, as a Unix timestamp in seconds
IMPORTANT
The token expiration (exp) must be less than one year from the issued-at time (iat).
Iterable won't process tokens with an expiration that exceeds one year.
# Example JWT payloads
Using email:
{ "email": "user@example.com", "iat": 1640000000, "exp": 1640086400 }
Or, using userId:
{ "userId": "user123", "iat": 1640000000, "exp": 1640086400 }
# JWT payload best practices
- Include either
emailoruserIdin the JWT payload, but not both. - The identifier in the JWT must match the one set with
setEmail()orsetUserId(). - Your server must sign the JWT using the Iterable JWT secret you obtained when you created your JWT API key.
- Token expiration must be less than one year from the issued-at time.
- See the example generator.
# Configuring JWT authentication
To enable JWT authentication in your React Native app, implement an authHandler
function when initializing the SDK to fetch a valid JWT from your server and
provide it to the SDK.
# Basic configuration
import { Iterable, IterableConfig } from '@iterable/react-native-sdk'; const config = new IterableConfig(); config.authHandler = async () => { // Fetch a JWT from your server try { const response = await fetch('https://your-server.com/api/get-jwt', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ // Include any information your server needs to identify the user userId: 'user123', }), }); const data = await response.json(); return data.jwt; // Return the JWT string } catch (error) { console.error('Error fetching JWT:', error); return undefined; // Returning undefined indicates failure } }; // Initialize with your JWT-enabled API key Iterable.initialize('YOUR_JWT_ENABLED_API_KEY', config);
# Advanced configuration with callbacks
You can also return an IterableAuthResponse object to handle success and failure
callbacks.
import { Iterable, IterableConfig, IterableAuthResponse } from '@iterable/react-native-sdk'; const config = new IterableConfig(); config.authHandler = async () => { const response = new IterableAuthResponse(); try { const result = await fetch('https://your-server.com/api/get-jwt', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: 'user123' }), }); const data = await result.json(); response.authToken = data.jwt; // Optional: Set a callback for successful authentication response.successCallback = () => { console.log('Successfully authenticated with Iterable'); }; // Optional: Set a callback for failed authentication response.failureCallback = () => { console.error('Failed to authenticate with Iterable'); }; } catch (error) { console.error('Error in authHandler:', error); response.authToken = null; } return response; }; Iterable.initialize('YOUR_JWT_ENABLED_API_KEY', config);
# When the authHandler is called
The SDK automatically calls your authHandler in the following scenarios:
-
User identification - When you call
setEmail()orsetUserId()to identify a user -
Email updates - When you call
updateEmail()to change a user's email -
Token refresh - Before the current JWT expires (based on
expiringAuthTokenRefreshPeriod) -
Token invalidation - When Iterable's API returns a 401 error with an
InvalidJwtPayloaderror code
NOTE
The SDK includes automatic retry logic. If a request fails with a 401 error,
the SDK will call the authHandler to get a new token and retry the request.
However, if the second request also fails with a 401, the SDK won't call the
authHandler again until you call setEmail() or setUserId().
# Passing pre-fetched auth tokens
To avoid race conditions or to optimize the authentication flow, you can pass a pre-fetched JWT directly when setting the user identity:
# With setEmail
// Fetch the JWT before setting the email const jwt = await fetchJwtFromYourServer('user@example.com'); // Pass the JWT as the second parameter Iterable.setEmail('user@example.com', jwt);
# With setUserId
// Fetch the JWT before setting the user ID const jwt = await fetchJwtFromYourServer('user123'); // Pass the JWT as the second parameter Iterable.setUserId('user123', jwt);
# With updateEmail
// Fetch the JWT for the new email const jwt = await fetchJwtFromYourServer('newemail@example.com'); // Pass the JWT as the second parameter Iterable.updateEmail('newemail@example.com', jwt);
IMPORTANT
When passing a pre-fetched auth token, make sure the JWT's email or userId
claim matches the identifier you're setting in the SDK.
# Configuring token refresh behavior
To specify when the SDK should refresh expiring JWT tokens, set the
expiringAuthTokenRefreshPeriod property, as follows:
const config = new IterableConfig(); // Set auth handler config.authHandler = async () => { // ... fetch JWT from your server }; // Refresh the token 120 seconds (2 minutes) before it expires // Default is 60 seconds config.expiringAuthTokenRefreshPeriod = 120.0; Iterable.initialize('YOUR_JWT_ENABLED_API_KEY', config);
This setting determines how many seconds before the JWT's expiration time (exp)
the SDK should call the authHandler to fetch a new token.
# Handling JWT errors
To make it easier to log, debug, and monitor authentication issues, implement an error handler that notifies you when JWT authentication fails.
import { Iterable, IterableConfig, IterableAuthFailure, IterableAuthFailureReason } from '@iterable/react-native-sdk'; const config = new IterableConfig(); config.authHandler = async () => { // ... fetch JWT from your server }; config.onJwtError = (authFailure: IterableAuthFailure) => { console.error('JWT Authentication Error:', { reason: authFailure.failureReason, userKey: authFailure.userKey, failedToken: authFailure.failedAuthToken, timestamp: authFailure.failedRequestTime, }); // Handle specific error types switch (authFailure.failureReason) { case IterableAuthFailureReason.AUTH_TOKEN_EXPIRED: console.error('Token expired - SDK will automatically retry'); break; case IterableAuthFailureReason.AUTH_TOKEN_SIGNATURE_INVALID: console.error('Invalid token signature - check your server implementation'); break; case IterableAuthFailureReason.AUTH_TOKEN_USER_KEY_INVALID: console.error('User key mismatch - ensure JWT matches setEmail/setUserId'); break; default: console.error('Other authentication error'); } }; Iterable.initialize('YOUR_JWT_ENABLED_API_KEY', config);
# JWT failure reasons
The SDK provides the following failure reasons through the IterableAuthFailureReason enum:
-
AUTH_TOKEN_EXPIRATION_INVALID- The token's expiration is more than one year from its issued-at time. -
AUTH_TOKEN_EXPIRED- The token has expired. -
AUTH_TOKEN_FORMAT_INVALID- The token format is invalid (failed regex validation). -
AUTH_TOKEN_GENERATION_ERROR- TheauthHandlerthrew an exception. -
AUTH_TOKEN_GENERIC_ERROR- An unspecified authentication error occurred. -
AUTH_TOKEN_INVALIDATED- Iterable has invalidated this token. -
AUTH_TOKEN_MISSING- The API request did not include a JWT authorization header. -
AUTH_TOKEN_NULL- TheauthHandlerreturned a null JWT token. -
AUTH_TOKEN_PAYLOAD_INVALID- Iterable could not decode the token's payload (iat,exp,email, oruserId). -
AUTH_TOKEN_SIGNATURE_INVALID- Iterable couldn't validate the token's authenticity. -
AUTH_TOKEN_USER_KEY_INVALID- The token doesn't includeemailoruserId, or the user doesn't exist in Iterable.
NOTE
The SDK automatically handles retry logic for JWT errors. The onJwtError
callback is primarily for logging and monitoring purposes.
# Configuring retry policy
To customize the retry behavior for JWT token refresh, use the retryPolicy
configuration option, as follows:
import { Iterable, IterableConfig, IterableRetryBackoff } from '@iterable/react-native-sdk'; const config = new IterableConfig(); config.authHandler = async () => { // ... fetch JWT from your server }; // Configure custom retry policy config.retryPolicy = { maxRetries: 5, retryBackoff: IterableRetryBackoff.LINEAR, retryInterval: 2.0, }; Iterable.initialize('YOUR_JWT_ENABLED_API_KEY', config);
# Retry policy options
Use the following policy options to specify how JWT tokens are refreshed:
maxRetries- The maximum number of retry attempts for JWT token refreshes (defaults to5)retryBackoff(IterableRetryBackoff) - The backoff strategy, eitherLINEARorEXPONENTIAL(default).retryInterval- The initial retry interval in seconds (defaults to2.0)
# Examples
JWT authentication in React Native requires coordination between your mobile app and your backend server. The mobile app requests tokens from your server, which generates and signs them using your Iterable JWT secret. The app then uses these tokens to authenticate API requests to Iterable.
NOTE
Before using any of these examples, remember to follow the critical security practices mentioned at the beginning of this article.
# JavaScript implementation
The following example shows how to configure JWT authentication in your React
Native app. The key component is the authHandler, which fetches tokens from
your backend server whenever the SDK needs to authenticate.
import { Iterable, IterableConfig, IterableAuthResponse, IterableAuthFailure, IterableAuthFailureReason, IterableRetryBackoff } from '@iterable/react-native-sdk'; // Initialize configuration const config = new IterableConfig(); // Configure auth handler to fetch JWT from your server config.authHandler = async () => { const response = new IterableAuthResponse(); try { const result = await fetch('https://your-server.com/api/get-jwt', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${yourAppAuthToken}`, // Your app's auth }, body: JSON.stringify({ userId: await getCurrentUserId(), // Get current user }), }); if (!result.ok) { throw new Error(`Server returned ${result.status}`); } const data = await result.json(); response.authToken = data.jwt; response.successCallback = () => { console.log('Successfully authenticated with Iterable'); }; response.failureCallback = () => { console.error('Failed to authenticate with Iterable'); }; } catch (error) { console.error('Error fetching JWT:', error); response.authToken = null; } return response; }; // Configure when to refresh expiring tokens (120 seconds before expiration) config.expiringAuthTokenRefreshPeriod = 120.0; // Configure error handling for JWT failures config.onJwtError = (authFailure: IterableAuthFailure) => { console.error('JWT Error:', { reason: authFailure.failureReason, user: authFailure.userKey, timestamp: new Date(authFailure.failedRequestTime * 1000), }); // Log to your analytics service logAnalyticsEvent('jwt_auth_error', { reason: authFailure.failureReason, userKey: authFailure.userKey, }); }; // Configure retry policy config.retryPolicy = { maxRetries: 5, retryBackoff: IterableRetryBackoff.EXPONENTIAL, retryInterval: 2.0, }; // Optional: Configure other SDK settings config.autoPushRegistration = true; config.logLevel = IterableLogLevel.info; // Initialize the SDK with your JWT-enabled API key Iterable.initialize('YOUR_JWT_ENABLED_API_KEY', config); // Later, when you identify the user async function identifyUser(email: string) { // Option 1: Let the authHandler fetch the token automatically Iterable.setEmail(email); // Option 2: Pre-fetch the token to avoid race conditions const jwt = await fetchJwtFromYourServer(email); Iterable.setEmail(email, jwt); }
# Server-side JWT generation
Your backend server is responsible for generating JWTs signed with your Iterable JWT secret. The JWT payload must include the user's email or userId and an expiration time.
For detailed server-side implementation examples in multiple languages (including Java, Node.js, Python, and Ruby), see Sample Code for JWT Generation in Iterable's JWT-Enabled API Keys documentation.
# Complete working example
For a fully functional implementation that demonstrates JWT authentication across the entire stack, see the React Native SDK example app.
The example app includes native modules that bridge to React Native, demonstrating how to integrate JWT token generation into both Android and iOS platforms. The following key files are included in the example app.
Android
| File | Purpose |
|---|---|
| IterableJwtGenerator.java | Implements the JWT token generation logic, creating signed JWT tokens with user credentials and expiration times for authentication with Iterable's API. |
| JwtTokenModule.kt | Bridges the native Android JWT generation functionality to React Native, exposing methods that can be called from JavaScript to generate tokens. |
| JwtTokenPackage.kt | Registers the JwtTokenModule with React Native's native module system, making the JWT functionality discoverable and accessible to the JavaScript layer. |
| MainApplication.kt | The main Android application class where the JwtTokenPackage is added to the list of React Native packages during app initialization. |
iOS
| File | Purpose |
|---|---|
| IterableJwtGenerator.swift | Implements the JWT token generation logic for iOS, creating signed JWT tokens with user credentials and expiration times for authentication with Iterable's API. |
| JwtTokenModule.mm | Objective-C++ bridge file that connects React Native's JavaScript layer to the Swift JWT token module, enabling cross-language communication. |
| JwtTokenModule.swift | Swift-based React Native module that exposes the JWT token generation functionality to JavaScript, allowing the React Native app to request tokens from native iOS code. |
React Native
| File | Purpose |
|---|---|
| NativeJwtTokenModule.ts | TypeScript interface that defines the contract for accessing native JWT token generation modules from JavaScript, providing type-safe access to the platform-specific implementations. |
| useIterableApp.tsx | React hook that demonstrates how to integrate JWT authentication into a React Native app, including configuring the SDK with the authentication handler and managing the authentication lifecycle. Implementation is found in the initialize function. |
NOTE
The example app's native modules are provided for demonstration purposes. In a production app, your authHandler should call your backend server's JWT generation endpoint rather than generating tokens locally in the mobile app.
# Migration from standard API keys
If you're migrating from a standard API key to a JWT-enabled API key:
- Set up your server - Implement JWT generation on your backend.
-
Update your configuration - Add the
authHandlerto yourIterableConfig. -
Replace your API key - Use your JWT-enabled API key when calling
Iterable.initialize(). - Test thoroughly - Verify authentication works in development before deploying.
TIP
Verify JWT authentication before you use it in your production environment by testing a JWT-enabled API key in a sandbox Iterable project.
# Troubleshooting
# Common issues and solutions
# "AUTH_TOKEN_SIGNATURE_INVALID" error
Cause: The JWT signature doesn't match the expected signature based on your Iterable JWT secret.
Solutions:
- Verify that you're using the correct JWT secret from your Iterable project settings.
- Ensure that your server is signing the JWT with the HS256 algorithm.
- Check that the JWT secret hasn't been rotated in Iterable.
# "AUTH_TOKEN_USER_KEY_INVALID" error
Cause: The email or userId in the JWT doesn't match the user identifier
set in the SDK, or the user doesn't exist in Iterable.
Solutions:
- Ensure that the JWT payload includes the same identifier you passed to
setEmail()orsetUserId(). - Verify that the user exists in your Iterable project.
- Check for typos or formatting differences in the email/userId.
# "AUTH_TOKEN_EXPIRED" errors
Cause: The JWT has expired before the SDK could refresh it.
Solutions:
- Increase
expiringAuthTokenRefreshPeriodto refresh tokens earlier. - Increase the token lifetime (
exp) when generating JWTs on your server. - Ensure your server and device clocks are synchronized.
# authHandler not being called
Cause: The SDK may have cached a valid token or encountered an error.
Solutions:
- Check the console for error messages.
- Verify network connectivity between your app and server.
- Force a new authentication by calling
setEmail()orsetUserId()again.
# 401 errors with retry loops
Cause: The authHandler is returning invalid or expired tokens repeatedly.
Solutions:
- Verify that your server is generating valid JWTs with the correct structure.
- Check that the JWT secret matches between your server and Iterable.
- Implement logging in your
authHandlerto debug the returned tokens.
# Best practices
Token lifetime - Set JWT expiration times between 15 minutes and 1 hour for a balance between security and user experience. Never set token expiration to one year or more, Iterable won't process them.
Refresh timing - Configure
expiringAuthTokenRefreshPeriodto refresh tokens well before they expire (e.g., 60-120 seconds).Error monitoring - Implement
onJwtErrorto log and monitor authentication failures in production.Server security - Protect your JWT generation endpoint with proper authentication and rate limiting.
Pre-fetch tokens - When possible, pre-fetch JWTs before calling
setEmail()orsetUserId()to avoid race conditions.Test thoroughly - Test JWT authentication with various scenarios including token expiration, network failures, and invalid tokens.
Cache tokens - Consider implementing token caching on your server to reduce the load from frequent JWT generation requests.
# Further reading
- JWT-Enabled API Keys
- Installing Iterable's React Native SDK
- Managing User Identity
- Overview of Iterable's React Native SDK
- JWT.io - Learn more about JSON Web Tokens