From 306c96f084b6f7bb8c406e81ae11b192e95c6f96 Mon Sep 17 00:00:00 2001 From: Harrishan Date: Fri, 20 Dec 2024 02:05:13 +0100 Subject: [PATCH 1/3] fix: remove google auth from `SignIn` screen --- .../periodpals/ui/authentication/SignIn.kt | 127 ------------------ .../ui/authentication/SignInTest.kt | 19 --- 2 files changed, 146 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/ui/authentication/SignIn.kt b/app/src/main/java/com/android/periodpals/ui/authentication/SignIn.kt index d3e5e7b6d..c2664857f 100644 --- a/app/src/main/java/com/android/periodpals/ui/authentication/SignIn.kt +++ b/app/src/main/java/com/android/periodpals/ui/authentication/SignIn.kt @@ -4,19 +4,14 @@ import android.content.Context import android.os.Handler import android.os.Looper import android.widget.Toast -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -25,22 +20,15 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.credentials.CredentialManager -import androidx.credentials.GetCredentialRequest -import androidx.credentials.exceptions.GetCredentialException import com.android.periodpals.R import com.android.periodpals.model.authentication.AuthenticationViewModel import com.android.periodpals.resources.C.Tag.AuthenticationScreens.SignInScreen -import com.android.periodpals.resources.ComponentColor.getFilledPrimaryContainerButtonColors import com.android.periodpals.services.PushNotificationsServiceImpl import com.android.periodpals.ui.components.AuthenticationCard import com.android.periodpals.ui.components.AuthenticationEmailInput @@ -53,12 +41,6 @@ import com.android.periodpals.ui.navigation.NavigationActions import com.android.periodpals.ui.navigation.Screen import com.android.periodpals.ui.theme.dimens import com.dsc.form_builder.TextFieldState -import com.google.android.libraries.identity.googleid.GetGoogleIdOption -import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential -import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException -import java.util.UUID -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch private const val DEFAULT_IS_PASSWORD_VISIBLE = false @@ -138,19 +120,6 @@ fun SignInScreen( }, testTag = SignInScreen.SIGN_IN_BUTTON, ) - - Text( - modifier = - Modifier.fillMaxWidth() - .wrapContentHeight() - .testTag(SignInScreen.CONTINUE_WITH_TEXT), - text = context.getString(R.string.sign_in_continue_with_text), - color = MaterialTheme.colorScheme.onSurface, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.bodyLarge, - ) - - AuthenticationGoogleButton(context, authenticationViewModel, navigationActions) } NavigateBetweenAuthScreens( @@ -163,54 +132,6 @@ fun SignInScreen( } } -/** - * Composable function that displays a button for Google sign-in. - * - * @param context The context used to show Toast messages. - * @param authenticationViewModel The ViewModel that handles authentication logic. - * @param modifier The modifier to be applied to the button. - */ -@Composable -fun AuthenticationGoogleButton( - context: Context, - authenticationViewModel: AuthenticationViewModel, - navigationActions: NavigationActions, - modifier: Modifier = Modifier, -) { - val coroutineScope = rememberCoroutineScope() - Button( - modifier = modifier.wrapContentSize().testTag(SignInScreen.GOOGLE_BUTTON), - onClick = { - attemptAuthenticateWithGoogle( - context = context, - authenticationViewModel = authenticationViewModel, - navigationActions = navigationActions, - coroutineScope = coroutineScope, - ) - }, - colors = getFilledPrimaryContainerButtonColors(), - ) { - Row( - modifier = Modifier.wrapContentSize(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = - Arrangement.spacedBy(MaterialTheme.dimens.small2, Alignment.CenterHorizontally), - ) { - Image( - painter = painterResource(id = R.drawable.google_logo), - contentDescription = "Google Logo", - modifier = Modifier.size(MaterialTheme.dimens.iconSize), - ) - Text( - modifier = Modifier.wrapContentSize(), - text = context.getString(R.string.sign_in_sign_up_with_google), - fontWeight = FontWeight.Medium, - style = MaterialTheme.typography.bodyMedium, - ) - } - } -} - /** * Attempts to sign in the user with the provided email and password. * @@ -260,51 +181,3 @@ private fun attemptSignIn( }, ) } - -/** - * Attempts to authenticate the user with Google. - * - * @param context The context used to show Toast messages. - * @param authenticationViewModel The ViewModel that handles authentication logic. - * @param navigationActions The navigation actions to navigate between screens. - * @param coroutineScope The coroutine scope to launch the authentication process. - * @return A lambda function to be called on button click. - */ -private fun attemptAuthenticateWithGoogle( - context: Context, - authenticationViewModel: AuthenticationViewModel, - navigationActions: NavigationActions, - coroutineScope: CoroutineScope, -) { - // Create a CredentialManager instance - val credentialManager = CredentialManager.create(context) - - val rawNonce = UUID.randomUUID().toString() - - // Configure Google ID option - val googleIdOption: GetGoogleIdOption = - GetGoogleIdOption.Builder() - .setFilterByAuthorizedAccounts(false) - .setServerClientId(context.getString(R.string.google_client_id)) - .setNonce(authenticationViewModel.generateHashCode(rawNonce)) - .build() - - // Create a GetCredentialRequest - val request: GetCredentialRequest = - GetCredentialRequest.Builder().addCredentialOption(googleIdOption).build() - - // Retrieve the credential - coroutineScope.launch { - try { - val result = credentialManager.getCredential(request = request, context = context) - val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(result.credential.data) - authenticationViewModel.loginWithGoogle(googleIdTokenCredential.idToken, rawNonce) - navigationActions.navigateTo(Screen.EDIT_PROFILE) - Toast.makeText(context, "Successful login", Toast.LENGTH_SHORT).show() - } catch (e: GetCredentialException) { - Toast.makeText(context, "Failed to get Google ID token", Toast.LENGTH_SHORT).show() - } catch (e: GoogleIdTokenParsingException) { - Toast.makeText(context, "Failed to parse Google ID token", Toast.LENGTH_SHORT).show() - } - } -} diff --git a/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt b/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt index 3398a0b86..6d4ad8f47 100644 --- a/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt +++ b/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt @@ -46,8 +46,6 @@ class SignInScreenTest { companion object { private const val EMAIL = "test@example.com" private const val PASSWORD = "password" - private const val GOOGLE_ID_TOKEN = "test_token" - private const val RAW_NONCE = "test_nonce" private const val PASSWORD_MAX_LENGTH = 128 private const val EMPTY_EMAIL_ERROR_MESSAGE = "Email cannot be empty" @@ -248,21 +246,4 @@ class SignInScreenTest { verify(navigationActions).navigateTo(Screen.SIGN_UP) } - - @Test - fun googleSignInPerformClick() { - `when`( - authViewModel.loginWithGoogle( - googleIdToken = any(), - rawNonce = any(), - onSuccess = any(), - onFailure = any(), - )) - .thenAnswer { - val onSuccess = it.arguments[2] as () -> Unit - onSuccess() - } - - composeTestRule.onNodeWithTag(SignInScreen.GOOGLE_BUTTON).performScrollTo().performClick() - } } From 17f5ebb76d31ca313492a358939fb9ad03f7c3d5 Mon Sep 17 00:00:00 2001 From: Harrishan Date: Fri, 20 Dec 2024 02:12:59 +0100 Subject: [PATCH 2/3] fix: remove login with google method from the authentication model --- .../authentication/AuthenticationModel.kt | 19 --------- .../AuthenticationModelSupabase.kt | 30 -------------- .../authentication/AuthenticationViewModel.kt | 36 ----------------- .../AuthenticationModelSupabaseTest.kt | 33 ---------------- .../AuthenticationViewModelTest.kt | 39 ------------------- 5 files changed, 157 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModel.kt b/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModel.kt index e7413558e..080108c71 100644 --- a/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModel.kt +++ b/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModel.kt @@ -65,23 +65,4 @@ interface AuthenticationModel { onSuccess: (UserInfo) -> Unit, onFailure: (Exception) -> Unit, ) - - /** - * Logs in a user using Google authentication. - * - * This function uses the Google Sign-In SDK to authenticate the user with their Google account. - * It retrieves the user's Google ID token and uses it to sign in with Supabase. - * - * @param googleIdToken The Google ID token of the user. - * @param rawNonce The raw nonce used to sign the user in. - * @param onSuccess Callback function to be called on successful login. - * @param onFailure Callback function to be called on login failure, with the exception as a - * parameter. - */ - suspend fun loginGoogle( - googleIdToken: String, - rawNonce: String?, - onSuccess: () -> Unit, - onFailure: (Exception) -> Unit - ) } diff --git a/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModelSupabase.kt b/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModelSupabase.kt index 119cc3a9a..fb1ba2aef 100644 --- a/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModelSupabase.kt +++ b/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationModelSupabase.kt @@ -4,9 +4,7 @@ import android.util.Log import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.auth.Auth import io.github.jan.supabase.auth.SignOutScope -import io.github.jan.supabase.auth.providers.Google import io.github.jan.supabase.auth.providers.builtin.Email -import io.github.jan.supabase.auth.providers.builtin.IDToken import io.github.jan.supabase.auth.user.UserInfo private const val TAG = "AuthenticationModelSupabase" @@ -144,32 +142,4 @@ class AuthenticationModelSupabase( onFailure(e) } } - - /** - * Logs in a user using Google authentication. - * - * @param googleIdToken The Google ID token. - * @param rawNonce The raw nonce. - * @param onSuccess Callback function to be called on successful login. - * @param onFailure Callback function to be called on login failure, with the exception as a - * parameter. - */ - override suspend fun loginGoogle( - googleIdToken: String, - rawNonce: String?, - onSuccess: () -> Unit, - onFailure: (Exception) -> Unit - ) { - try { - supabaseAuth.signInWith(IDToken) { - idToken = googleIdToken - provider = Google - nonce = rawNonce - } - onSuccess() - } catch (e: Exception) { - Log.d(TAG, "loginGoogle: failed to log in: ${e.message}") - onFailure(e) - } - } } diff --git a/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationViewModel.kt b/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationViewModel.kt index a36cabe1d..841e89b09 100644 --- a/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationViewModel.kt +++ b/app/src/main/java/com/android/periodpals/model/authentication/AuthenticationViewModel.kt @@ -265,42 +265,6 @@ class AuthenticationViewModel(private val authenticationModel: AuthenticationMod } } - /** - * Logs in a user with the provided Google ID token. - * - * @param googleIdToken The Google ID token. - * @param rawNonce The raw nonce. - * @param onSuccess Callback to be invoked when the login is successful. - * @param onFailure Callback to be invoked when the login fails. - */ - fun loginWithGoogle( - googleIdToken: String, - rawNonce: String?, - onSuccess: () -> Unit = { Log.d(TAG, "loginWithGoogle success callback") }, - onFailure: (Exception) -> Unit = { e: Exception -> - Log.d(TAG, "loginWithGoogle failure callback: $e") - }, - ) { - _userAuthenticationState.value = UserAuthenticationState.Loading - viewModelScope.launch { - authenticationModel.loginGoogle( - googleIdToken, - rawNonce, - onSuccess = { - Log.d(TAG, "loginWithGoogle: logged in successfully") - _userAuthenticationState.value = - UserAuthenticationState.Success("Logged in successfully") - onSuccess() - }, - onFailure = { e: Exception -> - Log.d(TAG, "loginWithGoogle: failed to log in: $e") - _userAuthenticationState.value = UserAuthenticationState.Error("Error: $e") - onFailure(e) - }, - ) - } - } - /** * Generates a hash code from a raw nonce. * diff --git a/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationModelSupabaseTest.kt b/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationModelSupabaseTest.kt index 19de55e52..f2d9d1faf 100644 --- a/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationModelSupabaseTest.kt +++ b/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationModelSupabaseTest.kt @@ -5,7 +5,6 @@ import io.github.jan.supabase.auth.Auth import io.github.jan.supabase.auth.AuthConfig import io.github.jan.supabase.auth.deepLinkOrNull import io.github.jan.supabase.auth.providers.builtin.Email -import io.github.jan.supabase.auth.providers.builtin.IDToken import io.github.jan.supabase.auth.user.UserInfo import junit.framework.TestCase.assertEquals import junit.framework.TestCase.fail @@ -36,8 +35,6 @@ class AuthenticationModelSupabaseTest { private val deepLink = "https://example.com" private val aud = "test_aud" private val id = "test_id" - private val idToken = "test_token" - private val rawNonce = "test_nonce" } @Before @@ -175,34 +172,4 @@ class AuthenticationModelSupabaseTest { onFailure = { assert(true) }, ) } - - @Test - fun `login with google success`() = runBlocking { - `when`(auth.signInWith(IDToken)).thenReturn(Unit) - - var successCalled = false - authModel.loginGoogle( - idToken, - rawNonce, - { successCalled = true }, - { fail("Should not call onFailure") }, - ) - assert(successCalled) - } - - @Test - fun `login with google failure`() = runBlocking { - val exception = RuntimeException("Login failed") - doThrow(exception).`when`(auth).signInWith(any(), anyOrNull(), any()) - - var failureCalled = false - authModel.loginGoogle( - idToken, - rawNonce, - { fail("Should not call onSuccess") }, - { failureCalled = true }, - ) - - assert(failureCalled) - } } diff --git a/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationViewModelTest.kt index a4b19c67d..b225fa872 100644 --- a/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/authentication/AuthenticationViewModelTest.kt @@ -32,8 +32,6 @@ class AuthenticationViewModelTest { private const val PASSWORD = "password" private const val AUD = "test_aud" private const val ID = "test_id" - private const val GOOGLE_ID_TOKEN = "test_token" - private const val RAW_NONCE = "test_nonce" } @Before @@ -200,43 +198,6 @@ class AuthenticationViewModelTest { assertNull(authenticationViewModel.authUserData.value) } - @Test - fun `signInWithGoogle success`() = runBlocking { - doAnswer { inv -> inv.getArgument<() -> Unit>(2)() } - .`when`(authModel) - .loginGoogle(any(), any(), any<() -> Unit>(), any<(Exception) -> Unit>()) - - authenticationViewModel.loginWithGoogle(GOOGLE_ID_TOKEN, RAW_NONCE) - - val result = - when (authenticationViewModel.userAuthenticationState.value) { - is UserAuthenticationState.Success -> true - else -> false - } - assert(result) - } - - @Test - fun `signInWithGoogle failure`() = runBlocking { - doAnswer { inv -> - val onFailure = inv.getArgument<(Exception) -> Unit>(3) - onFailure(Exception("sign in failure")) - } - .`when`(authModel) - .loginGoogle(any(), any(), any<() -> Unit>(), any<(Exception) -> Unit>()) - - authenticationViewModel.loginWithGoogle(GOOGLE_ID_TOKEN, RAW_NONCE) - - val result = - when (authenticationViewModel.userAuthenticationState.value) { - is UserAuthenticationState.Success -> false - is UserAuthenticationState.Error -> true - is UserAuthenticationState.Loading -> false - else -> false - } - assert(result) - } - @Test fun testGenerateHashCodeFormat() { val rawNonce = "testNonce" From afa51e5b374cc4f35973d21df02864cc60952816 Mon Sep 17 00:00:00 2001 From: Harrishan Date: Fri, 20 Dec 2024 02:35:53 +0100 Subject: [PATCH 3/3] fix: remove `C` tags and `string.xml` values for google auth --- .../main/java/com/android/periodpals/resources/C.kt | 2 -- app/src/main/res/values/strings.xml | 2 -- .../android/periodpals/ui/authentication/SignInTest.kt | 10 ---------- 3 files changed, 14 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/resources/C.kt b/app/src/main/java/com/android/periodpals/resources/C.kt index 5af7192c2..e0dc2a202 100644 --- a/app/src/main/java/com/android/periodpals/resources/C.kt +++ b/app/src/main/java/com/android/periodpals/resources/C.kt @@ -82,8 +82,6 @@ object C { const val SCREEN = "signInScreen" const val INSTRUCTION_TEXT = "instructionText" const val SIGN_IN_BUTTON = "signInButton" - const val CONTINUE_WITH_TEXT = "continueWith" - const val GOOGLE_BUTTON = "googleButton" const val NOT_REGISTERED_NAV_LINK = "notRegisteredButton" } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aa26de677..66ab4edbe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,8 +5,6 @@ Sign in to your account Sign in - Or continue with - Sign in with Google Not registered yet?\u00A0 Sign up here! diff --git a/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt b/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt index 6d4ad8f47..c024e1aab 100644 --- a/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt +++ b/app/src/test/java/com/android/periodpals/ui/authentication/SignInTest.kt @@ -129,16 +129,6 @@ class SignInScreenTest { .performScrollTo() .assertIsDisplayed() .assertTextEquals(getResourceString(R.string.sign_in_button_text)) - composeTestRule - .onNodeWithTag(SignInScreen.CONTINUE_WITH_TEXT) - .performScrollTo() - .assertIsDisplayed() - .assertTextEquals(getResourceString(R.string.sign_in_continue_with_text)) - composeTestRule - .onNodeWithTag(SignInScreen.GOOGLE_BUTTON) - .performScrollTo() - .assertIsDisplayed() - .assertTextEquals(getResourceString(R.string.sign_in_sign_up_with_google)) composeTestRule .onNodeWithTag(SignInScreen.NOT_REGISTERED_NAV_LINK) .performScrollTo()