@@ -5,6 +5,7 @@ import android.content.Context
55import android.content.Intent
66import android.util.Log
77import com.clerk.api.Clerk
8+ import com.clerk.api.network.serialization.ClerkResult
89import com.facebook.react.bridge.ActivityEventListener
910import com.facebook.react.bridge.Promise
1011import com.facebook.react.bridge.ReactApplicationContext
@@ -67,41 +68,63 @@ class ClerkExpoModule(reactContext: ReactApplicationContext) :
6768 try {
6869 publishableKey = pubKey
6970
70- // If the JS SDK has a bearer token, write it to the native SDK's
71- // SharedPreferences so both SDKs share the same Clerk API client.
72- if (! bearerToken.isNullOrEmpty()) {
73- reactApplicationContext.getSharedPreferences(" clerk_preferences" , Context .MODE_PRIVATE )
74- .edit()
75- .putString(" DEVICE_TOKEN" , bearerToken)
76- .apply ()
77- debugLog(TAG , " configure - wrote JS bearer token to native SharedPreferences" )
78- }
79-
80- Clerk .initialize(reactApplicationContext, pubKey)
71+ if (! Clerk .isInitialized.value) {
72+ // First-time initialization — write the bearer token to SharedPreferences
73+ // before initializing so the SDK boots with the correct client.
74+ if (! bearerToken.isNullOrEmpty()) {
75+ reactApplicationContext.getSharedPreferences(" clerk_preferences" , Context .MODE_PRIVATE )
76+ .edit()
77+ .putString(" DEVICE_TOKEN" , bearerToken)
78+ .apply ()
79+ }
8180
82- // Wait for initialization to complete with timeout
83- try {
84- withTimeout(10_000L ) {
85- Clerk .isInitialized.first { it }
81+ Clerk .initialize(reactApplicationContext, pubKey)
82+
83+ // Wait for initialization to complete with timeout
84+ try {
85+ withTimeout(10_000L ) {
86+ Clerk .isInitialized.first { it }
87+ }
88+ } catch (e: TimeoutCancellationException ) {
89+ val initError = Clerk .initializationError.value
90+ val message = if (initError != null ) {
91+ " Clerk initialization timed out: ${initError.message} "
92+ } else {
93+ " Clerk initialization timed out after 10 seconds"
94+ }
95+ promise.reject(" E_TIMEOUT" , message)
96+ return @launch
8697 }
87- } catch (e: TimeoutCancellationException ) {
88- val initError = Clerk .initializationError.value
89- val message = if (initError != null ) {
90- " Clerk initialization timed out: ${initError.message} "
98+
99+ // Check for initialization errors
100+ val error = Clerk .initializationError.value
101+ if (error != null ) {
102+ promise.reject(" E_INIT_FAILED" , " Failed to initialize Clerk SDK: ${error.message} " )
91103 } else {
92- " Clerk initialization timed out after 10 seconds "
104+ promise.resolve( null )
93105 }
94- promise.reject(" E_TIMEOUT" , message)
95106 return @launch
96107 }
97108
98- // Check for initialization errors
99- val error = Clerk .initializationError.value
100- if (error != null ) {
101- promise.reject(" E_INIT_FAILED" , " Failed to initialize Clerk SDK: ${error.message} " )
102- } else {
103- promise.resolve(null )
109+ // Already initialized — use the public SDK API to update
110+ // the device token and trigger a client/environment refresh.
111+ if (! bearerToken.isNullOrEmpty()) {
112+ val result = Clerk .updateDeviceToken(bearerToken)
113+ if (result is ClerkResult .Failure ) {
114+ debugLog(TAG , " configure - updateDeviceToken failed: ${result.error} " )
115+ }
116+
117+ // Wait for session to appear with the new token (up to 5s)
118+ try {
119+ withTimeout(5_000L ) {
120+ Clerk .sessionFlow.first { it != null }
121+ }
122+ } catch (_: TimeoutCancellationException ) {
123+ debugLog(TAG , " configure - session did not appear after token update" )
124+ }
104125 }
126+
127+ promise.resolve(null )
105128 } catch (e: Exception ) {
106129 promise.reject(" E_INIT_FAILED" , " Failed to initialize Clerk SDK: ${e.message} " , e)
107130 }
@@ -174,15 +197,15 @@ class ClerkExpoModule(reactContext: ReactApplicationContext) :
174197 @ReactMethod
175198 override fun getSession (promise : Promise ) {
176199 if (! Clerk .isInitialized.value) {
177- promise.reject(" E_NOT_INITIALIZED" , " Clerk SDK is not initialized. Call configure() first." )
200+ // Return null when not initialized (matches iOS behavior)
201+ // so callers can proceed to call configure() with a bearer token.
202+ promise.resolve(null )
178203 return
179204 }
180205
181206 val session = Clerk .session
182207 val user = Clerk .user
183208
184- debugLog(TAG , " getSession - hasSession: ${session != null } , hasUser: ${user != null } " )
185-
186209 val result = WritableNativeMap ()
187210
188211 session?.let {
@@ -217,7 +240,6 @@ class ClerkExpoModule(reactContext: ReactApplicationContext) :
217240 try {
218241 val prefs = reactApplicationContext.getSharedPreferences(" clerk_preferences" , Context .MODE_PRIVATE )
219242 val deviceToken = prefs.getString(" DEVICE_TOKEN" , null )
220- debugLog(TAG , " getClientToken - deviceToken: ${if (deviceToken != null ) " found" else " null" } " )
221243 promise.resolve(deviceToken)
222244 } catch (e: Exception ) {
223245 debugLog(TAG , " getClientToken failed: ${e.message} " )
@@ -230,7 +252,8 @@ class ClerkExpoModule(reactContext: ReactApplicationContext) :
230252 @ReactMethod
231253 override fun signOut (promise : Promise ) {
232254 if (! Clerk .isInitialized.value) {
233- promise.reject(" E_NOT_INITIALIZED" , " Clerk SDK is not initialized. Call configure() first." )
255+ // Resolve gracefully when not initialized (matches iOS behavior)
256+ promise.resolve(null )
234257 return
235258 }
236259
@@ -258,17 +281,13 @@ class ClerkExpoModule(reactContext: ReactApplicationContext) :
258281 }
259282
260283 private fun handleAuthResult (resultCode : Int , data : Intent ? ) {
261- debugLog(TAG , " handleAuthResult - resultCode: $resultCode " )
262-
263284 val promise = pendingAuthPromise ? : return
264285 pendingAuthPromise = null
265286
266287 if (resultCode == Activity .RESULT_OK ) {
267288 val session = Clerk .session
268289 val user = Clerk .user
269290
270- debugLog(TAG , " handleAuthResult - hasSession: ${session != null } , hasUser: ${user != null } " )
271-
272291 val result = WritableNativeMap ()
273292
274293 // Top-level sessionId for JS SDK compatibility (matches iOS response format)
@@ -296,7 +315,6 @@ class ClerkExpoModule(reactContext: ReactApplicationContext) :
296315
297316 promise.resolve(result)
298317 } else {
299- debugLog(TAG , " handleAuthResult - user cancelled" )
300318 val result = WritableNativeMap ()
301319 result.putBoolean(" cancelled" , true )
302320 promise.resolve(result)
0 commit comments