Skip to content

Commit

Permalink
Refactor SessionStatus
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-tennert committed Sep 20, 2024
1 parent bac3c30 commit 7f22fc0
Show file tree
Hide file tree
Showing 18 changed files with 94 additions and 61 deletions.
10 changes: 6 additions & 4 deletions Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/Auth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -284,15 +286,15 @@ sealed interface Auth : MainPlugin<AuthConfig>, 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
*/
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
Expand Down Expand Up @@ -339,7 +341,7 @@ sealed interface Auth : MainPlugin<AuthConfig>, 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()

Expand Down Expand Up @@ -401,7 +403,7 @@ sealed interface Auth : MainPlugin<AuthConfig>, 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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -64,7 +67,7 @@ internal class AuthImpl(
override val config: AuthConfig
) : Auth {

private val _sessionStatus = MutableStateFlow<SessionStatus>(SessionStatus.LoadingFromStorage)
private val _sessionStatus = MutableStateFlow<SessionStatus>(SessionStatus.Initializing)
override val sessionStatus: StateFlow<SessionStatus> = _sessionStatus.asStateFlow()
internal val authScope = CoroutineScope(config.coroutineDispatcher)
override val sessionManager = config.sessionManager ?: createDefaultSessionManager()
Expand Down Expand Up @@ -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 {
Expand All @@ -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()
}
Expand Down Expand Up @@ -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
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Check warning

Code scanning / detekt

Public properties require documentation. Warning

The property exception is missing documentation.

/**
* The refresh failed due to an internal server error
*/
data class InternalServerError(val exception: RestException) : RefreshFailureCause

Check warning

Code scanning / detekt

Public properties require documentation. Warning

The property exception is missing documentation.

}
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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

Check warning

Code scanning / detekt

Public properties require documentation. Warning

The property cause is missing documentation.

/**
* 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

}

}
2 changes: 1 addition & 1 deletion Auth/src/commonTest/kotlin/AuthApiTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Auth/src/commonTest/kotlin/AuthTest.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion Auth/src/commonTest/kotlin/AuthTestUtils.kt
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion buildSrc/src/main/kotlin/KotlinTargets.kt
Original file line number Diff line number Diff line change
@@ -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()
Expand Down Expand Up @@ -67,6 +67,7 @@ fun KotlinMultiplatformExtension.jsTarget() {
}
}

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
fun KotlinMultiplatformExtension.wasmJsTarget() {
wasmJs {
browser {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7f22fc0

Please sign in to comment.