@@ -38,14 +38,43 @@ public object WebAuthProvider : SenderConstraining<WebAuthProvider> {
3838 private val callbacks = CopyOnWriteArraySet <Callback <Credentials , AuthenticationException >>()
3939 private val parCallbacks = CopyOnWriteArraySet <Callback <AuthorizationCode , AuthenticationException >>()
4040
41+ // Buffers a state-restore result that completed before any callback was registered (process
42+ // death during login: the restored AuthenticationActivity finishes the token exchange before
43+ // the host app can subscribe). Delivered to the next addCallback subscriber.
44+ private sealed class RecoveredResult {
45+ class Success (val credentials : Credentials ) : RecoveredResult()
46+ class Failure (val error : AuthenticationException ) : RecoveredResult()
47+ }
48+
49+ private val recoveryLock = Any ()
50+ private var pendingRecovered: RecoveredResult ? = null
51+
4152 @JvmStatic
4253 @get:VisibleForTesting(otherwise = VisibleForTesting .PRIVATE )
4354 internal var managerInstance: ResumableManager ? = null
4455 private set
4556
57+ /* *
58+ * Registers a callback for Universal Login results from the state-restore path
59+ * ([onRestoreInstanceState]). A result buffered before this call is delivered immediately and
60+ * consumed. Normal in-process logins resolve through the [Builder.start] callback, not here.
61+ */
4662 @JvmStatic
4763 public fun addCallback (callback : Callback <Credentials , AuthenticationException >) {
48- callbacks + = callback
64+ val buffered = synchronized(recoveryLock) {
65+ val pending = pendingRecovered
66+ if (pending != null ) {
67+ pendingRecovered = null
68+ } else {
69+ callbacks + = callback
70+ }
71+ pending
72+ }
73+ when (buffered) {
74+ is RecoveredResult .Success -> callback.onSuccess(buffered.credentials)
75+ is RecoveredResult .Failure -> callback.onFailure(buffered.error)
76+ null -> {}
77+ }
4978 }
5079
5180 @JvmStatic
@@ -152,12 +181,24 @@ public object WebAuthProvider : SenderConstraining<WebAuthProvider> {
152181 state,
153182 object : Callback <Credentials , AuthenticationException > {
154183 override fun onSuccess (result : Credentials ) {
184+ synchronized(recoveryLock) {
185+ if (callbacks.isEmpty()) {
186+ pendingRecovered = RecoveredResult .Success (result)
187+ return
188+ }
189+ }
155190 for (callback in callbacks) {
156191 callback.onSuccess(result)
157192 }
158193 }
159194
160195 override fun onFailure (error : AuthenticationException ) {
196+ synchronized(recoveryLock) {
197+ if (callbacks.isEmpty()) {
198+ pendingRecovered = RecoveredResult .Failure (error)
199+ return
200+ }
201+ }
161202 for (callback in callbacks) {
162203 callback.onFailure(error)
163204 }
0 commit comments