@@ -205,16 +205,22 @@ class A0Auth0Module(private val reactContext: ReactApplicationContext) : A0Auth0
205205
206206 UiThreadUtil .runOnUiThread {
207207 val resolved = java.util.concurrent.atomic.AtomicBoolean (false )
208+ // Holds the pending grace-window timeout so a callback that settles the promise
209+ // first can cancel it, releasing the retained promise/activity references instead
210+ // of leaking them until the delay elapses.
211+ val timeoutHandler = Handler (Looper .getMainLooper())
208212 val loginCallback = object :
209213 com.auth0.android.callback.Callback <Credentials , AuthenticationException > {
210214 override fun onSuccess (result : Credentials ) {
211215 if (resolved.compareAndSet(false , true )) {
216+ timeoutHandler.removeCallbacksAndMessages(null )
212217 promise.resolve(CredentialsParser .toMap(result))
213218 }
214219 }
215220
216221 override fun onFailure (error : AuthenticationException ) {
217222 if (resolved.compareAndSet(false , true )) {
223+ timeoutHandler.removeCallbacksAndMessages(null )
218224 handleError(error, promise)
219225 }
220226 }
@@ -232,8 +238,9 @@ class A0Auth0Module(private val reactContext: ReactApplicationContext) : A0Auth0
232238
233239 // Safety net for the rare case where the restored token exchange is still in
234240 // flight: give it a short grace window, then resolve null if nothing arrived.
241+ // The login callback cancels this timeout if it settles first.
235242 if (! resolved.get()) {
236- Handler ( Looper .getMainLooper()) .postDelayed({
243+ timeoutHandler .postDelayed({
237244 if (resolved.compareAndSet(false , true )) {
238245 promise.resolve(null )
239246 }
0 commit comments