Repository navigation
FR-25958 FR-25959 FR-25952 feat: Expo config plugin, new architecture, multi-region and getOrRefreshAccessToken - #143
Merged
Conversation
…boModule support Expo config plugin generates the iOS and Android native setup on prebuild, including multi-region via Frontegg.plist and FRONTEGG_REGIONS. Exposes getRegions, initWithRegion and selectedRegion, adds getOrRefreshAccessToken, and supports the new architecture through a codegen spec with old and new arch module implementations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEp5Aemy81i5PZmfV7iX4t
The multi-region init only forwarded useAssetsLinks and useChromeCustomTabs, so FRONTEGG_ENABLE_OFFLINE_MODE, FRONTEGG_DISABLE_AUTO_REFRESH, FRONTEGG_ENABLE_SESSION_PER_TENANT, FRONTEGG_ENTITLEMENTS_ENABLED, FRONTEGG_USE_DISK_CACHE_WEBVIEW, FRONTEGG_MAIN_ACTIVITY_CLASS and FRONTEGG_ORGANIZATION were silently ignored in multi-region apps. Read them through the SDK's FronteggConstantsProvider, as its single-region auto-init does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
Regions were only configured when React Native created the module, so a process restarted straight into the login redirect started the SDK without them. Add FronteggRegions.initFromBuildConfig, which MainApplication can call before React Native starts, and have the Expo config plugin insert it after super.onCreate() in Kotlin MainApplication files for multi-region apps. The module's own init stays idempotent, so the second call is a no-op. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
…or fresh token - Resolve null in a multi-region app before a region is selected instead of waiting 20 seconds on initializing and rejecting. - Check connectivity before refreshing: with offline mode enabled resolve the cached token, otherwise reject right away instead of waiting about a minute in refreshTokenAndWait. Offline mode is read from FronteggConstantsProvider, the source the SDK is initialized from. - When refreshTokenAndWait reports success but the token is still not valid for 15 seconds (another refresh was running), wait up to 10 seconds for it, then refresh once more before failing. Document the exact per-platform behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
expo 58 declares node ^22.13.0, so yarn install --frozen-lockfile failed on Node 18 in every job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
dianaKhortiuk-frontegg
marked this pull request as ready for review
October 26, 2026 19:50
The once-guard was set before reading the SDK constants and calling FronteggApp.initWithRegions, so an exception there left it set: the module's later call skipped setup and the SDK fell back to single-region with no domain, and the exception crashed MainApplication.onCreate. Log the error and reset the guard instead, so the next call retries. A malformed FRONTEGG_REGIONS still keeps the guard set, since it cannot succeed later. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
initWithRegion rejected every exception as INVALID_REGION. Check the key against the configured regions first, as iOS does, and reject a failure of the SDK switch itself with INIT_WITH_REGION_ERROR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
Both native modules update selectedRegion synchronously and push it through the auth state event, which the JS listener debounces by 50 ms, so the promise resolves before useAuth() sees the new region. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 28ba607. Configure here.
This was referenced Oct 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Jira: FR-25958 (Expo config plugin and TurboModule readiness), FR-25959 (multi-region), FR-25952 (get-or-refresh access-token helper).
Summary
Expo config plugin
Add
@frontegg/react-nativewith its options topluginsinapp.jsonand runnpx expo prebuild. The plugin generates the native iOS and Android setup fromdocs/setup.md.baseUrl,clientId, optionalapplicationId; orregions({key, baseUrl, clientId, applicationId?}); pluskeychainService,embeddedMode,logLevel,useAssetLinks,useChromeCustomTabs,associatedDomains, and the pass-throughiosPlistandandroidBuildConfigFields.Frontegg.plist, associated domains, the URL scheme,handleOpenUrlinAppDelegate.swift(SDK 53–57 and 58 templates), static frameworks and the SPM hook.FRONTEGG_*BuildConfig fields,minSdkVersion26, intent filters for every extra region, and, withregions, a call toFronteggRegions.initFromBuildConfig(this)inMainApplication.onCreateso regions are set up before React Native starts.AppDelegate, a JavaMainApplicationorbuild.gradle.ktsget a warning and are skipped.expois a dev dependency and an optional peer dependency.New architecture (TurboModule)
The module supports both architectures. Codegen spec in
src/NativeFronteggRN.tswithcodegenConfig(RNFronteggRNSpec). Android usesnewarch//oldarch/source sets and aTurboReactPackage; iOSFronteggRN.mis nowFronteggRN.mmand conforms to the spec underRCT_NEW_ARCH_ENABLED.Multi-region
getRegions(),initWithRegion(regionKey)(rejects withINVALID_REGIONfor an unknown key),useAuth().selectedRegion, andisRegional/regionDataingetConstants().regionsfromFrontegg.plist. Android reads theFRONTEGG_REGIONSBuildConfig field and callsFronteggApp.initWithRegions, passing the otherFRONTEGG_*options thatinitWithRegionsaccepts (offline mode, auto-refresh, session per tenant, entitlements, and so on). Bare React Native apps should callFronteggRegions.initFromBuildConfig(this)inMainApplication.onCreate; Expo does it automatically.getOrRefreshAccessToken
getOrRefreshAccessToken(): Promise<string | null>returns an access token valid for more than 15 s, refreshing it first when needed. Use it before API calls after the app resumes. It rejects withGET_OR_REFRESH_ACCESS_TOKEN_ERRORon failure.nullwhen there is no session, when the server rejected the refresh token, or in a multi-region app before a region is selected.CI
CI and
.nvmrcnow use Node 22 (was 18): theexpodev dependency requires Node 22.13 or newer.Risks
Testing
MainApplicationchange against the SDK 53–58 templates.#import "RNFronteggRNSpec.h"under static frameworks needs checking on an Expo SDK 53+ app), a build against RN 0.79+, and device runs.The first commit's message doesn't follow the conventional-commit format; reword it on merge if needed.
🤖 Generated with Claude Code
https://claude.ai/code/session_01S2yW3aohcrkGZ96iyLpv3a
Note
Medium Risk
Touches authentication token refresh, multi-region SDK initialization timing, and native bridge/codegen; OTA JS bundles that call new APIs need a matching native build.
Overview
Adds an Expo config plugin (
app.plugin.js+plugin/) that runs onexpo prebuildto generateFrontegg.plist, AndroidFRONTEGG_*BuildConfig/manifest placeholders, associated domains, URL handling in SwiftAppDelegate, multi-region intent filters, andFronteggRegions.initFromBuildConfigin KotlinMainApplication.Converts the native module to a TurboModule with codegen (
src/NativeFronteggRN.ts,RNFronteggRNSpec),TurboReactPackageon Android (newarch/oldarchspecs), andFronteggRN.mmfor new-arch iOS. JS now loads the module viaTurboModuleRegistryinstead ofNativeModules.Ships multi-region:
getRegions(),initWithRegion(),useAuth().selectedRegion, andisRegional/regionDatain constants; Android readsFRONTEGG_REGIONSand initializes viaRegionsInitializer/FronteggRegions.Adds
getOrRefreshAccessToken()on both platforms (AndroidAccessTokenRefresherwith JWT validity, offline mode, and concurrent-refresh handling; iOS delegates to the native SDK).CI / toolchain: Node 22 in E2E workflows and
.nvmrcfor the Expo dev dependency. Docs expanded for Expo setup, regions, new architecture, and token usage.Reviewed by Cursor Bugbot for commit 28ba607. Bugbot is set up for automated code reviews on this repo. Configure here.