Improving the Login Experience and Reducing Cost: Passkey Integration in Android Apps

Passkey Jun 28, 2024

In today's fast-paced digital landscape, where convenience and security are paramount, finding the right balance between user experience and robust authentication measures is essential for organisations. At Halodoc, we understand the significance of improving the Login experience while maintaining stringent security standards.

In this blog post, we explore our latest endeavour  the integration of Passkey authentication into our Android apps. This innovative solution not only enhances user experience by improving the Login experience but also reduces costs associated with traditional OTP based authentication methods. Join us as we delve into the implementation of Passkey integration and its transformative impact on our Android app ecosystem.

Passkey Implementation

Let's now delve into the implementation details of Passkey within our Android application. The foundational implementation can be categorised into below key segments:

  • Digital Asset Link Configuration
  • Authenticator Integration
  • ProGuard Configuration
  • Passkey Registration
  • Passkey Authentication

Let's explore each section in depth!

To initiate support for passkeys within our Android application, the first step is to establish an association between our app and the corresponding website. This involves declaring associations via a Digital Asset Links JSON file hosted on our website and integrating a link to this file within our app's manifest.

The Digital Asset Links protocol and API enable apps and websites to assert verifiable statements about each other. This allows for associations between a website and a specific Android app, enhancing interoperability for seamless authentication experiences. This streamlined approach promotes trust and facilitates secure interactions across platforms.

[
  {
    "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
      ]
    }
  }
]

Relation Field: The relation field specifies the relationship between the website and the app. Here, we declare two relations:

  • delegate_permission/common.handle_all_urls: Grants the app the ability to handle all URLs, facilitating smooth app-linking functionality.
delegate_permission/common.get_login_creds: Specifies the app's capability to retrieve sign-in credentials, essential for Passkey Credential Manager Authenticator.

Target Field: The target field specifies the asset the declaration applies to, including:

  • namespace: Identifies the asset as belonging to an Android app.
  • package_name: Specifies the package name declared in the app's manifest.
  • SHA_HEX_VALUE: Lists the SHA256 fingerprints of our app, ensuring secure verification.

Hosting Digital Asset Link File: To enable proper verification by Google, we host the Digital Assets Link JSON file at the following public location on our sign-in domain:

https://[domain_name_of_website]/.well-known/assetlinks.json

By configuring Digital Asset Links in this manner, we establish a trusted connection between our website and app, facilitating secure authentication processes for our users.

2. Authenticator Integration

To integrate credential management authenticator functionality into our app, add the specified dependencies to our app module's build script.

implementation("androidx.credentials:credentials:1.3.0-alpha03")

This provides the core credential management features.

implementation("androidx.credentials:credentials-play-services-auth:1.3.0-alpha03")

This provides for devices running Android 13 and below to support credentials via Google Play Services.

Passkeys are exclusively supported on devices running Android 9 (API level 28) or higher

3. ProGuard Configuration

The ProGuard directives aim to preserve specific classes within the Android application, ensuring they are not obfuscated during the code shrinking and optimisation process.

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

specifies that the class androidx.credentials.CredentialManager should be preserved from obfuscation.

-keep class androidx.credentials.playservices.** { *; } ensures that all classes within the package androidx.credentials.playservices and its sub-packages are retained without obfuscation. This preservation is essential for maintaining the functionality and integrity of classes related to Google Play Services authentication within the application.

4. Passkey Registration

To utilise the Credential Manager for passkey registration, it is imperative to initialise the instance with either the application context or activity context. The following code demonstrates how to create a Credential Manager instance using the context:

val credentialManager = CredentialManager.create(context)

To initiate the Passkey registration process, users are required to create a Passkey and associate it with their account. Subsequently, the Passkey's public key is stored on the backend server, while the private key is securely stored on the user's device.

Let's delve deeper into the registration process:

  • When a user attempts to create a passkey, the client will call our server to request a challenge and other necessary metadata required for passkey creation by the Credential Manager Authenticator.
{
  "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"
  }
}
  • The client utilizes this response to prompt the authenticator to generate a new key pair. Before generating the key pair, the authenticator verifies the RpId in the response with the domain name of the Digital Asset link for security purposes. After verification, the newly created key pair serves to verify the user's identity to the relying party.
  • Passkey Creation Process: The createPasskey function initialises passkey creation by sending a backend JSON response to the CreateCredentialRequest, enabling asynchronous registration of user credentials.
  • User Consent and Credential Storage: Upon invocation, the function patiently awaits user consent for passkey usage, which involve biometric fingerprint or device PIN authentication. It also specifies the credential provider and account for passkey storage, thereby ensuring a secure authentication process.
  • The credential provider generates a fresh asymmetric key pair, preserving the private key securely within the device while transmitting the public key and pertinent details encapsulated in the JSON structure below to the client.
{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
  }
}
  • Subsequently, the client forwards the JSON containing the public key to our backend for validation and storing purposes, crucial for facilitating the user's login procedure.

5. Passkey Authentication

When a user attempts to log in by selecting the passkey option from the prompt, the authenticator will furnish a signature generated by the private key linked with the credential. On the backend server, the validity of this signature for login purposes will be verified using the corresponding public key.

Let's delve deeper into the authentication process:

  • When users opt for Passkey authentication, the client application will send a request to our server, fetching a challenge and crucial metadata essential for the Credential Manager Authenticator to proceed with the login process.
{
  "challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
  "allowCredentials": [],
  "timeout": 1800000,
  "userVerification": "required",
  "rpId": "credential-manager-app-test.glitch.me"
}
  • The client requires the authenticator to use one of the passkeys linked to the relying party to sign this JSON.
  • Requesting Passkey Credentials: The loginWithPasskey function retrieves Passkey credentials by sending JSON data from the backend to the authenticator. The authenticator verifies the RpId in the response against the domain name in the Digital Asset link for security purposes. It then locates a suitable credential corresponding to the Relying Party ID and initiates the authentication process.
  • User Consent and Authentication: Upon identification of a matching credential, the credential provider prompts the user to provide consent for authentication. Triggering the creation of a new assertion by signing over the clientDataHash and authenticatorData using the private key associated with the user's account.
{
  "id": "DEastxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "DEastxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eDEaseXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8tDEasQdAAAAAA",
    "signature": "MEUCIQCO1Cm4SA2xDEasdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
    "userHandle": "2HzoHm_hDEasuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
  }
}
  • Data Transfer to Backend Server: The authenticator returns the authenticatorData and assertion signature to the client, which then forwards this data to the backend Relying Party server.
  • Signature Validation: Here, the signature undergoes validation against the public key, ultimately determining the authentication result. If the validation is successful, the user gains access to our app's services.

Conclusion

The introduction of Passkey authentication marks a significant leap in enhancing user experience and security in our Android applications. With Passkey authentication, powered by biometric verification, we ensure robust account protection against potential threats while simplifying the login process and reducing operational costs associated with traditional OTP methods.

Moving forward, our commitment to innovation drives us to continually refine Passkey authentication and explore new solutions. With Passkey authentication, users can confidently access our Android apps, knowing their accounts are safeguarded by advanced security measures, while enjoying a seamless login experience.

References

Improving the Login Experience and Reducing Cost: The Complete Guide to Passkeys Integration at Halodoc
This blog explores Improving the Login Experience and Reducing Cost. The Complete Guide to Passkeys Integration at Halodoc
Improving the Login Experience and Reducing Cost: Passkeys Integration in iOS Apps
This blog walks you through improving the Login Experience and Reducing Cost by Passkeys Integration in iOS Apps
Sign in your user with Credential Manager | Android Developers
Passwordless login with passkeys | Authentication | Google for Developers
Bringing seamless authentication to your apps using Credential Manager API
Traditional authentication solutions pose a number of security and usability challenges. The Credential Manager API helps you and your users overcome these challenges. Android has created the…

Join us

Scalability, reliability and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resume at careers.india@halodoc.com.

About Halodoc

Halodoc is the number 1 Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek, and many more. We recently closed our Series D round and in total have raised around USD$100+ million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalized for all of our patient's needs, and are continuously on a path to simplify healthcare for Indonesia.

Mayur Hebbar

Android Developer