diff --git a/CredentialManager/app/build.gradle b/CredentialManager/app/build.gradle index ebddd33..bb498a4 100644 --- a/CredentialManager/app/build.gradle +++ b/CredentialManager/app/build.gradle @@ -32,10 +32,11 @@ keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) android { namespace 'com.google.credentialmanager.sample' compileSdk 34 + compileSdkPreview "VanillaIceCream" defaultConfig { applicationId "com.google.credentialmanager.sample" minSdk 21 - targetSdk 34 + targetSdkPreview "VanillaIceCream" versionCode 1 versionName "1.0" @@ -100,8 +101,8 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation 'androidx.credentials:credentials-play-services-auth:1.3.0-alpha01' - implementation 'androidx.credentials:credentials:1.3.0-alpha01' + implementation 'androidx.credentials:credentials-play-services-auth:1.5.0-alpha02' + implementation 'androidx.credentials:credentials:1.5.0-alpha02' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" diff --git a/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignInFragment.kt b/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignInFragment.kt index 494f7c4..09277b0 100644 --- a/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignInFragment.kt +++ b/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignInFragment.kt @@ -28,7 +28,9 @@ import androidx.credentials.GetCredentialRequest import androidx.credentials.GetPasswordOption import androidx.credentials.GetPublicKeyCredentialOption import androidx.credentials.PasswordCredential +import androidx.credentials.PendingGetCredentialRequest import androidx.credentials.PublicKeyCredential +import androidx.credentials.pendingGetCredentialRequest import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import com.google.credentialmanager.sample.databinding.FragmentSignInBinding @@ -64,27 +66,67 @@ class SignInFragment : Fragment() { credentialManager = CredentialManager.create(requireActivity()) - binding.signInWithSavedCredentials.setOnClickListener(signInWithSavedCredentials()) + val getCredentialRequest = configureGetCredentialRequest() + + configureAutofill(getCredentialRequest) + + binding.signInWithSavedCredentials.setOnClickListener( + signInWithSavedCredentials( + getCredentialRequest + ) + ) + } + + private fun configureAutofill(getCredentialRequest: GetCredentialRequest) { + binding.textUsername + .pendingGetCredentialRequest = PendingGetCredentialRequest( + getCredentialRequest + ) { response -> + if (response.credential is PublicKeyCredential) { + DataProvider.setSignedInThroughPasskeys(true) + } + if (response.credential is PasswordCredential) { + DataProvider.setSignedInThroughPasskeys(false) + } + showHome() + } + } + + private fun configureGetCredentialRequest(): GetCredentialRequest { + val getPublicKeyCredentialOption = + GetPublicKeyCredentialOption(fetchAuthJsonFromServer(), null) + val getPasswordOption = GetPasswordOption() + val getCredentialRequest = GetCredentialRequest( + listOf( + getPublicKeyCredentialOption, + getPasswordOption + ) + ) + return getCredentialRequest } - private fun signInWithSavedCredentials(): View.OnClickListener { + private fun signInWithSavedCredentials(getCredentialRequest: GetCredentialRequest): View.OnClickListener { return View.OnClickListener { lifecycleScope.launch { configureViews(View.VISIBLE, false) - val data = getSavedCredentials() + val data = getSavedCredentials(getCredentialRequest) configureViews(View.INVISIBLE, true) data?.let { - sendSignInResponseToServer() - listener.showHome() + showHome() } } } } + private fun showHome() { + sendSignInResponseToServer() + listener.showHome() + } + private fun configureViews(visibility: Int, flag: Boolean) { configureProgress(visibility) binding.signInWithSavedCredentials.isEnabled = flag @@ -103,19 +145,12 @@ class SignInFragment : Fragment() { return true } - private suspend fun getSavedCredentials(): String? { - val getPublicKeyCredentialOption = - GetPublicKeyCredentialOption(fetchAuthJsonFromServer(), null) - val getPasswordOption = GetPasswordOption() + private suspend fun getSavedCredentials(getCredentialRequest: GetCredentialRequest): String? { + val result = try { credentialManager.getCredential( requireActivity(), - GetCredentialRequest( - listOf( - getPublicKeyCredentialOption, - getPasswordOption - ) - ) + getCredentialRequest, ) } catch (e: Exception) { configureViews(View.INVISIBLE, true) diff --git a/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignUpFragment.kt b/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignUpFragment.kt index 36119a6..93974c1 100644 --- a/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignUpFragment.kt +++ b/CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignUpFragment.kt @@ -215,23 +215,28 @@ class SignUpFragment : Fragment() { // WebAuthn spec using e.domError "An error occurred while creating a passkey, please check logs for additional details." } + is CreateCredentialCancellationException -> { // The user intentionally canceled the operation and chose not // to register the credential. "The user intentionally canceled the operation and chose not to register the credential. Check logs for additional details." } + is CreateCredentialInterruptedException -> { // Retry-able error. Consider retrying the call. "The operation was interrupted, please retry the call. Check logs for additional details." } + is CreateCredentialProviderConfigurationException -> { // Your app is missing the provider configuration dependency. // Most likely, you're missing "credentials-play-services-auth". "Your app is missing the provider configuration dependency. Check logs for additional details." } + is CreateCredentialUnknownException -> { "An unknown error occurred while creating passkey. Check logs for additional details." } + is CreateCredentialCustomException -> { // You have encountered an error from a 3rd-party SDK. If you // make the API call with a request object that's a subclass of @@ -241,6 +246,7 @@ class SignUpFragment : Fragment() { // exception. "An unknown error occurred from a 3rd party SDK. Check logs for additional details." } + else -> { Log.w("Auth", "Unexpected exception type ${e::class.java.name}") "An unknown error occurred." diff --git a/CredentialManager/app/src/main/res/layout/fragment_sign_in.xml b/CredentialManager/app/src/main/res/layout/fragment_sign_in.xml index 289eb17..1569af4 100644 --- a/CredentialManager/app/src/main/res/layout/fragment_sign_in.xml +++ b/CredentialManager/app/src/main/res/layout/fragment_sign_in.xml @@ -28,7 +28,7 @@ android:layout_marginTop="20dp" android:gravity="center" android:padding="16dp" - android:text="Sign in" + android:text="@string/sign_in" android:textSize="24sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" @@ -39,7 +39,7 @@ android:id="@+id/circularProgressIndicator" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="10dp" + android:layout_marginTop="20dp" android:indeterminate="true" android:visibility="invisible" app:layout_constraintCircleRadius="0dp" @@ -55,8 +55,8 @@ android:id="@+id/textProgress" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="10dp" - android:text="operation in progress... " + android:layout_marginBottom="16dp" + android:text="@string/operation_in_progress" android:visibility="invisible" app:layout_constraintBottom_toBottomOf="@+id/circularProgressIndicator" app:layout_constraintStart_toEndOf="@+id/circularProgressIndicator" @@ -66,11 +66,21 @@ android:id="@+id/sign_in_with_savedCredentials" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:text="Sign in with passkey/saved password" + android:layout_marginTop="40dp" + android:text="@string/sign_in_with_passkey_saved_password" android:textAllCaps="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/circularProgressIndicator" /> + + diff --git a/CredentialManager/app/src/main/res/values/strings.xml b/CredentialManager/app/src/main/res/values/strings.xml index f9dbd19..eb63159 100644 --- a/CredentialManager/app/src/main/res/values/strings.xml +++ b/CredentialManager/app/src/main/res/values/strings.xml @@ -25,4 +25,8 @@ Hello first fragment Hello second fragment. Arg: %1$s + Sign in + operation in progress... + Sign in with passkey/saved password + Autofill saved credentials