@@ -5,6 +5,7 @@ import io.github.jan.supabase.annotations.SupabaseExperimental
5
5
import io.github.jan.supabase.annotations.SupabaseInternal
6
6
import io.github.jan.supabase.auth.admin.AdminApi
7
7
import io.github.jan.supabase.auth.admin.AdminApiImpl
8
+ import io.github.jan.supabase.auth.event.AuthEvent
8
9
import io.github.jan.supabase.auth.exception.AuthRestException
9
10
import io.github.jan.supabase.auth.exception.AuthSessionMissingException
10
11
import io.github.jan.supabase.auth.exception.AuthWeakPasswordException
@@ -44,8 +45,11 @@ import kotlinx.coroutines.SupervisorJob
44
45
import kotlinx.coroutines.cancel
45
46
import kotlinx.coroutines.delay
46
47
import kotlinx.coroutines.ensureActive
48
+ import kotlinx.coroutines.flow.MutableSharedFlow
47
49
import kotlinx.coroutines.flow.MutableStateFlow
50
+ import kotlinx.coroutines.flow.SharedFlow
48
51
import kotlinx.coroutines.flow.StateFlow
52
+ import kotlinx.coroutines.flow.asSharedFlow
49
53
import kotlinx.coroutines.flow.asStateFlow
50
54
import kotlinx.coroutines.flow.first
51
55
import kotlinx.coroutines.launch
@@ -73,6 +77,9 @@ internal class AuthImpl(
73
77
74
78
private val _sessionStatus = MutableStateFlow <SessionStatus >(SessionStatus .Initializing )
75
79
override val sessionStatus: StateFlow <SessionStatus > = _sessionStatus .asStateFlow()
80
+ private val _events = MutableSharedFlow <AuthEvent >(replay = 1 )
81
+ override val events: SharedFlow <AuthEvent > = _events .asSharedFlow()
82
+ @Suppress(" DEPRECATION" )
76
83
internal val authScope = CoroutineScope ((config.coroutineDispatcher ? : supabaseClient.coroutineDispatcher) + SupervisorJob ())
77
84
override val sessionManager = config.sessionManager ? : createDefaultSessionManager()
78
85
override val codeVerifierCache = config.codeVerifierCache ? : createDefaultCodeVerifierCache()
@@ -99,7 +106,6 @@ internal class AuthImpl(
99
106
100
107
override fun init () {
101
108
Auth .logger.d { " Initializing Auth plugin..." }
102
- setupPlatform()
103
109
if (config.autoLoadFromStorage) {
104
110
authScope.launch {
105
111
Auth .logger.i {
@@ -112,15 +118,16 @@ internal class AuthImpl(
112
118
}
113
119
} else {
114
120
Auth .logger.i {
115
- " No session found. Setting session status to NotAuthenticated ."
121
+ " No session found in storage ."
116
122
}
117
- _sessionStatus .value = SessionStatus .NotAuthenticated (false )
123
+ setSessionStatus( SessionStatus .NotAuthenticated () )
118
124
}
119
125
}
120
126
} else {
121
127
Auth .logger.d { " Skipping loading from storage (autoLoadFromStorage is set to false)" }
122
- _sessionStatus .value = SessionStatus .NotAuthenticated (false )
128
+ setSessionStatus( SessionStatus .NotAuthenticated () )
123
129
}
130
+ setupPlatform()
124
131
Auth .logger.d { " Initialized Auth plugin" }
125
132
}
126
133
@@ -184,7 +191,7 @@ internal class AuthImpl(
184
191
val session = currentSessionOrNull() ? : return
185
192
val newUser = session.user?.copy(identities = session.user.identities?.filter { it.identityId != identityId })
186
193
val newSession = session.copy(user = newUser)
187
- _sessionStatus .value = SessionStatus .Authenticated (newSession, SessionSource .UserIdentitiesChanged (session))
194
+ setSessionStatus( SessionStatus .Authenticated (newSession, SessionSource .UserIdentitiesChanged (session) ))
188
195
}
189
196
}
190
197
@@ -237,7 +244,7 @@ internal class AuthImpl(
237
244
if (this .config.autoSaveToStorage) {
238
245
sessionManager.saveSession(newSession)
239
246
}
240
- _sessionStatus .value = SessionStatus .Authenticated (newSession, SessionSource .UserChanged (newSession))
247
+ setSessionStatus( SessionStatus .Authenticated (newSession, SessionSource .UserChanged (newSession) ))
241
248
}
242
249
return userInfo
243
250
}
@@ -364,7 +371,7 @@ internal class AuthImpl(
364
371
if (updateSession) {
365
372
val session = currentSessionOrNull() ? : error(" No session found" )
366
373
val newStatus = SessionStatus .Authenticated (session.copy(user = user), SessionSource .UserChanged (currentSessionOrNull() ? : error(" Session shouldn't be null" )))
367
- _sessionStatus .value = newStatus
374
+ setSessionStatus( newStatus)
368
375
if (config.autoSaveToStorage) sessionManager.saveSession(newStatus.session)
369
376
}
370
377
return user
@@ -420,7 +427,7 @@ internal class AuthImpl(
420
427
sessionManager.saveSession(session)
421
428
Auth .logger.d { " Session saved to storage (no auto refresh)" }
422
429
}
423
- _sessionStatus .value = SessionStatus .Authenticated (session, source)
430
+ setSessionStatus( SessionStatus .Authenticated (session, source) )
424
431
Auth .logger.d { " Session imported successfully." }
425
432
return
426
433
}
@@ -429,22 +436,24 @@ internal class AuthImpl(
429
436
Auth .logger.d { " Session is under the threshold date. Refreshing session..." }
430
437
tryImportingSession(
431
438
{ handleExpiredSession(session, config.alwaysAutoRefresh) },
432
- { importSession(session) }
439
+ { importSession(session) },
440
+ { updateStatusIfExpired(session, it) }
433
441
)
434
442
} else {
435
443
if (config.autoSaveToStorage) {
436
444
sessionManager.saveSession(session)
437
445
Auth .logger.d { " Session saved to storage (auto refresh enabled)" }
438
446
}
439
- _sessionStatus .value = SessionStatus .Authenticated (session, source)
447
+ setSessionStatus( SessionStatus .Authenticated (session, source) )
440
448
Auth .logger.d { " Session imported successfully. Starting auto refresh..." }
441
449
sessionJob?.cancel()
442
450
sessionJob = authScope.launch {
443
451
delayBeforeExpiry(session)
444
452
launch {
445
453
tryImportingSession(
446
454
{ handleExpiredSession(session) },
447
- { importSession(session, source = source) }
455
+ { importSession(session, source = source) },
456
+ { updateStatusIfExpired(session, it) }
448
457
)
449
458
}
450
459
}
@@ -455,14 +464,15 @@ internal class AuthImpl(
455
464
@Suppress(" MagicNumber" )
456
465
private suspend fun tryImportingSession (
457
466
importRefreshedSession : suspend () -> Unit ,
458
- retry : suspend () -> Unit
467
+ retry : suspend () -> Unit ,
468
+ updateStatus : suspend (RefreshFailureCause ) -> Unit
459
469
) {
460
470
try {
461
471
importRefreshedSession()
462
472
} catch (e: RestException ) {
463
473
if (e.statusCode in 500 .. 599 ) {
464
474
Auth .logger.e(e) { " Couldn't refresh session due to an internal server error. Retrying in ${config.retryDelay} (Status code ${e.statusCode} )..." }
465
- _sessionStatus .value = SessionStatus . RefreshFailure (RefreshFailureCause .InternalServerError (e))
475
+ updateStatus (RefreshFailureCause .InternalServerError (e))
466
476
delay(config.retryDelay)
467
477
retry()
468
478
} else {
@@ -472,12 +482,20 @@ internal class AuthImpl(
472
482
} catch (e: Exception ) {
473
483
coroutineContext.ensureActive()
474
484
Auth .logger.e(e) { " Couldn't reach Supabase. Either the address doesn't exist or the network might not be on. Retrying in ${config.retryDelay} ..." }
475
- _sessionStatus .value = SessionStatus . RefreshFailure (RefreshFailureCause .NetworkError (e))
485
+ updateStatus (RefreshFailureCause .NetworkError (e))
476
486
delay(config.retryDelay)
477
487
retry()
478
488
}
479
489
}
480
490
491
+ private fun updateStatusIfExpired (session : UserSession , reason : RefreshFailureCause ) {
492
+ if (session.expiresAt <= Clock .System .now()) {
493
+ Auth .logger.d { " Session expired while trying to refresh the session. Updating status..." }
494
+ setSessionStatus(SessionStatus .RefreshFailure (reason))
495
+ }
496
+ emitEvent(AuthEvent .RefreshFailure (reason))
497
+ }
498
+
481
499
private suspend fun delayBeforeExpiry (session : UserSession ) {
482
500
val timeAtBeginningOfSession = session.expiresAt - session.expiresIn.seconds
483
501
@@ -590,16 +608,22 @@ internal class AuthImpl(
590
608
codeVerifierCache.deleteCodeVerifier()
591
609
sessionManager.deleteSession()
592
610
sessionJob?.cancel()
593
- _sessionStatus .value = SessionStatus .NotAuthenticated (true )
611
+ setSessionStatus( SessionStatus .NotAuthenticated (true ) )
594
612
sessionJob = null
595
613
}
596
614
597
615
override suspend fun awaitInitialization () {
598
616
sessionStatus.first { it !is SessionStatus .Initializing }
599
617
}
600
618
601
- fun resetLoadingState () {
602
- _sessionStatus .value = SessionStatus .Initializing
619
+ override fun setSessionStatus (status : SessionStatus ) {
620
+ Auth .logger.d { " Setting session status to $status " }
621
+ _sessionStatus .value = status
622
+ }
623
+
624
+ override fun emitEvent (event : AuthEvent ) {
625
+ Auth .logger.d { " Emitting event $event " }
626
+ _events .tryEmit(event)
603
627
}
604
628
605
629
/* *
0 commit comments