From 14f0c51864b2829508a39fb9d9d702bcb67af9a9 Mon Sep 17 00:00:00 2001 From: Jan Tennert Date: Fri, 10 Jan 2025 14:24:47 +0100 Subject: [PATCH 1/3] Make the dialog type for the Native Google Sign In configurable --- .../github/jan/supabase/compose/auth/Utils.kt | 16 +++++- .../compose/auth/composable/GoogleAuth.kt | 49 ++++++++++++++----- .../compose/auth/composable/GoogleAuth.kt | 6 ++- .../auth/composable/NativeGoogleAuth.kt | 15 ++++++ .../compose/auth/composable/GoogleAuth.kt | 6 ++- 5 files changed, 76 insertions(+), 16 deletions(-) diff --git a/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/Utils.kt b/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/Utils.kt index b4c00fe47..466795f34 100644 --- a/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/Utils.kt +++ b/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/Utils.kt @@ -3,9 +3,10 @@ package io.github.jan.supabase.compose.auth import android.app.Activity import android.content.Context import android.content.ContextWrapper +import com.google.android.libraries.identity.googleid.GetGoogleIdOption import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption -internal fun getSignInWithGoogleOptions( +internal fun getGoogleButtonOptions( config: GoogleLoginConfig?, nonce: String? = null ): GetSignInWithGoogleOption { @@ -14,6 +15,19 @@ internal fun getSignInWithGoogleOptions( return signInWithGoogleOption.build() } +internal fun getGoogleBottomSheetOptions( + config: GoogleLoginConfig?, + filterByAuthorizedAccounts: Boolean = true, + nonce: String? = null +): GetGoogleIdOption { + val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder() + .setFilterByAuthorizedAccounts(filterByAuthorizedAccounts) + .setServerClientId(config?.serverClientId ?: error("Trying to use Google Auth without setting the serverClientId")) + .setNonce(nonce) + .build() + return googleIdOption +} + internal fun Context.getActivity(): Activity? = when (this) { is Activity -> this is ContextWrapper -> baseContext.getActivity() diff --git a/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt b/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt index 91c11e5c8..e7efd7c60 100644 --- a/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt +++ b/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt @@ -1,5 +1,7 @@ package io.github.jan.supabase.compose.auth.composable +import android.app.Activity +import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -17,7 +19,8 @@ import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingExcept import io.github.jan.supabase.compose.auth.ComposeAuth import io.github.jan.supabase.compose.auth.applicationContext import io.github.jan.supabase.compose.auth.getActivity -import io.github.jan.supabase.compose.auth.getSignInWithGoogleOptions +import io.github.jan.supabase.compose.auth.getGoogleBottomSheetOptions +import io.github.jan.supabase.compose.auth.getGoogleButtonOptions import io.github.jan.supabase.compose.auth.hash import io.github.jan.supabase.compose.auth.signInWithGoogle import io.github.jan.supabase.logging.d @@ -33,12 +36,20 @@ import io.github.jan.supabase.logging.e * @return [NativeSignInState] */ @Composable -actual fun ComposeAuth.rememberSignInWithGoogle(onResult: (NativeSignInResult) -> Unit, fallback: suspend () -> Unit): NativeSignInState { - return signInWithCM(onResult, fallback) +actual fun ComposeAuth.rememberSignInWithGoogle( + onResult: (NativeSignInResult) -> Unit, + type: GoogleDialogType, + fallback: suspend () -> Unit) +: NativeSignInState { + return signInWithCM(onResult, type, fallback) } @Composable -internal fun ComposeAuth.signInWithCM(onResult: (NativeSignInResult) -> Unit, fallback: suspend () -> Unit): NativeSignInState{ +internal fun ComposeAuth.signInWithCM( + onResult: (NativeSignInResult) -> Unit, + type: GoogleDialogType, + fallback: suspend () -> Unit +): NativeSignInState{ val state = remember { NativeSignInState(serializer) } val context = LocalContext.current @@ -50,7 +61,7 @@ internal fun ComposeAuth.signInWithCM(onResult: (NativeSignInResult) -> Unit, fa if (activity != null && config.googleLoginConfig != null) { val hashedNonce = status.nonce?.hash() ComposeAuth.logger.d { "Starting Google Sign In Flow${if(hashedNonce != null) " with hashed nonce: $hashedNonce" else ""}" } - val response = makeRequest(context, activity, config, hashedNonce) + val response = makeRequest(context, activity, config, hashedNonce, type) if(response == null) { onResult.invoke(NativeSignInResult.ClosedByUser) ComposeAuth.logger.d { "Google Sign In Flow was closed by user" } @@ -136,27 +147,39 @@ internal actual suspend fun handleGoogleSignOut() { } private suspend fun tryRequest( - context: android.content.Context, - activity: android.app.Activity, + context: Context, + activity: Activity, config: ComposeAuth.Config, - nonce: String? + nonce: String?, + type: GoogleDialogType, + onlyAuthorizedAccounts: Boolean = true ): GetCredentialResponse { + val option = when(type) { + GoogleDialogType.BOTTOM_SHEET -> getGoogleBottomSheetOptions(config.googleLoginConfig, onlyAuthorizedAccounts, nonce) + GoogleDialogType.DIALOG -> getGoogleButtonOptions(config.googleLoginConfig, nonce) + } val request = GetCredentialRequest.Builder() - .addCredentialOption(getSignInWithGoogleOptions(config.googleLoginConfig, nonce)) + .addCredentialOption(option) .build() return CredentialManager.create(context).getCredential(activity, request) } private suspend fun makeRequest( - context: android.content.Context, - activity: android.app.Activity, + context: Context, + activity: Activity, config: ComposeAuth.Config, - nonce: String? + nonce: String?, + type: GoogleDialogType ): GetCredentialResponse? { return try { ComposeAuth.logger.d { "Trying to get Google ID Token Credential" } - tryRequest(context, activity, config, nonce) + tryRequest(context, activity, config, nonce, type) } catch(e: GetCredentialCancellationException) { return null + } catch(e: GetCredentialException) { + if(type == GoogleDialogType.BOTTOM_SHEET) { + ComposeAuth.logger.d { "Error while trying to get Google ID Token Credential. Retrying without only authorized accounts" } + tryRequest(context, activity, config, nonce, type, false) + } else null } } \ No newline at end of file diff --git a/plugins/ComposeAuth/src/appleMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt b/plugins/ComposeAuth/src/appleMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt index aeb1b0000..7abf9e736 100644 --- a/plugins/ComposeAuth/src/appleMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt +++ b/plugins/ComposeAuth/src/appleMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt @@ -14,6 +14,10 @@ import io.github.jan.supabase.compose.auth.defaultLoginBehavior * @return [NativeSignInState] */ @Composable -actual fun ComposeAuth.rememberSignInWithGoogle(onResult: (NativeSignInResult) -> Unit, fallback: suspend () -> Unit): NativeSignInState = defaultLoginBehavior(fallback) +actual fun ComposeAuth.rememberSignInWithGoogle( + onResult: (NativeSignInResult) -> Unit, + type: GoogleDialogType, + fallback: suspend () -> Unit +): NativeSignInState = defaultLoginBehavior(fallback) internal actual suspend fun handleGoogleSignOut() = Unit \ No newline at end of file diff --git a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt index 8fbb4899d..c0cd71bd0 100644 --- a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt +++ b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt @@ -5,6 +5,20 @@ import io.github.jan.supabase.auth.providers.Google import io.github.jan.supabase.compose.auth.ComposeAuth import io.github.jan.supabase.compose.auth.fallbackLogin +/** + * Enum class for the type of Google Dialog + */ +enum class GoogleDialogType { + /** + * A bottom sheet dialog + */ + BOTTOM_SHEET, + /** + * A standard dialog + */ + DIALOG +} + /** * Composable function that implements Native Google Auth. * @@ -17,6 +31,7 @@ import io.github.jan.supabase.compose.auth.fallbackLogin @Composable expect fun ComposeAuth.rememberSignInWithGoogle( onResult: (NativeSignInResult) -> Unit = {}, + type: GoogleDialogType = GoogleDialogType.DIALOG, fallback: suspend () -> Unit = { fallbackLogin(Google) } diff --git a/plugins/ComposeAuth/src/noDefaultMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt b/plugins/ComposeAuth/src/noDefaultMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt index aeb1b0000..7abf9e736 100644 --- a/plugins/ComposeAuth/src/noDefaultMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt +++ b/plugins/ComposeAuth/src/noDefaultMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt @@ -14,6 +14,10 @@ import io.github.jan.supabase.compose.auth.defaultLoginBehavior * @return [NativeSignInState] */ @Composable -actual fun ComposeAuth.rememberSignInWithGoogle(onResult: (NativeSignInResult) -> Unit, fallback: suspend () -> Unit): NativeSignInState = defaultLoginBehavior(fallback) +actual fun ComposeAuth.rememberSignInWithGoogle( + onResult: (NativeSignInResult) -> Unit, + type: GoogleDialogType, + fallback: suspend () -> Unit +): NativeSignInState = defaultLoginBehavior(fallback) internal actual suspend fun handleGoogleSignOut() = Unit \ No newline at end of file From fb3dcef565f58036856b50d26b928299c8ac0fe6 Mon Sep 17 00:00:00 2001 From: Jan Tennert Date: Sat, 11 Jan 2025 14:08:55 +0100 Subject: [PATCH 2/3] Suppress warning --- .../jan/supabase/compose/auth/composable/NativeGoogleAuth.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt index c0cd71bd0..f95d195db 100644 --- a/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt +++ b/plugins/ComposeAuth/src/commonMain/kotlin/io/github/jan/supabase/compose/auth/composable/NativeGoogleAuth.kt @@ -1,3 +1,4 @@ +@file:Suppress("MatchingDeclarationName") package io.github.jan.supabase.compose.auth.composable import androidx.compose.runtime.Composable From 7903a73f92f17667838c695519a497c0dac69da3 Mon Sep 17 00:00:00 2001 From: Jan Tennert Date: Sat, 11 Jan 2025 14:19:22 +0100 Subject: [PATCH 3/3] Improve code --- .../compose/auth/composable/GoogleAuth.kt | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt b/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt index e7efd7c60..d526a8247 100644 --- a/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt +++ b/plugins/ComposeAuth/src/androidMain/kotlin/io/github/jan/supabase/compose/auth/composable/GoogleAuth.kt @@ -17,6 +17,7 @@ import androidx.credentials.exceptions.GetCredentialException import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException import io.github.jan.supabase.compose.auth.ComposeAuth +import io.github.jan.supabase.compose.auth.GoogleLoginConfig import io.github.jan.supabase.compose.auth.applicationContext import io.github.jan.supabase.compose.auth.getActivity import io.github.jan.supabase.compose.auth.getGoogleBottomSheetOptions @@ -26,6 +27,13 @@ import io.github.jan.supabase.compose.auth.signInWithGoogle import io.github.jan.supabase.logging.d import io.github.jan.supabase.logging.e +private data class GoogleRequestOptions( + val config: GoogleLoginConfig?, + val nonce: String?, + val filterByAuthorizedAccounts: Boolean = false, + val type: GoogleDialogType +) + /** * Composable function that implements Native Google Auth. * @@ -61,7 +69,11 @@ internal fun ComposeAuth.signInWithCM( if (activity != null && config.googleLoginConfig != null) { val hashedNonce = status.nonce?.hash() ComposeAuth.logger.d { "Starting Google Sign In Flow${if(hashedNonce != null) " with hashed nonce: $hashedNonce" else ""}" } - val response = makeRequest(context, activity, config, hashedNonce, type) + val response = makeRequest( + context, + activity, + GoogleRequestOptions(config = config.googleLoginConfig, nonce = hashedNonce, type = type) + ) if(response == null) { onResult.invoke(NativeSignInResult.ClosedByUser) ComposeAuth.logger.d { "Google Sign In Flow was closed by user" } @@ -149,14 +161,11 @@ internal actual suspend fun handleGoogleSignOut() { private suspend fun tryRequest( context: Context, activity: Activity, - config: ComposeAuth.Config, - nonce: String?, - type: GoogleDialogType, - onlyAuthorizedAccounts: Boolean = true + options: GoogleRequestOptions ): GetCredentialResponse { - val option = when(type) { - GoogleDialogType.BOTTOM_SHEET -> getGoogleBottomSheetOptions(config.googleLoginConfig, onlyAuthorizedAccounts, nonce) - GoogleDialogType.DIALOG -> getGoogleButtonOptions(config.googleLoginConfig, nonce) + val option = when(options.type) { + GoogleDialogType.BOTTOM_SHEET -> getGoogleBottomSheetOptions(options.config, options.filterByAuthorizedAccounts, options.nonce) + GoogleDialogType.DIALOG -> getGoogleButtonOptions(options.config, options.nonce) } val request = GetCredentialRequest.Builder() .addCredentialOption(option) @@ -167,19 +176,17 @@ private suspend fun tryRequest( private suspend fun makeRequest( context: Context, activity: Activity, - config: ComposeAuth.Config, - nonce: String?, - type: GoogleDialogType + options: GoogleRequestOptions ): GetCredentialResponse? { return try { ComposeAuth.logger.d { "Trying to get Google ID Token Credential" } - tryRequest(context, activity, config, nonce, type) + tryRequest(context, activity, options) } catch(e: GetCredentialCancellationException) { return null } catch(e: GetCredentialException) { - if(type == GoogleDialogType.BOTTOM_SHEET) { + if(options.type == GoogleDialogType.BOTTOM_SHEET) { ComposeAuth.logger.d { "Error while trying to get Google ID Token Credential. Retrying without only authorized accounts" } - tryRequest(context, activity, config, nonce, type, false) + tryRequest(context, activity, options) } else null } } \ No newline at end of file