Sign in your user with Credential Manager

Credential Manager is a Jetpack API that supports multiple sign-in methods, such as username and password, passkeys, and federated sign-in solutions (such as Sign-in with Google) in a single API, thus simplifying the integration for developers.

Furthermore, for users, Credential Manager unifies the sign-in interface across authentication methods, making it clearer and easier for users to sign into apps, regardless of the method they choose.

This page explains the concept of passkeys and the steps to implementing client-side support for authentication solutions, including passkeys, using the Credential Manager API. There is also a separate FAQ page that provides answers to more detailed, specific questions.

Your feedback is a crucial part of improving the Credential Manager API. Share any issues you find or ideas for improving the API using the following link:

Give feedback

About passkeys

Passkeys are a safer and easier replacement for passwords. With passkeys, users can sign in to apps and websites using a biometric sensor (such as a fingerprint or facial recognition), PIN, or pattern. This provides a seamless sign-in experience, freeing your users from having to remember usernames or passwords.

Passkeys rely on WebAuthn (Web Authentication), a standard jointly developed by the FIDO Alliance and the World Wide Web Consortium (W3C). WebAuthn uses public-key cryptography to authenticate the user. The website or app that the user is signing into can see and store the public key, but never the private key. The private key is kept secret and safe. And because the key is unique and tied to the website or app, passkeys are un-phishable, adding further security.

Credential Manager allows users to create passkeys and store them in Google Password Manager.

Prerequisites

To use Credential Manager, complete the steps in this section.

Use a recent platform version

Credential Manager is supported on Android 4.4 (API level 19) and higher.

Add dependencies to your app

Add the following dependencies to your app module's build script:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.3.0-alpha01")

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation("androidx.credentials:credentials-play-services-auth:1.3.0-alpha01")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.3.0-alpha01"

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation "androidx.credentials:credentials-play-services-auth:1.3.0-alpha01"
}

Preserve classes in ProGuard file

In your module's proguard-rules.pro file, add the following directives:

-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
  *;
}

Learn more about how to shrink, obfuscate, and optimize your app.

Add support for Digital Asset Links

To enable passkey support for your Android app, associate your app with a website that your app owns. You can declare this association by completing the following steps:

  1. Create a Digital Asset Links JSON file. For example, to declare that the website https://signin.example.com and an Android app with the package name com.example can share sign-in credentials, create a file named assetlinks.json with the following content:

    [
      {
        "relation" : [
          "delegate_permission/common.handle_all_urls",
          "delegate_permission/common.get_login_creds"
        ],
        "target" : {
          "namespace" : "android_app",
          "package_name" : "com.example.android",
          "sha256_cert_fingerprints" : [
            SHA_HEX_VALUE
          ]
        }
      }
    ]
    

    The relation field is an array of one or more strings that describe the relationship being declared. To declare that apps and sites share sign-in credentials, specify the relationships as delegate_permission/handle_all_urls and delegate_permission/common.get_login_creds.

    The target field is an object that specifies the asset the declaration applies to. The following fields identify a website:

    namespace web
    site

    The website's URL, in the format https://domain[:optional_port]; for example, https://www.example.com.

    The domain must be fully-qualified., and optional_port must be omitted when using port 443 for HTTPS.

    A site target can only be a root domain: you cannot limit an app association to a specific subdirectory. Don't include a path in the URL, such as a trailing slash.

    Subdomains are not considered to match: that is, if you specify the domain as www.example.com, the domain www.counter.example.com is not associated with your app.

    The following fields identify an Android app:

    namespace android_app
    package_name The package name declared in the app's manifest. For example, com.example.android
    sha256_cert_fingerprints The SHA256 fingerprints of your app's signing certificate.
  2. Host the Digital Assets Link JSON file at the following location on the sign-in domain:

    https://domain[:optional_port]/.well-known/assetlinks.json
    

    For example, if your sign-in domain is signin.example.com, host the JSON file at https://signin.example.com/.well-known/assetlinks.json.

    The MIME type for the Digital Assets Link file needs to be JSON. Make sure the server sends a Content-Type: application/json header in the response.

  3. Ensure that your host permits Google to retrieve your Digital Asset Link file. If you have a robots.txt file, it must allow the Googlebot agent to retrieve /.well-known/assetlinks.json. Most sites can allow any automated agent to retrieve files in the /.well-known/ path so that other services can access the metadata in those files:

    User-agent: *
    Allow: /.well-known/
    
  4. Add the following line to the manifest file under <application>:

    <meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
    
  5. If you are using password sign-in through Credential Manager, follow this step to configure digital asset linking in manifest. This step is not required if you are only using passkeys.

    Declare the association in the Android app. Add an object that specifies the assetlinks.json files to load. You must escape any apostrophes and quotation marks you use in the string. For example:

    <string name="asset_statements" translatable="false">
    [{
      \"include\": \"https://signin.example.com/.well-known/assetlinks.json\"
    }]
    </string>
    
    > GET /.well-known/assetlinks.json HTTP/1.1
    > User-Agent: curl/7.35.0
    > Host: signin.example.com
    
    < HTTP/1.1 200 OK
    < Content-Type: application/json
    

Configure the Credential Manager

To configure and initialize a CredentialManager object, add logic similar to the following:

Kotlin

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
val credentialManager = CredentialManager.create(context)

Java

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
CredentialManager credentialManager = CredentialManager.create(context)

Indicate credential fields

On Android 14 and higher, the isCredential attribute can be used to indicate credential fields, such as username or password fields. This attribute indicates that this view is a credential field that is intended to work with Credential Manager and third party credential providers, while helping autofill services provide better autofill suggestions. When the app uses the Credential Manager API, the Credential Manager bottom sheet with available credentials is displayed and there's no further need to show autofill's fill dialog for username or password.

To use the isCredential attribute, add it to the relevant Views:

<TextView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:isCredential="true"
...
 />

Sign in your user

To retrieve all the passkey and password options that are associated with the user's account, complete these steps:

  1. Initialize the password and passkey authentication options:

    Kotlin

    // Retrieves the user's saved password for your app from their
    // password provider.
    val getPasswordOption = GetPasswordOption()
    
    // Get passkey from the user's public key credential provider.
    val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
        requestJson = requestJson
    )

    Java

    // Retrieves the user's saved password for your app from their
    // password provider.
    GetPasswordOption getPasswordOption = new GetPasswordOption();
    
    // Get passkey from the user's public key credential provider.
    GetPublicKeyCredentialOption getPublicKeyCredentialOption =
            new GetPublicKeyCredentialOption(requestJson);
  2. Use the options retrieved from the previous step to build the sign-in request.

    Kotlin

    val getCredRequest = GetCredentialRequest(
        listOf(getPasswordOption, getPublicKeyCredentialOption)
    )

    Java

    GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder()
        .addCredentialOption(getPasswordOption)
        .addCredentialOption(getPublicKeyCredentialOption)
        .build();
  3. Launch the sign-in flow:

    Kotlin

    coroutineScope.launch {
        try {
            val result = credentialManager.getCredential(
                // Use an activity-based context to avoid undefined system UI
                // launching behavior.
                context = activityContext,
                request = getCredRequest
            )
            handleSignIn(result)
        } catch (e : GetCredentialException) {
            handleFailure(e)
        }
    }
    
    fun handleSignIn(result: GetCredentialResponse) {
        // Handle the successfully returned credential.
        val credential = result.credential
    
        when (credential) {
            is PublicKeyCredential -> {
                val responseJson = credential.authenticationResponseJson
                // Share responseJson i.e. a GetCredentialResponse on your server to
                // validate and  authenticate
            }
            is PasswordCredential -> {
                val username = credential.id
                val password = credential.password
                // Use id and password to send to your server to validate
                // and authenticate
            }
          is CustomCredential -> {
              // If you are also using any external sign-in libraries, parse them
              // here with the utility functions provided.
              if (credential.type == ExampleCustomCredential.TYPE)  {
              try {
                  val ExampleCustomCredential = ExampleCustomCredential.createFrom(credential.data)
                  // Extract the required credentials and complete the authentication as per
                  // the federated sign in or any external sign in library flow
                  } catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
                      // Unlikely to happen. If it does, you likely need to update the dependency
                      // version of your external sign-in library.
                      Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
                  }
              } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential")
              }
            } else -> {
                // Catch any unrecognized credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }
    }

    Java

    credentialManager.getCredentialAsync(
        // Use activity based context to avoid undefined
        // system UI launching behavior
        activity,
        getCredRequest,
        cancellationSignal,
        <executor>,
        new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
            @Override
            public void onResult(GetCredentialResponse result) {
                handleSignIn(result);
            }
    
            @Override
            public void onError(GetCredentialException e) {
                handleFailure(e);
            }
        }
    );
    
    public void handleSignIn(GetCredentialResponse result) {
        // Handle the successfully returned credential.
        Credential credential = result.getCredential();
        if (credential instanceof PublicKeyCredential) {
            String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
            // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
        } else if (credential instanceof PasswordCredential) {
            String username = ((PasswordCredential) credential).getId();
            String password = ((PasswordCredential) credential).getPassword();
            // Use id and password to send to your server to validate and authenticate
        } else if (credential instanceof CustomCredential) {
            if (ExampleCustomCredential.TYPE.equals(credential.getType())) {
                try {
                    ExampleCustomCredential customCred = ExampleCustomCredential.createFrom(customCredential.getData());
                    // Extract the required credentials and complete the
                    // authentication as per the federated sign in or any external
                    // sign in library flow
                } catch (ExampleCustomCredential.ExampleCustomCredentialParsingException e) {
                    // Unlikely to happen. If it does, you likely need to update the
                    // dependency version of your external sign-in library.
                    Log.e(TAG, "Failed to parse an ExampleCustomCredential", e);
                }
            } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential");
            }
        } else {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential");
        }
    }

