close icon
Passkeys

Native Login with Passkeys Is Now in Limited Early Access for Android Applications!

Native login with passkeys allows you to integrate passkeys into your native applications and offers a smooth user experience with all the benefits of passkeys.

November 20, 2024

Native login with passkeys allows you to seamlessly integrate passkeys into your mobile applications. If you wanted to add passkeys before, you had to integrate a web redirect flow that uses Universal Login.

Today, we are announcing Limited Early Access to Native Login with passkeys for Android applications.

How Does It Work?

In order to use Native Login with passkeys in your Android application, there are a few requirements:

  • Enable and configure your passkey policy: First of all, you need to enable passkeys for your tenant. Learn how to do it using our documentation.
  • Configure a Custom Domain: you can learn how to configure a custom domain using our documentation.
  • Enable Android App Links Support, which you can do following our docs.

Native login is available on the Android SDK from version 3.2.0 and Android API 28.

Sign Up

To sign up, you’ll need to use the "signupWithPasskey" method from the "AuthenticationAPIClient". When successful, you can handle the passkey creation from your device using Android’s CredentialManager following Android’s official docs. Here’s an example implementation in Kotlin:

val account = Auth0.getInstance("{YOUR_CLIENT_ID}", "{YOUR_DOMAIN}")

val client = AuthenticationAPIClient(account) 

client.signupWithPasskey(
            UserData(
                email = "jndoe@email.com"
            ), "{realm}"  
        ).start(object : Callback<PasskeyRegistrationChallenge, AuthenticationException> {
            override fun onSuccess(result: PasskeyRegistrationChallenge) {
                val passKeyRegistrationChallenge = result
                val request = CreatePublicKeyCredentialRequest(
                    Gson().toJson(
                        passKeyRegistrationChallenge.authParamsPublicKey
                    )
                )
                var response: CreatePublicKeyCredentialResponse?

        // start credential creation on the device 
                credentialManager.createCredentialAsync(requireContext(),
                    request,
                    CancellationSignal(),
                    Executors.newSingleThreadExecutor(),
                    object :
                        CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException> {

                        override fun onError(e: CreateCredentialException) {
                        }

                        override fun onResult(result: CreateCredentialResponse) {

                            response = result as CreatePublicKeyCredentialResponse
                            val authRequest = Gson().fromJson(
                                response?.registrationResponseJson,
                                PublicKeyCredentials::class.java
                            )

       // once the device created the key pair and signed the challenge you can sign in to Auth0 
                            client.signinWithPasskey(
                                passKeyRegistrationChallenge.authSession,
                                authRequest,
                                "{realm}"
                            )
                                .validateClaims()
                                .start(object : Callback<Credentials, AuthenticationException> {
                                    override fun onSuccess(result: Credentials) {
                                        credentialsManager.saveCredentials(result)
                                        Snackbar.make(
                                            requireView(),
                                            "Hello ${result.user.name}",
                                            Snackbar.LENGTH_LONG
                                        ).show()
                                    }

                                    override fun onFailure(error: AuthenticationException) {
                                        Snackbar.make(
                                            requireView(),
                                            error.getDescription(),
                                            Snackbar.LENGTH_LONG
                                        ).show()
                                    }
                                })
                        }
                    })
            }

            override fun onFailure(error: AuthenticationException) {
                Snackbar.make(
                    requireView(),
                    error.getDescription(),
                    Snackbar.LENGTH_LONG
                ).show()
            }
        })

The signupWithPasskey method starts the passkey creation ceremony. It generates the challenge for the authenticator to sign once it has created the public key credential pair. In your case, your Android device acts as the authenticator and you create the key pair and sign the challenge when you use the CreatePublicKeyCredentialRequest method from Android’s Credential Manager.

Then, you can use Android’s Credential Manager method createCredential or createCredentialAsync to start the creation. Once successful, you can use the result from the creation to send that data to Auth0 and sign in using the signinWithPasskey method, which is covered below.

Login

To sign in, you’ll have to first initiate the passkey challenge by using the passkeyChallenge method from the AuthenticationAPIClient. When the call is successful, you can initiate the credential retrieval using Android’s getCredential method from the CredentialManager.

When your device retrieves the credential successfully, you can then initiate the sign-in process with Auth0. An example implementation could be:

val account = Auth0.getInstance("{YOUR_CLIENT_ID}", "{YOUR_DOMAIN}")
val client = AuthenticationAPIClient(account)

client.passkeyChallenge(realm)
            .start(object : Callback<PasskeyChallenge, AuthenticationException> {
                override fun onSuccess(result: PasskeyChallenge) {
                    val passkeyChallengeResponse = result
                    val request =
                        GetPublicKeyCredentialOption(Gson().toJson(passkeyChallengeResponse.authParamsPublicKey))
                    val getCredRequest = GetCredentialRequest(
                        listOf(request)
                    )
                    credentialManager.getCredentialAsync(context,
                        getCredRequest,
                        CancellationSignal(),
                        executor,
                        object :
                            CredentialManagerCallback<GetCredentialResponse, GetCredentialException> {
                            override fun onError(e: GetCredentialException) {
                                Log.w(TAG, "Error fetching public key credential")
                                callback.onFailure(handleGetCredentialFailure(e))
                            }

                            override fun onResult(result: GetCredentialResponse) {
                                when (val credential = result.credential) {
                                    is PublicKeyCredential -> {
                                        val authRequest = Gson().fromJson(
                                            credential.authenticationResponseJson,
                                            PublicKeyCredentials::class.java
                                        )
                // initiate signin process with Auth0
                                        client.signinWithPasskey(
                                            passkeyChallengeResponse.authSession,
                                            authRequest,
                                            realm
                                        )
                                            .validateClaims()
                                            .addParameters(parameters)
                                            .start(callback)
                                    }

                                    else -> {
                                        Log.w(
                                            TAG,
                                            "Received unrecognized credential type ${credential.type}."
                                        )
                                        callback.onFailure(AuthenticationException("Received unrecognized credential type ${credential.type}"))
                                    }
                                }
                            }
                        })

                }

For more detailed examples, you can check out the Android SDK sample folder on Github.

If you need Java examples, you can check out this doc.

What Use Cases Are Supported?

The initial early access release provides support for the following use cases:

  • Sign Up with a passkey: users can register a new passkey for Auth0/your app and store it in the device credential manager for future use.
  • Login with a passkey: users can retrieve passkeys using the device’s credential manager and use them to log in.

How Can You Apply?

If you are interested in joining the early access program, please reach out to your account manager.

Any products, features, or functionality referenced in this material that are not currently generally available may not be delivered on time or at all. Product roadmaps do not represent a commitment, obligation, or promise to deliver any product, feature, or functionality, and you should not rely on them to make your purchase decisions.

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon