A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://codelabs.developers.google.com/credential-manager-api-for-android below:

Revision 2024 Q4: Learn how to simplify auth journeys using Credential Manager API in your Android app

Skip to main content Revision 2024 Q4: Learn how to simplify auth journeys using Credential Manager API in your Android app 1. Before you begin

Traditional authentication solutions pose a number of security and usability challenges.

Passwords are widely used but...

Android has worked towards creating Credential Manager API to simplify the sign-in experience and address security risks by supporting passkeys, the next generation industry standard for passwordless authentication.

Credential Manager brings together support for passkeys and combines it with traditional authentication methods such as passwords, Sign in with Google etc.

Users will be able to create passkeys, store them in Google Password Manager, which will sync those passkeys across the Android devices where the user is signed in. A passkey has to be created, associated with a user account, and have its public key stored on a server before a user can sign in with it.

Note: Support for 3rd-party password managers is available on devices that run Android 14 or higher.

In this codelab, you will learn how to sign up using passkeys and password using Credential Manager API and use them for future authentication purposes. There are 2 flows including:

Prerequisites What you'll learn What you'll need

One of the following device combinations:

Note: Credential Manager API is supported from Android 4.4 for passwords and sign-in with Google options. Passkeys are supported only on devices that run Android 9 (API level 28)

or higher.

2. Get set up

Note: The first step to enable support for passkeys for your Android app is to associate your app and the website.

This sample app requires a digital asset linking to a website for Credential Manager to validate the linking and proceed further, so the rp id used in the mock responses is from a mocked 3P server(Glitch.me). If you want to try your own mock response, try adding your app domain and don't forget to complete the digital asset linking as mentioned here.

Use the same debug.keystore mentioned in the project to build debug and release variants to verify the digital asset linking of the package name and sha on your mock server. (This is already being done for you for the sample app in build.gradle).

  1. Clone this repo on your laptop from credman_codelab branch : https://github.com/android/identity-samples/tree/credman_codelab
git clone -b credman_codelab https://github.com/android/identity-samples.git
  1. Go to the CredentialManager module and open the project in Android Studio.
Lets see app's initial state

To see how the initial state of the app works, follow these steps:

  1. Launch the app.
  2. You see a main screen with a sign up and sign in button. These buttons don't do anything yet, but we'll enable their functionality in the upcoming sections.

3. Add the ability to sign up using passkeys

When signing up for a new account on an Android app that uses the Credential Manager API, users can create a passkey for their account. This passkey will be securely stored on the user's chosen credential provider and used for future sign-ins, without requiring the user to enter their password each time.

Now, you will create a passkey and register user credentials using biometrics/screen lock.

Sign up with passkey

The code inside Credential Manager/app/main/java/SignUpFragment.kt, defines a text field "username" and a button to sign up with a passkey.

Pass the challenge and other json response to a createPasskey() call

Before a passkey is created, you need to request from the server the necessary information to be passed to the Credential Manager API during createCredential() call.

You already have a mock response in your project's assets, called RegFromServer.txt, which returns the necessary parameters in this codelab.

SignUpFragment.kt
//TODO : Call createPasskey() to signup with passkey

val data = createPasskey()

This method will be called once you have a valid username filled on the screen.

SignUpFragment.kt
//TODO create a CreatePublicKeyCredentialRequest() with necessary registration json from server

val request = CreatePublicKeyCredentialRequest(fetchRegistrationJsonFromServer())

The fetchRegistrationJsonFromServer() method reads an emulated server PublicKeyCredentialCreationOptions JSON response from assets and returns the registration JSON to be passed while creating the passkey.

SignUpFragment.kt
//TODO fetch registration mock response

val response = requireContext().readFromAsset("RegFromServer")

//Update userId,challenge, name and Display name in the mock
return response.replace("<userId>", getEncodedUserId())
   .replace("<userName>", binding.username.text.toString())
   .replace("<userDisplayName>", binding.username.text.toString())
   .replace("<challenge>", getEncodedChallenge())

Note: userId and challenge need to be encoded using Base64. Check getEncodedUserId() and getEncodedChallenge() to make sure they are encoded properly.

A real server PublicKeyCredentialCreationOptions response may return more options. An example of some of these fields is given below:

{
  "challenge": String,
  "rp": {
    "name": String,
    "id": String
  },
  "user": {
    "id": String,
    "name": String,
    "displayName": String
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

The following table explains some of the important parameters in a PublicKeyCredentialCreationOptions object:

Parameters

Descriptions

challenge

A server-generated random string that contains enough entropy to make guessing it infeasible. It should be at least 16 bytes long. This is required but unused during registration unless doing attestation.

user.id

A user's unique ID. This value must not include personally identifying information, for example, e-mail addresses or usernames. A random, 16-byte value generated per account will work well.

user.name

This field should hold a unique identifier for the account that the user will recognise, like their email address or username. This will be displayed in the account selector. (If using a username, use the same value as in password authentication.)

user.displayName

This field is an optional, more user-friendly name for the account.

rp.id

The Relying Party Entity corresponds to your application details. It has the following attributes:

pubKeyCredParams

List of allowed algorithms and key types. This list must contain at least one element.

excludeCredentials

The user trying to register a device may have registered other devices. To limit the creation of multiple credentials for the same account on a single authenticator, you can then ignore these devices. The transports member, if provided, should contain the result of calling getTransports() during the registration of each credential.

authenticatorSelection.authenticatorAttachment

Indicates if the device should be attached on the platform, or not or if there is no requirement to do so. Set this value to platform. This indicates that you want an authenticator that is embedded into the platform device, and the user will not be prompted to insert e.g. a USB security key.

residentKey

indicate the value required to create a passkey.

Create a credential
  1. Once you create a CreatePublicKeyCredentialRequest(), you need to call the createCredential() call with the created request.
SignUpFragment.kt
//TODO call createCredential() with createPublicKeyCredentialRequest

try {
   response = credentialManager.createCredential(
       requireActivity(),
       request
   ) as CreatePublicKeyCredentialResponse
} catch (e: CreateCredentialException) {
   configureProgress(View.INVISIBLE)
   handlePasskeyFailure(e)
}

Your data is safe: Even if the user uses a biometric sensor to create a passkey, the server never sees the biometric data. The device only verifies that the stored biometric information matches the user's one locally to create a passkey or sign in with it. Biometric information never leaves the device.

Note: createCredential() call is a suspend function, so this needs to be called through a coroutineScope/another suspend function.

  1. Finally, you need to complete the registration process. The app sends a public key credential to the server which registers it to the current user.

Here, we have used a mock server, so we just return true indicating that server has saved the registered public key for future authentication and validation purposes. You can read more about server-side passkey registration for your own implementation.

Inside signUpWithPasskeys() method, find relevant comment and replace with following code:

SignUpFragment.kt
//TODO : complete the registration process after sending public key credential to your server and let the user in

data?.let {
   registerResponse()
   DataProvider.setSignedInThroughPasskeys(true)
   listener.showHome()
}

A real PublicKeyCredential may contain more fields. An example of these fields is shown below:

{
  "id": String,
  "rawId": String,
  "type": "public-key",
  "response": {
    "clientDataJSON": String,
    "attestationObject": String,
  }
}

The following table explains some of the important parameters in a PublicKeyCredential object:

Parameters

Descriptions

id

A Base64URL encoded ID of the created passkey. This ID helps the browser determine whether a matching passkey is in the device upon authentication. This value must be stored in the database on the backend.

rawId

An ArrayBuffer object version of credential ID.

response.clientDataJSON

An ArrayBuffer object encoded client data.

response.attestationObject

An ArrayBuffer encoded attestation object. It contains important information, such as an RP ID, flags, and a public key.

Run the app, and you will be able to click on the Sign up with passkeys button and create a passkey.

4. Save a password in Credential Provider

In this app, inside your SignUp screen, you already have a sign up with username and password implemented for demonstration purposes.

To save the user password credential with their password provider, you will implement a CreatePasswordRequest to pass to createCredential() to save the password.

SignUpFragment.kt
//TODO : Save the user credential password with their password provider

createPassword()
SignUpFragment.kt
//TODO : CreatePasswordRequest with entered username and password

val request = CreatePasswordRequest(
   binding.username.text.toString(),
   binding.password.text.toString()
)
SignUpFragment.kt
//TODO : Create credential with created password request


try {
   credentialManager.createCredential(requireActivity(), request) as CreatePasswordResponse
} catch (e: Exception) {
   Log.e("Auth", " Exception Message : " + e.message)
}
5. Add the ability to authenticate with a passkey or password

Now you are ready to use it as a way to authenticate to your app safely.

Obtain the challenge and other options to pass to getPasskey() call

Before you ask the user to authenticate, you need to request parameters to pass in WebAuthn JSON from the server, including a challenge.

You already have a mock response in your assets (AuthFromServer.txt) which returns such parameters in this codelab.

SignInFragment.kt
//TODO : Call getSavedCredentials() method to signin using passkey/password

val data = getSavedCredentials()
SigninFragment.kt
//TODO create a GetPublicKeyCredentialOption() with necessary registration json from server

val getPublicKeyCredentialOption =
   GetPublicKeyCredentialOption(fetchAuthJsonFromServer(), null)

The fetchAuthJsonFromServer() method reads the authentication JSON response from assets and returns authentication JSON to retrieve all the passkeys associated with this user account.

The 2nd parameter of GetPublicKeyCredentialOption() is clientDataHash - a hash that is used to verify the relying party identity. Set this only if you have set the GetCredentialRequest.origin. For the sample app, this is set to null.

SignInFragment.kt
//TODO fetch authentication mock json

return requireContext().readFromAsset("AuthFromServer")

Note : This codelab's server is designed to return a JSON that is as similar as possible to the PublicKeyCredentialRequestOptions dictionary that's passed to API's getCredential() call. The following code snippet includes a few example options that you could receive in a real response:

{
  "challenge": String,
  "rpId": String,
  "userVerification": "",
  "timeout": 1800000
}

The following table explains some of the important parameters in a PublicKeyCredentialRequestOptions object:

Parameters

Descriptions

challenge

A server-generated challenge in an ArrayBuffer object. This is required to prevent replay attacks. Never accept the same challenge in a response twice. Consider it a CSRF token.

rpId

An RP ID is a domain. A website can specify either its domain or a registrable suffix. This value must match the rp.id parameter used when the passkey was created.

SigninFragment.kt
//TODO create a PasswordOption to retrieve all the associated user's password

val getPasswordOption = GetPasswordOption()
Get credentials SignInFragment.kt
//TODO call getCredential() with required credential options

val result = try {
   credentialManager.getCredential(
       requireActivity(),
       GetCredentialRequest(
           listOf(
               getPublicKeyCredentialOption,
               getPasswordOption
           )
     )
   )
} catch (e: Exception) {
   configureViews(View.INVISIBLE, true)
   Log.e("Auth", "getCredential failed with exception: " + e.message.toString())
   activity?.showErrorAlert(
       "An error occurred while authenticating through saved credentials. Check logs for additional details"
   )
   return null
}

if (result.credential is PublicKeyCredential) {
   val cred = result.credential as PublicKeyCredential
   DataProvider.setSignedInThroughPasskeys(true)
   return "Passkey: ${cred.authenticationResponseJson}"
}
if (result.credential is PasswordCredential) {
   val cred = result.credential as PasswordCredential
   DataProvider.setSignedInThroughPasskeys(false)
   return "Got Password - User:${cred.id} Password: ${cred.password}"
}
if (result.credential is CustomCredential) {
   //If you are also using any external sign-in libraries, parse them here with the utility functions provided.
}

The following code snippet includes an example PublicKeyCredential object:

{
  "id": String
  "rawId": String
  "type": "public-key",
  "response": {
    "clientDataJSON": String
    "authenticatorData": String
    "signature": String
    "userHandle": String
  }
}

The following table isn't exhaustive, but it contains the important parameters in the PublicKeyCredential object:

Parameters

Descriptions

id

The Base64URL encoded ID of the authenticated passkey credential.

rawId

An ArrayBuffer object version of credential ID.

response.clientDataJSON

An ArrayBuffer object of client data. This field contains information, such as the challenge and the origin that the RP server needs to verify.

response.authenticatorData

An ArrayBuffer object of authenticator data. This field contains information like RP ID.

response.signature

An ArrayBuffer object of the signature. This value is the core of the credential and must be verified on the server.

response.userHandle

An ArrayBuffer object that contains the user ID set at creation time. This value can be used instead of the credential ID if the server needs to pick the ID values that it uses, or if the backend wishes to avoid the creation of an index on credential IDs.

Note: getCredential() call is a suspend function, so this needs to be called through a coroutineScope/another suspend function.

Here, we have used a mock server, so we just return true indicating that server has verified the assertion. You can read more about server-side passkey authentication for your own implementation.

Inside the signInWithSavedCredentials() method, find the relevant comment and replace with the following code:

SignInFragment.kt
//TODO : complete the authentication process after validating the public key credential to your server and let the user in.

data?.let {
   sendSignInResponseToServer()
   listener.showHome()
}

Run the app and navigate to sign in > Sign in with passkeys/saved password, and try signing in using saved credentials.

Try it

You implemented the creation of passkeys, saving password in Credential Manager, and authentication through passkeys or saved password using Credential Manager API on your Android app.

6. Congratulations!

You finished this codelab! If you want to check the final solution, which is available at https://github.com/android/identity-samples/tree/main/CredentialManager

If you have any questions, ask them on StackOverflow with a passkey tag.

Learn more

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],[],[],[]]


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4