The following example shows how to format the JSON request when you get a passkey:

{
  "challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
  "allowCredentials": [],
  "timeout": 1800000,
  "userVerification": "required",
  "rpId": "credential-manager-app-test.glitch.me"
}

The following example shows how a JSON response might look after you get a public key credential:

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
    "signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
    "userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
  }
}

Handle exceptions when no credentials are available

In some cases, the user may not have any credentials available, or the user may not grant consent to using an available credential. If getCredential() is invoked and no credentials are not found, a NoCredentialException is returned. If this happens, your code should handle the NoCredentialException instances.

Kotlin

try {
  val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
  Log.e("CredentialManager", "No credential available", e)
}

Java

try {
  Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
  Log.e("CredentialManager", "No credential available", e);
}

On Android 14 or higher, you can reduce latency when showing the account selector by using the prepareGetCredential() method before calling getCredential().

Kotlin

val response = credentialManager.prepareGetCredential(
  GetCredentialRequest(
    listOf(
      <getPublicKeyCredentialOption>,
      <getPasswordOption>
    )
  )
}

Java

GetCredentialResponse response = credentialManager.prepareGetCredential(
  new GetCredentialRequest(
    Arrays.asList(
      new PublicKeyCredentialOption(),
      new PasswordOption()
    )
  )
);

The prepareGetCredential() method doesn't invoke UI elements. It only helps you perform the preparation work so that you can later launch the remaining get-credential operation (which involves UIs) through the getCredential() API.

The cached data is returned in a PrepareGetCredentialResponse object. If there are existing credentials, the results will be cached and you can then later launch the remaining getCredential() API to bring up the account selector with the cached data.

Registration flows

You can register a user for authentication using either a passkey or a password.

Create a passkey

To give users the choice to enroll a passkey and use it for reauthentication, register a user credential using a CreatePublicKeyCredentialRequest object.

Kotlin

fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) {
    val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
        // Contains the request in JSON format. Uses the standard WebAuthn
        // web JSON spec.
        requestJson = requestJson,
        // Defines whether you prefer to use only immediately available
        // credentials, not hybrid credentials, to fulfill this request.
        // This value is false by default.
        preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
    )

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    coroutineScope.launch {
        try {
            val result = credentialManager.createCredential(
                // Use an activity-based context to avoid undefined system
                // UI launching behavior
                context = activityContext,
                request = createPublicKeyCredentialRequest,
            )
            handlePasskeyRegistrationResult(result)
        } catch (e : CreateCredentialException){
            handleFailure(e)
        }
    }
}

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
            handlePasskeyError(e.domError)
        }
        is CreateCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to register the credential.
        }
        is CreateCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is CreateCredentialProviderConfigurationException -> {
            // Your app is missing the provider configuration dependency.
            // Most likely, you're missing the
            // "credentials-play-services-auth" module.
        }
        is CreateCredentialUnknownException -> ...
        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
            // CreateCustomCredentialRequest using a 3rd-party SDK, then you
            // should check for any custom exception type constants within
            // that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
    }
}

Java

public void createPasskey(String requestJson, boolean preferImmediatelyAvailableCredentials) {
    CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
            // `requestJson` contains the request in JSON format. Uses the standard
            // WebAuthn web JSON spec.
            // `preferImmediatelyAvailableCredentials` defines whether you prefer
            // to only use immediately available credentials, not  hybrid credentials,
            // to fulfill this request. This value is false by default.
            new CreatePublicKeyCredentialRequest(
                requestJson, preferImmediatelyAvailableCredentials);

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined system
        // UI launching behavior
        requireActivity(),
        createPublicKeyCredentialRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleSuccessfulCreatePasskeyResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                if (e instanceof CreatePublicKeyCredentialDomException) {
                    // Handle the passkey DOM errors thrown according to the
                    // WebAuthn spec.
                    handlePasskeyError(((CreatePublicKeyCredentialDomException)e).getDomError());
                } else if (e instanceof CreateCredentialCancellationException) {
                    // The user intentionally canceled the operation and chose not
                    // to register the credential.
                } else if (e instanceof CreateCredentialInterruptedException) {
                    // Retry-able error. Consider retrying the call.
                } else if (e instanceof CreateCredentialProviderConfigurationException) {
                    // Your app is missing the provider configuration dependency.
                    // Most likely, you're missing the
                    // "credentials-play-services-auth" module.
                } else if (e instanceof CreateCredentialUnknownException) {
                } else if (e instanceof 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
                    // CreateCustomCredentialRequest using a 3rd-party SDK,
                    // then you should check for any custom exception type
                    // constants within that SDK to match with e.type.
                    // Otherwise, drop or log the exception.
                } else {
                  Log.w(TAG, "Unexpected exception type "
                          + e.getClass().getName());
                }
            }
        }
    );
}

Format the JSON request

After you create a passkey, you must associate it with a user's account, and store the passkey's public key on your server. The following code example shows an how to format the JSON request when you create a passkey.

This blog post about bringing seamless authentication to your apps shows you how to format your JSON request when you create passkeys and when you authenticate using passkeys. It also explains why passwords aren't an effective authentication solution, how to leverage existing biometric credentials, how to associate your app with a website that you own, how to create passkeys, and how to authenticate using passkeys.

{
  "challenge": "abc123",
  "rp": {
    "name": "Credential Manager example",
    "id": "credential-manager-test.example.com"
  },
  "user": {
    "id": "def456",
    "name": "helloandroid@gmail.com",
    "displayName": "helloandroid@gmail.com"
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [
    {"id": "ghi789", "type": "public-key"},
    {"id": "jkl012", "type": "public-key"}
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

Set values for authenticatorAttachment

The authenticatorAttachment parameter can only be set at credential creation time. You can specify platform, cross-platform, or no value. In most cases, no value is recommended.

  • platform: To register the user's current device or prompt a password user to upgrade to passkeys after a sign-in, set authenticatorAttachment to platform.
  • cross-platform: This value is commonly used when enrolling multi-factor credentials and is not used in a passkey context.
  • No value: To provide users with the flexibility to create passkeys on their preferred devices (such as in account settings), the authenticatorAttachment parameter shouldn't be specified when a user chooses to add a passkey. In most cases, leaving the parameter unspecified is the best option.

Prevent the creation of duplicate passkeys

List credential IDs in the optional excludeCredentials array to prevent the creation of a new passkey if one already exists with the same passkey provider.

Handle the JSON response

The following code snippet shows an example JSON response for creating a public key credential. Learn more about how to handle the returned public key credential.

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
  }
}

Verify origin from client data JSON

The origin represents the application or website that a request comes from, and is used by passkeys to protect against phishing attacks. Your app's server is required to check the client data origin against an allowlist of approved apps and websites. If the server receives a request from an app or website from an unrecognized origin, the request should be rejected.

In the Web case, origin reflects the same-site origin where the credential was signed in. For example, given a URL of https://www.example.com:8443/store?category=shoes#athletic , the origin is https://www.example.com:8443.

For Android apps, the user agent automatically sets origin to the signature of the calling app. This signature should be verified as a match on your server to validate the caller of the passkey API. The Android origin is a URI derived from the SHA-256 hash of the APK signing certificate, such as:

android:apk-key-hash:<sha256_hash-of-apk-signing-cert>

The SHA-256 hashes of the signing certificates from a keystore can be found by running the following terminal command:

keytool -list -keystore <path-to-apk-signing-keystore>

The SHA-256 hashes are in a colon-delimited hexadecimal format (91:F7:CB:F9:D6:81…), and the Android origin values are base64url-encoded. This Python example demonstrates how to convert the hash format to a compatible, colon-separated hexadecimal format:

import binascii
import base64
fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5'
print("android:apk-key-hash:" + base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))

Replace the value of fingerprint with your own value. Here is an example result:

android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU

You can then match that string as an allowed origin on your server. If you have multiple signing certificates, such as certificates for debugging and release, or multiple apps, then repeat the process and accept all those origins as valid on the server.

Save a user's password

If the user provides a username and password for an authentication flow in your app, you can register a user credential that can be used to authenticate the user. To do so, create a CreatePasswordRequest object:

Kotlin

fun registerPassword(username: String, password: String) {
    // Initialize a CreatePasswordRequest object.
    val createPasswordRequest =
            CreatePasswordRequest(id = username, password = password)

    // Create credential and handle result.
    coroutineScope.launch {
        try {
            val result =
                credentialManager.createCredential(
                    // Use an activity based context to avoid undefined
                    // system UI launching behavior.
                    activityContext,
                    createPasswordRequest
                  )
            handleRegisterPasswordResult(result)
        } catch (e: CreateCredentialException) {
            handleFailure(e)
        }
    }
}

Java

void registerPassword(String username, String password) {
    // Initialize a CreatePasswordRequest object.
    CreatePasswordRequest createPasswordRequest =
        new CreatePasswordRequest(username, password);

    // Register the username and password.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined
        // system UI launching behavior
        requireActivity(),
        createPasswordRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                handleFailure(e);
            }
        }
    );
}

Support credential recovery

If a user no longer has access to a device where they had stored their credentials, they might need to recover from a secure online backup. To learn more about how to support this credential recovery process, read the section titled "Recovering access or adding new devices" in this blog post: Security of Passkeys in the Google Password Manager.

Add support for password management tools with passkey endpoints well-known URLs

For seamless integration and future compatibility with password and credential management tools, we recommend adding support for passkey endpoints well-known URLs. This is an open protocol for aligned parties to formally advertise their support for passkeys and provide direct links for passkey enrollment and management.

  1. For a relying party at https://example.com, which has a website plus Android and iOS apps, the well-known URL would be https://example.com/.well-known/passkey-endpoints.
  2. When the URL is queried, the response should use the following schema

    {
      "enroll": "https://example.com/account/manage/passkeys/create"
      "manage": "https://example.com/account/manage/passkeys"
    }
    
  3. To have this link open directly in your app instead of on the web, use Android app links.

  4. Further details can be found in the passkey endpoints well-known URL explainer on GitHub.

Troubleshoot common errors

The following table shows several common error codes and descriptions, and provides some information about their causes:

Error code and description Cause
On Begin Sign In Failure: 16: Caller has been temporarily blocked due to too many canceled sign-in prompts.

If you encounter this 24-hour cooldown period during development, you can reset it by clearing Google Play services' app storage.

Alternatively, to toggle this cooldown on a test device or emulator, go to the Dialer app and input the following code: *#*#66382723#*#*. The Dialer app clears all input and may close, but there isn't a confirmation message.

On Begin Sign In Failure: 8: Unknown internal error.
  1. The device is not set up properly with the Google Account.
  2. The passkey JSON is being created incorrectly.
CreatePublicKeyCredentialDomException: The incoming request cannot be validated The app's package ID is not registered with your server. Validate this in your server-side integration.
CreateCredentialUnknownException: During save password, found password failure response from one tap 16: Skipping password saving since the user is likely prompted with Android Autofill This error occurs only on Android 13 and lower, and only if Google is the Autofill provider. In this case, users see a save prompt from Autofill and the password is saved to the Google Password Manager. Note that the credentials saved using Autofill with Google are bi-directionally shared with Credential Manager API. As a result, this error can be safely ignored.

Additional resources

To learn more about the Credential Manager API and passkeys, view the following resources: