Skip to content

Instantly share code, notes, and snippets.

@SamuelWakoli
Last active February 14, 2025 10:52
Show Gist options
  • Save SamuelWakoli/3235ba0a912d73b35e983e448f1ed84f to your computer and use it in GitHub Desktop.
Save SamuelWakoli/3235ba0a912d73b35e983e448f1ed84f to your computer and use it in GitHub Desktop.
A Firebase authentication manager for Android that integrates Google and email/password sign-in with credential management and password reset support.
package ...
// Dependencies:
// Google ID: com.google.android.libraries.identity.googleid
// Android Credentials: androidx.credentials
// Auth Play Services: androidx.credentials.play.services.auth
import android.content.Context
import android.util.Log
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CredentialManager
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import androidx.credentials.GetPasswordOption
import androidx.credentials.PasswordCredential
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.NoCredentialException
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
import com.google.firebase.auth.GoogleAuthProvider
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import com.kcauptti.auth.BuildConfig
import com.kcauptti.auth.domain.model.auth.SignInResult
import kotlinx.coroutines.tasks.await
class AuthManager {
private val auth = Firebase.auth
// Configuration for Google ID sign-in
private val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(BuildConfig.WEB_CLIENT_ID)
.setAutoSelectEnabled(true)
.setNonce(NonceGenerator.generateNonce())
.build()
private val googleCredentialRequest: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()
// Sign in with Google account
suspend fun signInWithGoogle(context: Context): SignInResult {
val credentialManager: CredentialManager = CredentialManager.create(context)
return try {
val result: GetCredentialResponse = credentialManager.getCredential(
request = googleCredentialRequest,
context = context
)
val credential = result.credential
val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
val idToken = googleIdTokenCredential.idToken
val googleCredentials = GoogleAuthProvider.getCredential(idToken, null)
val user = auth.signInWithCredential(googleCredentials).await().user
SignInResult(user = user, errorMessage = null)
} catch (e: NoCredentialException) {
Log.e(TAG, "No Google account found", e)
val customError = NoCredentialException(errorMessage = "No Google account found")
SignInResult(user = null, errorMessage = customError.message)
} catch (e: GetCredentialException) {
Log.e(TAG, "Google sign-in failed", e)
SignInResult(user = null, errorMessage = e.message)
}
}
// Handle sign-in button click (password or Google)
suspend fun onClickSignInButton(context: Context): SignInResult {
val credentialManager: CredentialManager = CredentialManager.create(context)
return try {
val multipleRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.addCredentialOption(GetPasswordOption())
.build()
val result: GetCredentialResponse = credentialManager.getCredential(
request = multipleRequest,
context = context
)
val credential = result.credential
when (credential) {
is PasswordCredential -> {
val user = auth.signInWithEmailAndPassword(
credential.id, credential.password
).await().user
SignInResult(user = user, errorMessage = null)
}
is GoogleIdTokenCredential -> {
val googleIdTokenCredential =
GoogleIdTokenCredential.createFrom(credential.data)
val idToken = googleIdTokenCredential.idToken
val googleCredentials = GoogleAuthProvider.getCredential(idToken, null)
val user = auth.signInWithCredential(googleCredentials).await().user
SignInResult(user = user, errorMessage = null)
}
else -> SignInResult(user = null, errorMessage = "Unknown credential type")
}
} catch (e: NoCredentialException) {
val message = "No saved Google or Password credential found on the device"
Log.e(TAG, message, e)
val customError = NoCredentialException(errorMessage = message)
SignInResult(user = null, errorMessage = customError.message)
} catch (e: GetCredentialException) {
Log.e(TAG, "Google sign-in failed", e)
SignInResult(user = null, errorMessage = e.message)
} catch (e: Exception) {
SignInResult(user = null, errorMessage = e.message)
}
}
// Sign in with email and password
suspend fun signInWithPassword(
context: Context,
email: String,
password: String
): SignInResult {
val credentialManager: CredentialManager = CredentialManager.create(context)
return try {
val user = auth.signInWithEmailAndPassword(email, password).await().user
val passwordRequest = CreatePasswordRequest(id = email, password = password)
try {
// Attempt to create a credential if possible, but allow sign-in even without it.
credentialManager.createCredential(context = context, request = passwordRequest)
} catch (_: Exception) {
// Do nothing. Possibly user has an older device.
}
SignInResult(user = user, errorMessage = null)
} catch (e: Exception) {
SignInResult(user = null, errorMessage = e.message)
}
}
// Register with email and password
suspend fun registerWithPassword(
context: Context,
email: String,
password: String
): SignInResult {
val credentialManager: CredentialManager = CredentialManager.create(context)
return try {
val user = auth.createUserWithEmailAndPassword(email, password).await().user
val passwordRequest = CreatePasswordRequest(id = email, password = password)
try {
credentialManager.createCredential(context = context, request = passwordRequest)
} catch (_: Exception) {
// Do nothing. Possibly user has an older device.
}
SignInResult(user = user, errorMessage = null)
} catch (e: Exception) {
SignInResult(user = null, errorMessage = e.message)
}
}
// Forgot password
suspend fun forgotPassword(
email: String,
onSuccess: () -> Unit,
onFailure: (String) -> Unit
) {
try {
auth.sendPasswordResetEmail(email).await()
onSuccess()
} catch (e: Exception) {
onFailure(e.message ?: "Unknown error")
}
}
companion object {
private const val TAG = "AuthManager"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment