Search code examples
androidkotlingoogle-signincredential-manager

How to get user email and check if user already signed in using Credential Manager for Google SignIn


I have successfully integrated CredentialManager on Android. Suppose the user sign in for first time , I get the required details like email. Now the user completely closes the app and once he reopens again how to check if the user has already signed in using credential manager as well as get the email id of the signed in user, please note that the user has not logged out and has only closed the app completely from background. Here is my code

class MainActivity : AppCompatActivity() {

    private lateinit var googleIdOption: GetGoogleIdOption

    private lateinit var loginBtn: Button
    private lateinit var textView: TextView
    private lateinit var logoutBtn: Button

   

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        loginBtn = findViewById(R.id.btn)
        textView = findViewById(R.id.textview)
        logoutBtn = findViewById(R.id.logout)

        val credentialManager = CredentialManager.create(this)

     

        logoutBtn.setOnClickListener {
           lifecycleScope.launch {
               userPreferencesDataStore.edit {
                   it.clear()
               }
               val request = ClearCredentialStateRequest()
               credentialManager.clearCredentialState(request)
           }
        }

        loginBtn.setOnClickListener {
            val rawNonce = UUID.randomUUID().toString()
            val bytes = rawNonce.toByteArray()
            val md = MessageDigest.getInstance("SHA-256")
            val digest = md.digest(bytes)
            val hashedNonce = digest.fold(""){str, it -> str + "%02x".format(it)}

            googleIdOption = GetGoogleIdOption.Builder()
                .setFilterByAuthorizedAccounts(false) //IMP Part
                .setServerClientId("myapikey.apps.googleusercontent.com")
                .setAutoSelectEnabled(true)
                .setNonce(hashedNonce)
                .build()

            val request: GetCredentialRequest = GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOption)
                .build()

            lifecycleScope.launch {
                try {
                    val result = credentialManager.getCredential(
                        request = request,
                        context = this@MainActivity,
                    )
                    handleSignIn(result)
                } catch (e: GetCredentialException) {
                    Log.e("Erroris", "Error getting credential", e)
                }
            }
        }

    }

    private fun handleSignIn(result: GetCredentialResponse) {
        // Handle the successfully returned credential.
        when (val credential = result.credential) {

            // Passkey credential
            is PublicKeyCredential -> {
                // Share responseJson such as a GetCredentialResponse on your server to
                // validate and authenticate
                val responseJson = credential.authenticationResponseJson
            }

            // Password credential
            is PasswordCredential -> {
                // Send ID and password to your server to validate and authenticate.
                val username = credential.id
                val password = credential.password
            }

            // GoogleIdToken credential
            is CustomCredential -> {
                if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
                    try {
                        // Use googleIdTokenCredential and extract id to validate and
                        // authenticate on your server.
                        val googleIdTokenCredential = GoogleIdTokenCredential
                            .createFrom(credential.data)
                        val googleIdToken = googleIdTokenCredential.idToken
                        Log.i("answer",googleIdToken)
                        val personId = googleIdTokenCredential.id
                        Log.i("answer",personId) //email
                        val displayName = googleIdTokenCredential.displayName
                        Log.i("answer",displayName.toString())
                        val personPhoto = googleIdTokenCredential.profilePictureUri
                        Log.i("answer",personPhoto.toString())
                       
                    } catch (e: GoogleIdTokenParsingException) {
                        Log.e("Erroris", "Received an invalid google id token response", e)
                    }
                } else {
                    // Catch any unrecognized custom credential type here.
                    Log.e("Erroris", "Unexpected type of credential")
                }
            }

            else -> {
                // Catch any unrecognized credential type here.
                Log.e("Erroris", "Unexpected type of credential")
            }
        }
    }

  

}

Solution

  • The recommendation, as stated here, is to always call the API with .setFilterByAuthorizedAccounts(true) (with also .setAutoSelectEnabled(true)) first and then if you get a response to the effect that there was no credential, then call the same method but use .setFilterByAuthorizedAccounts(false). When you call the api with .setFilterByAuthorizedAccounts(true), you are basically asking for only those accounts that have already been used to sign into your app (i.e. user has already given the consent to share some info for that account with your app). If such an account is there (and if there is only one such an account on that device), then the parameter set by setting .setAutoSelectEnabled(true) allows the flow to move forward without any user interaction and at the end, it returns an ID Token to you, from which you can extract the email of that account.