diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt index d7c1ad00..a89213ac 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt @@ -12,6 +12,8 @@ import io.github.jan.supabase.auth.providers.OAuthProvider import io.github.jan.supabase.auth.providers.builtin.Email import io.github.jan.supabase.auth.providers.builtin.Phone import io.github.jan.supabase.auth.providers.builtin.SSO +import io.github.jan.supabase.auth.status.SessionSource +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.auth.user.UserInfo import io.github.jan.supabase.auth.user.UserSession import io.github.jan.supabase.auth.user.UserUpdateBuilder @@ -284,7 +286,7 @@ sealed interface Auth : MainPlugin, CustomSerializationPlugin { /** * Retrieves the current user with the current session - * @param updateSession Whether to update [sessionStatus] with the updated user, if [sessionStatus] is [SessionStatus.Authenticated] + * @param updateSession Whether to update [sessionStatus] with the updated user, if [sessionStatus] is [io.github.jan.supabase.auth.status.SessionStatus.Authenticated] * @throws RestException or one of its subclasses if receiving an error response. If the error response contains a error code, an [AuthRestException] will be thrown which can be used to easier identify the problem. * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues @@ -292,7 +294,7 @@ sealed interface Auth : MainPlugin, CustomSerializationPlugin { suspend fun retrieveUserForCurrentSession(updateSession: Boolean = false): UserInfo /** - * Signs out the current user, which means [sessionStatus] will be [SessionStatus.NotAuthenticated] and the access token will be revoked + * Signs out the current user, which means [sessionStatus] will be [io.github.jan.supabase.auth.status.SessionStatus.NotAuthenticated] and the access token will be revoked * @param scope The scope of the sign-out. * @throws RestException or one of its subclasses if receiving an error response. If the error response contains a error code, an [AuthRestException] will be thrown which can be used to easier identify the problem. * @throws HttpRequestTimeoutException if the request timed out @@ -339,7 +341,7 @@ sealed interface Auth : MainPlugin, CustomSerializationPlugin { suspend fun refreshCurrentSession() /** - * Deletes the current session from storage and sets [sessionStatus] to [SessionStatus.NotAuthenticated] + * Deletes the current session from storage and sets [sessionStatus] to [io.github.jan.supabase.auth.status.SessionStatus.NotAuthenticated] */ suspend fun clearSession() @@ -401,7 +403,7 @@ sealed interface Auth : MainPlugin, CustomSerializationPlugin { /** * Blocks the current coroutine until the plugin is initialized. * - * This will make sure that the [SessionStatus] is set to [SessionStatus.Authenticated], [SessionStatus.NotAuthenticated] or [SessionStatus.NetworkError]. + * This will make sure that the [io.github.jan.supabase.auth.status.SessionStatus] is set to [io.github.jan.supabase.auth.status.SessionStatus.Authenticated], [io.github.jan.supabase.auth.status.SessionStatus.NotAuthenticated] or [io.github.jan.supabase.auth.status.SessionStatus.RefreshError]. */ suspend fun awaitInitialization() diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt index b7fa0ec5..b699ae83 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt @@ -15,6 +15,9 @@ import io.github.jan.supabase.auth.providers.ExternalAuthConfigDefaults import io.github.jan.supabase.auth.providers.OAuthProvider import io.github.jan.supabase.auth.providers.builtin.OTP import io.github.jan.supabase.auth.providers.builtin.SSO +import io.github.jan.supabase.auth.status.RefreshFailureCause +import io.github.jan.supabase.auth.status.SessionSource +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.auth.user.UserInfo import io.github.jan.supabase.auth.user.UserSession import io.github.jan.supabase.auth.user.UserUpdateBuilder @@ -64,7 +67,7 @@ internal class AuthImpl( override val config: AuthConfig ) : Auth { - private val _sessionStatus = MutableStateFlow(SessionStatus.LoadingFromStorage) + private val _sessionStatus = MutableStateFlow(SessionStatus.Initializing) override val sessionStatus: StateFlow = _sessionStatus.asStateFlow() internal val authScope = CoroutineScope(config.coroutineDispatcher) override val sessionManager = config.sessionManager ?: createDefaultSessionManager() @@ -438,7 +441,7 @@ internal class AuthImpl( } catch (e: RestException) { if (e.statusCode in 500..599) { Auth.logger.e(e) { "Couldn't refresh session due to an internal server error. Retrying in ${config.retryDelay} (Status code ${e.statusCode})" } - _sessionStatus.value = SessionStatus.NetworkError //possibly rename this to something more generic + _sessionStatus.value = SessionStatus.RefreshFailure(RefreshFailureCause.InternalServerError(e)) delay(config.retryDelay) retry() } else { @@ -447,7 +450,7 @@ internal class AuthImpl( } } catch (e: Exception) { 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}" } - _sessionStatus.value = SessionStatus.NetworkError + _sessionStatus.value = SessionStatus.RefreshFailure(RefreshFailureCause.NetworkError(e)) delay(config.retryDelay) retry() } @@ -566,11 +569,11 @@ internal class AuthImpl( } override suspend fun awaitInitialization() { - sessionStatus.first { it !is SessionStatus.LoadingFromStorage } + sessionStatus.first { it !is SessionStatus.Initializing } } fun resetLoadingState() { - _sessionStatus.value = SessionStatus.LoadingFromStorage + _sessionStatus.value = SessionStatus.Initializing } /** diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt index bb3f4ce3..a3d7c177 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Utils.kt @@ -2,6 +2,7 @@ package io.github.jan.supabase.auth import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.annotations.SupabaseInternal +import io.github.jan.supabase.auth.status.SessionSource import io.github.jan.supabase.auth.user.UserSession import io.github.jan.supabase.logging.d import io.ktor.client.request.HttpRequestBuilder diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaApi.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaApi.kt index b7ba3ab1..33dfe786 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaApi.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/mfa/MfaApi.kt @@ -2,8 +2,8 @@ package io.github.jan.supabase.auth.mfa import io.github.jan.supabase.auth.Auth import io.github.jan.supabase.auth.AuthImpl -import io.github.jan.supabase.auth.SessionStatus import io.github.jan.supabase.auth.providers.builtin.Phone +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.auth.user.UserMfaFactor import io.github.jan.supabase.auth.user.UserSession import io.github.jan.supabase.putJsonObject diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/RefreshFailureCause.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/RefreshFailureCause.kt new file mode 100644 index 00000000..856e93ba --- /dev/null +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/RefreshFailureCause.kt @@ -0,0 +1,20 @@ +package io.github.jan.supabase.auth.status + +import io.github.jan.supabase.exceptions.RestException + +/** + * Represents the cause of a refresh error + */ +sealed interface RefreshFailureCause { + + /** + * The refresh failed due to a network error + */ + data class NetworkError(val exception: Throwable) : RefreshFailureCause + + /** + * The refresh failed due to an internal server error + */ + data class InternalServerError(val exception: RestException) : RefreshFailureCause + +} \ No newline at end of file diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionStatus.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/SessionSource.kt similarity index 54% rename from Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionStatus.kt rename to Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/SessionSource.kt index 73e04f6d..766ecab9 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/SessionStatus.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/SessionSource.kt @@ -1,46 +1,9 @@ -package io.github.jan.supabase.auth +package io.github.jan.supabase.auth.status +import io.github.jan.supabase.auth.Auth import io.github.jan.supabase.auth.providers.AuthProvider import io.github.jan.supabase.auth.user.UserSession -/** - * Represents the status of the current session in [Auth] - */ -sealed interface SessionStatus { - - /** - * This status means that the user is not logged in - * @param isSignOut Whether this status was caused by a sign out - */ - data class NotAuthenticated(val isSignOut: Boolean) : SessionStatus - - /** - * This status means that [Auth] is currently loading the session from storage - */ - data object LoadingFromStorage : SessionStatus - - /** - * This status means that [Auth] had an error while refreshing the session - */ - data object NetworkError : SessionStatus - - /** - * This status means that [Auth] holds a valid session - * @param session The session - * @param source The source of the session - */ - data class Authenticated(val session: UserSession, val source: SessionSource = SessionSource.Unknown) : SessionStatus { - - /** - * Whether the session is new, i.e. [source] is [SessionSource.SignIn], [SessionSource.SignUp] or [SessionSource.External]. - * Use this to determine whether this status is the result of a new sign in or sign up or just a session refresh. - */ - val isNew: Boolean = source is SessionSource.SignIn || source is SessionSource.SignUp || source is SessionSource.External - - } - -} - /** * Represents the source of a session */ @@ -63,13 +26,13 @@ sealed interface SessionSource { data class SignIn(val provider: AuthProvider<*, *>) : SessionSource /** - * The session was loaded from a sign up (only if auto-confirm is enabled) + * The session was loaded from a sign-up (only if auto-confirm is enabled) * @param provider The provider that was used to sign up */ data class SignUp(val provider: AuthProvider<*, *>) : SessionSource /** - * The session comes from an external source, e.g. OAuth via deeplinks. + * The session comes from an external source, e.g. OAuth via deep links. */ data object External : SessionSource diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/SessionStatus.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/SessionStatus.kt new file mode 100644 index 00000000..44097535 --- /dev/null +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/status/SessionStatus.kt @@ -0,0 +1,42 @@ +package io.github.jan.supabase.auth.status + +import io.github.jan.supabase.auth.Auth +import io.github.jan.supabase.auth.user.UserSession + +/** + * Represents the status of the current session in [Auth] + */ +sealed interface SessionStatus { + + /** + * This status means that the user is not logged in + * @param isSignOut Whether this status was caused by a sign-out + */ + data class NotAuthenticated(val isSignOut: Boolean) : SessionStatus + + /** + * This status means that [Auth] is currently initializing the session + */ + data object Initializing : SessionStatus + + /** + * This status means that [Auth] had an error while refreshing the session + */ + data class RefreshFailure(val cause: RefreshFailureCause) : SessionStatus + + /** + * This status means that [Auth] holds a valid session + * @param session The session + * @param source The source of the session + */ + data class Authenticated(val session: UserSession, val source: SessionSource = SessionSource.Unknown) : SessionStatus { + + /** + * Whether the session is new, i.e. [source] is [SessionSource.SignIn], [SessionSource.SignUp] or [SessionSource.External]. + * Use this to determine whether this status is the result of a new sign in or sign up or just a session refresh. + */ + val isNew: Boolean = source is SessionSource.SignIn || source is SessionSource.SignUp || source is SessionSource.External + + } + +} \ No newline at end of file diff --git a/Auth/src/commonTest/kotlin/AuthApiTest.kt b/Auth/src/commonTest/kotlin/AuthApiTest.kt index 364ffdb3..e87b4238 100644 --- a/Auth/src/commonTest/kotlin/AuthApiTest.kt +++ b/Auth/src/commonTest/kotlin/AuthApiTest.kt @@ -4,7 +4,6 @@ import io.github.jan.supabase.auth.AuthConfig import io.github.jan.supabase.auth.FlowType import io.github.jan.supabase.auth.OtpType import io.github.jan.supabase.auth.PKCEConstants -import io.github.jan.supabase.auth.SessionSource import io.github.jan.supabase.auth.SignOutScope import io.github.jan.supabase.auth.auth import io.github.jan.supabase.auth.minimalSettings @@ -13,6 +12,7 @@ import io.github.jan.supabase.auth.providers.builtin.Email import io.github.jan.supabase.auth.providers.builtin.IDToken import io.github.jan.supabase.auth.providers.builtin.OTP import io.github.jan.supabase.auth.providers.builtin.Phone +import io.github.jan.supabase.auth.status.SessionSource import io.github.jan.supabase.testing.assertMethodIs import io.github.jan.supabase.testing.assertPathIs import io.github.jan.supabase.testing.createMockedSupabaseClient diff --git a/Auth/src/commonTest/kotlin/AuthTest.kt b/Auth/src/commonTest/kotlin/AuthTest.kt index ed071de9..eeb0ee5b 100644 --- a/Auth/src/commonTest/kotlin/AuthTest.kt +++ b/Auth/src/commonTest/kotlin/AuthTest.kt @@ -1,10 +1,10 @@ import io.github.jan.supabase.SupabaseClientBuilder import io.github.jan.supabase.auth.Auth import io.github.jan.supabase.auth.MemorySessionManager -import io.github.jan.supabase.auth.SessionStatus import io.github.jan.supabase.auth.auth import io.github.jan.supabase.auth.minimalSettings import io.github.jan.supabase.auth.providers.Github +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.auth.user.Identity import io.github.jan.supabase.auth.user.UserInfo import io.github.jan.supabase.auth.user.UserSession diff --git a/Auth/src/commonTest/kotlin/AuthTestUtils.kt b/Auth/src/commonTest/kotlin/AuthTestUtils.kt index 48439e43..7e7140ec 100644 --- a/Auth/src/commonTest/kotlin/AuthTestUtils.kt +++ b/Auth/src/commonTest/kotlin/AuthTestUtils.kt @@ -1,5 +1,5 @@ import io.github.jan.supabase.auth.Auth -import io.github.jan.supabase.auth.SessionStatus +import io.github.jan.supabase.auth.status.SessionStatus fun Auth.sessionSource() = (sessionStatus.value as SessionStatus.Authenticated).source diff --git a/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt index f5eb65a3..46db2f7d 100644 --- a/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt +++ b/Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt @@ -1,6 +1,7 @@ package io.github.jan.supabase.auth import io.github.jan.supabase.annotations.SupabaseInternal +import io.github.jan.supabase.auth.status.SessionSource import io.ktor.util.PlatformUtils.IS_BROWSER import kotlinx.browser.window import kotlinx.coroutines.launch diff --git a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt index 816d362a..bb30adef 100644 --- a/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt +++ b/Realtime/src/commonMain/kotlin/io/github/jan/supabase/realtime/RealtimeImpl.kt @@ -3,7 +3,7 @@ package io.github.jan.supabase.realtime import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.annotations.SupabaseInternal import io.github.jan.supabase.auth.Auth -import io.github.jan.supabase.auth.SessionStatus +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.buildUrl import io.github.jan.supabase.collections.AtomicMutableMap import io.github.jan.supabase.exceptions.RestException diff --git a/buildSrc/src/main/kotlin/KotlinTargets.kt b/buildSrc/src/main/kotlin/KotlinTargets.kt index 8b6e82e7..77ee994f 100644 --- a/buildSrc/src/main/kotlin/KotlinTargets.kt +++ b/buildSrc/src/main/kotlin/KotlinTargets.kt @@ -1,8 +1,8 @@ import org.gradle.kotlin.dsl.assign import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl fun KotlinMultiplatformExtension.iosTargets() { iosX64() @@ -67,6 +67,7 @@ fun KotlinMultiplatformExtension.jsTarget() { } } +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) fun KotlinMultiplatformExtension.wasmJsTarget() { wasmJs { browser { diff --git a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt index 1b0b9f18..198470a6 100644 --- a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt +++ b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/ComposeAuth.kt @@ -2,12 +2,12 @@ package io.github.jan.supabase.compose.auth import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.SupabaseSerializer -import io.github.jan.supabase.auth.SessionStatus import io.github.jan.supabase.auth.auth import io.github.jan.supabase.auth.providers.Apple import io.github.jan.supabase.auth.providers.Google import io.github.jan.supabase.auth.providers.IDTokenProvider import io.github.jan.supabase.auth.providers.builtin.IDToken +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.compose.auth.composable.NativeSignInState import io.github.jan.supabase.logging.SupabaseLogger import io.github.jan.supabase.logging.d diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt index 9e856858..055acdf1 100644 --- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt +++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt @@ -11,7 +11,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import io.github.jan.supabase.auth.SessionStatus +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.common.ui.screen.ChatScreen import io.github.jan.supabase.common.ui.screen.LoginScreen diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt index 0aee98f1..0fb064e5 100644 --- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt +++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/ChatViewModel.kt @@ -3,7 +3,7 @@ package io.github.jan.supabase.common import co.touchlab.kermit.Logger import io.github.jan.supabase.SupabaseClient -import io.github.jan.supabase.auth.SessionStatus +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.common.net.AuthApi import io.github.jan.supabase.common.net.Message import io.github.jan.supabase.common.net.MessageApi diff --git a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/AuthApi.kt b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/AuthApi.kt index e6c6315f..aded209d 100644 --- a/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/AuthApi.kt +++ b/sample/chat-demo-mpp/common/src/commonMain/kotlin/io/github/jan/supabase/common/net/AuthApi.kt @@ -2,10 +2,10 @@ package io.github.jan.supabase.common.net import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.auth.OtpType -import io.github.jan.supabase.auth.SessionStatus import io.github.jan.supabase.auth.auth import io.github.jan.supabase.auth.providers.Google import io.github.jan.supabase.auth.providers.builtin.Email +import io.github.jan.supabase.auth.status.SessionStatus import kotlinx.coroutines.flow.Flow sealed interface AuthApi { diff --git a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt index 1eac458f..ee81434f 100644 --- a/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt +++ b/sample/multi-factor-auth/common/src/commonMain/kotlin/io/github/jan/supabase/common/App.kt @@ -11,7 +11,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import io.github.jan.supabase.auth.SessionStatus +import io.github.jan.supabase.auth.status.SessionStatus import io.github.jan.supabase.common.ui.components.AlertDialog import io.github.jan.supabase.common.ui.screen.LoginScreen import io.github.jan.supabase.common.ui.screen.MfaScreen