DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Credential Manager Complete Guide — Passkeys/Password Management/Compose Integration

What You'll Learn

Credential Manager (passkeys, password save/retrieve, Google Sign-In integration, Compose UI integration) explained.


Setup

dependencies {
    implementation("androidx.credentials:credentials:1.5.0")
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0")
    implementation("com.google.android.libraries.identity.googleid:googleid:1.1.1")
}
Enter fullscreen mode Exit fullscreen mode

CredentialRepository

class CredentialRepository @Inject constructor(
    @ApplicationContext private val context: Context
) {
    private val credentialManager = CredentialManager.create(context)

    suspend fun signInWithSavedCredentials(activity: Activity): AuthResult {
        val request = GetCredentialRequest.Builder()
            .addCredentialOption(GetPasswordOption())
            .addCredentialOption(
                GetPublicKeyCredentialOption(
                    requestJson = createPasskeyRequestJson()
                )
            )
            .addCredentialOption(
                GetGoogleIdOption.Builder()
                    .setServerClientId(BuildConfig.GOOGLE_CLIENT_ID)
                    .setFilterByAuthorizedAccounts(false)
                    .build()
            )
            .build()

        return try {
            val result = credentialManager.getCredential(activity, request)
            handleCredential(result.credential)
        } catch (e: GetCredentialCancellationException) {
            AuthResult.Cancelled
        } catch (e: NoCredentialException) {
            AuthResult.NoCredentials
        } catch (e: Exception) {
            AuthResult.Error(e.message ?: "Unknown error")
        }
    }

    suspend fun savePassword(activity: Activity, email: String, password: String) {
        val credential = CreatePasswordRequest(email, password)
        credentialManager.createCredential(activity, credential)
    }

    private fun handleCredential(credential: Credential): AuthResult {
        return when (credential) {
            is PasswordCredential -> {
                AuthResult.Success(credential.id, credential.password)
            }
            is PublicKeyCredential -> {
                AuthResult.PasskeySuccess(credential.authenticationResponseJson)
            }
            is CustomCredential -> {
                if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
                    val googleId = GoogleIdTokenCredential.createFrom(credential.data)
                    AuthResult.GoogleSuccess(googleId.idToken)
                } else {
                    AuthResult.Error("Unsupported credential type")
                }
            }
            else -> AuthResult.Error("Unsupported credential")
        }
    }
}

sealed interface AuthResult {
    data class Success(val email: String, val password: String) : AuthResult
    data class PasskeySuccess(val responseJson: String) : AuthResult
    data class GoogleSuccess(val idToken: String) : AuthResult
    data object Cancelled : AuthResult
    data object NoCredentials : AuthResult
    data class Error(val message: String) : AuthResult
}
Enter fullscreen mode Exit fullscreen mode

Compose Screen

@Composable
fun LoginScreen(viewModel: LoginViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    val activity = LocalContext.current as Activity

    LaunchedEffect(Unit) {
        viewModel.signInWithSavedCredentials(activity)
    }

    Column(
        Modifier.fillMaxSize().padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        when (uiState) {
            LoginUiState.Loading -> CircularProgressIndicator()
            is LoginUiState.Error -> {
                Text((uiState as LoginUiState.Error).message, color = MaterialTheme.colorScheme.error)
                Spacer(Modifier.height(16.dp))
                Button(onClick = { viewModel.signInWithSavedCredentials(activity) }) {
                    Text("Sign In")
                }
            }
            LoginUiState.NeedInput -> {
                Button(
                    onClick = { viewModel.signInWithSavedCredentials(activity) },
                    modifier = Modifier.fillMaxWidth()
                ) { Text("Sign In with Saved Credentials") }
            }
            LoginUiState.Success -> {
                Text("Sign In Successful!", style = MaterialTheme.typography.headlineMedium)
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary

Feature Implementation
Password retrieve GetPasswordOption
Passkeys GetPublicKeyCredentialOption
Google Sign-In GetGoogleIdOption
Save password CreatePasswordRequest
  • CredentialManager unified auth management
  • Passkeys/password/Google Sign-In presented together
  • User chooses preferred auth method
  • Android 14+ system integration for best UX

Ready-Made Android App Templates

8 production-ready Android app templates with Jetpack Compose, MVVM, Hilt, and Material 3.

Browse templatesGumroad

Top comments (0)