Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 98c995ed authored by Jonathan Klee's avatar Jonathan Klee
Browse files

fix: fix potential stale login for Google mode

parent 08302130
Loading
Loading
Loading
Loading
Loading
+3 −7
Original line number Diff line number Diff line
@@ -136,15 +136,11 @@ class PlayStoreAuthenticator @Inject constructor(
    }

    private suspend fun loginWithGoogleBackend(): ResultSupreme<AuthData?> {
        val aasToken = playStoreAuthStore.awaitAasToken()
        return if (aasToken.isNotBlank()) {
            loginWithAasToken()
        } else {
            loginWithAasTokenConversion()
        }
        val storedAasToken = playStoreAuthStore.awaitAasToken()
        if (storedAasToken.isNotBlank()) {
            return loginWithAasToken()
        }

    private suspend fun loginWithAasTokenConversion(): ResultSupreme<AuthData?> {
        val email = playStoreAuthStore.awaitEmail()
        val oauthToken = playStoreAuthStore.awaitOauthToken()
        val aasTokenResponse = aasTokenConverter.convert(email, oauthToken)
+129 −2
Original line number Diff line number Diff line
@@ -25,6 +25,10 @@ import kotlinx.serialization.json.Json
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.mockito.kotlin.verify as mockitoVerify
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

@@ -144,16 +148,18 @@ class PlayStoreAuthenticatorTest {
        val sessionRepository = FakeSessionRepository(
            initialUser = User.GOOGLE,
            playStoreAuthSource = PlayStoreAuthSource.GOOGLE,
            aasToken = "aas-token"
            aasToken = "",
            oauthToken = "oauth-token",
        )
        val googleLoginManager = mockk<GoogleLoginManager>()
        val microgLoginManager = mockk<MicrogLoginManager>()
        val anonymousLoginManager = mockk<AnonymousLoginManager>()
        val aasTokenConverter = mockk<OauthToAasTokenConverter>()
        val aasTokenConverter = mock<OauthToAasTokenConverter>()
        val userProfileFetcher = mockk<UserProfileFetcher>()

        val authData = AuthData(email = "user@gmail.com")
        val profile = UserProfile(name = "Test User", email = authData.email)
        whenever(aasTokenConverter.convert(any(), any())).thenReturn(ResultSupreme.Success("fresh-aas-token"))
        coEvery { googleLoginManager.login() } returns authData
        coEvery { userProfileFetcher.fetch(authData) } returns profile

@@ -176,10 +182,131 @@ class PlayStoreAuthenticatorTest {
        assertThat(authObject.result.data?.email).isEqualTo(authData.email)
        assertThat(authObject.result.data?.userProfile).isEqualTo(profile)
        assertThat(authObject.result.data?.locale).isEqualTo(context.resources.configuration.locales[0])
        mockitoVerify(aasTokenConverter).convert(any(), any())
        coVerify(exactly = 1) { googleLoginManager.login() }
        coVerify(exactly = 1) { userProfileFetcher.fetch(authData) }
    }

    @Test
    fun login_refreshesAasTokenViaConversionOnGoogleLogin() = runTest {
        val context = applicationContext()
        val sessionRepository = FakeSessionRepository(
            initialUser = User.GOOGLE,
            playStoreAuthSource = PlayStoreAuthSource.GOOGLE,
            aasToken = "",
            oauthToken = "oauth-token",
        )
        val googleLoginManager = mockk<GoogleLoginManager>()
        val aasTokenConverter = mock<OauthToAasTokenConverter>()
        val userProfileFetcher = mockk<UserProfileFetcher>()

        val authData = AuthData(email = "user@gmail.com")
        whenever(aasTokenConverter.convert(any(), any())).thenReturn(ResultSupreme.Success("fresh-aas-token"))
        coEvery { googleLoginManager.login() } returns authData
        coEvery { userProfileFetcher.fetch(any()) } returns null

        val authenticator = PlayStoreAuthenticator(
            context = context,
            sessionRepository = sessionRepository,
            playStoreAuthStore = sessionRepository,
            appPreferencesRepository = FakeAppPreferencesRepository(),
            authDataCache = AuthDataCache(sessionRepository, json),
            googleLoginManager = googleLoginManager,
            microgLoginManager = mockk(),
            anonymousLoginManager = mockk(),
            aasTokenConverter = aasTokenConverter,
            userProfileFetcher = userProfileFetcher
        )

        authenticator.login()

        mockitoVerify(aasTokenConverter).convert(any(), any())
        coVerify(exactly = 1) { googleLoginManager.login() }
        assertThat(sessionRepository.awaitAasToken()).isEqualTo("fresh-aas-token")
    }

    @Test
    fun login_usesStoredAasTokenWithoutConversionWhenAvailable() = runTest {
        val context = applicationContext()
        val sessionRepository = FakeSessionRepository(
            initialUser = User.GOOGLE,
            playStoreAuthSource = PlayStoreAuthSource.GOOGLE,
            aasToken = "stored-aas-token",
            oauthToken = "expired-oauth-token",
        )
        val googleLoginManager = mockk<GoogleLoginManager>()
        val aasTokenConverter = mock<OauthToAasTokenConverter>()
        val userProfileFetcher = mockk<UserProfileFetcher>()

        val authData = AuthData(email = "user@gmail.com")
        whenever(aasTokenConverter.convert(any(), any())).thenReturn(ResultSupreme.Error("conversion failed"))
        coEvery { googleLoginManager.login() } returns authData
        coEvery { userProfileFetcher.fetch(any()) } returns null

        val authenticator = PlayStoreAuthenticator(
            context = context,
            sessionRepository = sessionRepository,
            playStoreAuthStore = sessionRepository,
            appPreferencesRepository = FakeAppPreferencesRepository(),
            authDataCache = AuthDataCache(sessionRepository, json),
            googleLoginManager = googleLoginManager,
            microgLoginManager = mockk(),
            anonymousLoginManager = mockk(),
            aasTokenConverter = aasTokenConverter,
            userProfileFetcher = userProfileFetcher
        )

        val result = authenticator.login()
        val authObject = result.authObject as AuthObject.GPlayAuth

        assertThat(authObject.result.data?.email).isEqualTo(authData.email)
        mockitoVerify(aasTokenConverter, org.mockito.kotlin.never()).convert(any(), any())
        coVerify(exactly = 1) { googleLoginManager.login() }
        // AAS token in storage must not be overwritten when conversion failed
        assertThat(sessionRepository.awaitAasToken()).isEqualTo("stored-aas-token")
    }

    @Test
    fun login_fallsBackToOauthTokenWhenConversionFailsAndNoStoredAasToken() = runTest {
        val context = applicationContext()
        val sessionRepository = FakeSessionRepository(
            initialUser = User.GOOGLE,
            playStoreAuthSource = PlayStoreAuthSource.GOOGLE,
            aasToken = "",
            oauthToken = "oauth-token",
        )
        val googleLoginManager = mockk<GoogleLoginManager>()
        val aasTokenConverter = mock<OauthToAasTokenConverter>()
        val userProfileFetcher = mockk<UserProfileFetcher>()

        val authData = AuthData(email = "user@gmail.com")
        whenever(aasTokenConverter.convert(any(), any())).thenReturn(ResultSupreme.Error("conversion failed"))
        coEvery { googleLoginManager.loginWithOauthToken("oauth-token") } returns authData
        coEvery { userProfileFetcher.fetch(any()) } returns null

        val authenticator = PlayStoreAuthenticator(
            context = context,
            sessionRepository = sessionRepository,
            playStoreAuthStore = sessionRepository,
            appPreferencesRepository = FakeAppPreferencesRepository(),
            authDataCache = AuthDataCache(sessionRepository, json),
            googleLoginManager = googleLoginManager,
            microgLoginManager = mockk(),
            anonymousLoginManager = mockk(),
            aasTokenConverter = aasTokenConverter,
            userProfileFetcher = userProfileFetcher
        )

        val result = authenticator.login()
        val authObject = result.authObject as AuthObject.GPlayAuth

        assertThat(authObject.result.data?.email).isEqualTo(authData.email)
        mockitoVerify(aasTokenConverter).convert(any(), any())
        coVerify(exactly = 1) { googleLoginManager.loginWithOauthToken("oauth-token") }
        coVerify(exactly = 0) { googleLoginManager.login() }
        assertThat(sessionRepository.awaitAasToken()).isEmpty()
    }

    private class FakeSessionRepository(
        initialUser: User,
        currentAuthData: AuthData? = null,