Tag Archives: mobile

Order Files in Android

Posted by Aditya Kumar – Software Engineer

Context

Binary layout using a symbol order file (also known as binary order file or linker order file) is a well-known link-time optimization. The linker uses the order of symbols in order file to lay out symbols in the binary. Order file based binary layout improves application launch time as well as other critical user journeys. Order file generation is typically a multi-step process where developers use different tools at every stage. We are providing a unified set of tools and documentation that will allow every native app developer to leverage this optimization. Both Android app developers and the AOSP community can benefit from the tools.

Background

Source code is typically structured to facilitate software development and comprehension. The layout of functions and variables in a binary is also impacted by their relative ordering in the source code. The binary layout impacts application performance as the operating system has no way of knowing which symbols will be required in future and typically uses spatial locality as one of the cost models for prefetching subsequent pages.

But the order of symbols in a binary may not reflect the program execution order. When an application executes, fetching symbols that are not present in memory would result in page faults. For example, consider the following program:

// Test.cpp
int foo() { /* */ } int bar() { /* */ } // Other functions... int main() { bar(); foo();

}

Which gets compiled into:

# Test.app page_x: _foo page_y: _bar # Other symbols page_z:_main

When Test.app starts, its entrypoint _main is fetched first then _bar followed by _foo. Executing Test.app can lead to page faults for fetching each function. Compare this to the following binary layout where all the functions are located in the same page (assuming the functions are small enough).

# Test.app page_1: _main page_1: _bar page_1: _foo # Other symbols

In this case when _main gets fetched, _bar and _foo can get fetched in the memory at the same time. In case these symbols are large and they are located in consecutive pages, there is a high chance the operating system may prefetch those pages resulting in less page faults.

Because execution order of functions during an application lifecycle may depend on various factors it is impossible to have a unique order of symbols that is most efficient. Fortunately, application startup sequence is fairly deterministic and stable in general. And it is also possible to build a binary having a desired symbol order with the help of linkers like lld which is the default linker for Android NDK toolchain.

Order file is a text file containing a list of symbols. The linker uses the order of symbols in order file to lay out symbols in the binary. An order file having functions that get called during the app startup sequence can reduce page faults resulting in improved launch time. Order files can improve the launch time of mobile applications by more than 2%. The benefits of order files are more meaningful on larger apps and lower end devices. A more mature order file generation system can improve other critical user journeys.

Design

The order file generation involves the following steps

    • Collect app startup sequence using compiler instrumentation technique
      • Use compiler instrumentation to report every function invocation
      • Run the instrumented binary to collect launch sequence in a (binary) profraw file
    • Generate order file from the profraw files
    • Validate order file
    • Merge multiple order files into one
    • Recompile the app with the merged order file

Overview

The order file generation is based on LLVM’s compiler instrumentation process. LLVM has a stage to generate the order file then recompile the source code using the order file.ALT TEXT


Collect app startup sequence

The source code is instrumented by passing -forder-file-instrumentation to the compiler. Additionally, the -orderfile-write-mapping flag is also required for the compiler to generate a mapping file. The mapping file is generated during compilation and it is used while processing the profraw file. The mapping file shows the mapping from MD5 hash to function symbol (as shown below).

# Mapping file MD5 db956436e78dd5fa main MD5 83bff1e88ac48f32 _GLOBAL__sub_I_main.cpp MD5 c943255f95351375 _Z5mergePiiii MD5 d2d2238cf08db816 _Z9mergeSortPiii MD5 11ed18006e729e73 _Z4partPiii MD5 3e897b5ee8bebbd1 _Z9quickSortPiii

The profile (profraw file) is generated every time the instrumented application is executed. The profile data in the profraw file contains the MD5 hash of the functions executed in chronological order. The profraw file does not have duplicate entries because each function only outputs its MD5 hash on first invocation. A typical run of binary containing the functions listed in the mapping file above can have the following profraw entries.

# Profraw file 00000000 32 8f c4 8a e8 f1 bf 83 fa d5 8d e7 36 64 95 db |2...........6d..| 00000010 16 b8 8d f0 8c 23 d2 d2 75 13 35 95 5f 25 43 c9 |.....#..u.5._%C.| 00000020 d1 bb be e8 5e 7b 89 3e 00 00 00 00 00 00 00 00 |....^{.>........| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

In order to find the function names corresponding to the MD5 hashes in a profraw file, a corresponding mapping file is used.

Note: The compiler instrumentation for order files (-forder-file-instrumentation) only works when an optimization flag (01, 02, 03, 0s, 0z) is passed. So, if -O0 (compiler flag typically used for debug builds) is passed, the compiler will not instrument the binary. In principle, one should use the same optimization flag for instrumentation that is used in shipping release binaries.

The Android NDK repository has scripts that automate the order file generation given a mapping file and an order file.


Recompiling with Order File

Once you have an order file, you provide the path of the order file to the linker using the --symbol-ordering-file flag.


Detailed design

Creating Order File Build Property

The Android Open Source Project (AOSP) uses a build system called soong so we can leverage this build system to pass the flags as necessary. The order file build property has four main fields:

    • instrumentation
    • load_order_file
    • order_file_path
    • cflags

The cflags are meant to add other necessary flags (like mapping flags) during compilation. The load_order_file and order_file_path tells the build system to recompile with the order file rather than set it to the profiling stage. The order files must be in saved in one of two paths:

    • toolchain/pgo-profiles/orderfiles
    • vendor/google_data/pgo_profile/orderfiles

# Profiling orderfile: { instrumentation: true, load_order_file: false, order_file_path: "", cflags: [ "-mllvm", "-orderfile-write-mapping=<filename>-mapping.txt", ], } #Recompiling with Order File orderfile: { instrumentation: true, load_order_file: true, order_file_path: "<filename>.orderfile", }

Creating order files

We provide a python script to create an order file from a mapping file and a profraw file. The script also allows removing a particular symbol or creating an order file until a particular symbol.

Script Flags:

        • Profile file (--profile-file):
                • Description: The profile file generated by running a binary compiled with -forder-file-instrumentation
        • Mapping file (--mapping-file):
                • Description: The mapping file generated during compilation that maps MD5 hashes to symbol names
        • Output file (--output):
                • Description: The output file name for the order file. Default Name: default.orderfile
        • Deny List (--denylist):
                • Description: Symbols that you want to exclude from the order file
        • Last symbol (--last-symbol):
                • Description: The order file will end at the passed last symbol and ignore the symbols after it. If you want an order file only for startup, you should pass the last startup symbol. Last-symbol has priority over leftover so we will output until the last symbol and ignore the leftover flag.
        • Leftover symbols (--leftover):
                • Description: Some symbols (functions) might not have been executed so they will not appear in the profile file. If you want these symbols in your order file, you can use this flag and it will add them at the end.

Validating order files

Once we get an order file for a library or binary, we need to check if it is valid based on a set of criteria. Some order files may not be of good quality so they are better discarded. This can happen due to several reasons like application terminated unexpectedly, the runtime could not write the complete profraw file before exiting, an undesired code-sequence was collected in the profile, etc. To automate this process, we provide a python script that can help developers check for:

    • Partial order that needs to be in the order file
    • Symbols that have to be present in order file
    • Symbols that should not be present in order file
    • Minimum number of symbols to make an order file

Script Flags:

        • Order file (--order-file):
                • Description: The order file you are validating on the below criteria.
        • Partial Order (--partial):
                • Description: A partial order of symbols that must be held in the order file.
        • Allowed Lists (--allowlist):
                • Description: Symbols that must be present in the order file.
        • Denied Lists (--denylist):
                • Description: Symbols that should not be in the order file. Denylist flag has priority over allowlist.
        • Minimum Number of Entries (--min):
                • Description: Minimum number of symbols needed for an order file

Merging orderfiles

At a higher level, the order file symbols in a collection of order files approximate a partial order (poset) of function names with order defined by time of execution. Across different runs of an application, the order files might have variations. These variations could be due to OS, device class, build version, user configurations etc. However, the linker can only take one order file to build an application. In order to have one order file that provides the desired benefits, we need to merge these order files into a single order file. The merging algorithm also needs to be efficient so as to not slow down the build time. There are non-linear clustering algorithms that may not scale well for merging large numbers of order files, each having many symbols. We provide an efficient merging algorithm that converges quickly. The algorithm allows for customizable parameters, such that developers can tune the outcome.

Merging N partial order sets can be done either pessimistically (merging a selection of order files all the way until there is one order file left) or optimistically (merging all of them at once). The pessimistic approach can be inefficient as well as sub-optimal. As a result, it is better to work with all N partial order sets at once. In order to have an efficient implementation it helps to represent all N posets with a weighted directed Graph (V,E) where:

    • V: Elements of partial order sets (symbols) and the number of times it appears in different partial order sets. Note that the frequency of vertices may be greater than the sum of all incoming edges because of invocations from uninstrumented parts of binary, dependency injection etc.
    • E (V1 -> V2): An edge occurs if the element of V2 immediately succeeds V1 in any partial order set with its weight being the number of times this happens.

For a binary executable, there is one root (e.g., main) vertex, but shared libraries might have many roots based on which functions are called in the binary using them. The graph gets complicated if the application has threads as they frequently result in cycles. To have a topological order, cycles are removed by preferring the highest probability path over others. A Depth-First traversal that selects the highest weighted edge serves the purpose.

Removing Cycles:

- Mark back edges during a Depth-First traversal - For each Cycle (c):      - Add the weights of all in-edges of each vertex (v) in the cycle excluding the edges in the cycle      - Remove the cycle edge pointing **to** the vertex with highest sum

After cycles are removed, the same depth first traversal gives a topological order (the order file) when all the forward edges are removed. Essentially, the algorithm computes a minimum-spanning-tree of maximal weights and traverses the tree in topological order.

Producing an order:

printOrderUtil(G, n, order):    - If n was visited:         - return    - Add n to the end of order    - Sort all out edges based on weight    - For every out_edge (n, v):        - printOrderUtil(G, v, order) printOrder(G):    - Get all roots    - order = []    - For each root r:        - printOrderUtil(G, r, order)    - return order

Example:

Given the following order files:

    • main -> b -> c -> d
    • main -> a -> c
    • main -> e -> f
    • main -> b
    • main -> b
    • main -> c -> b
Flow diagram of orderfiles

The graph to the right is obtained by removing cycles.

    • DFS: main -> b-> c -> b
    • Back edge: c -> b
    • Cycle: b -> c-> b
    • Cycle edges: [b -> c, c -> b]
    • b’s sum of in-edges is 3
    • c’s sum of in-edges is 2
    • This implies b will be traversed from a higher frequency edge, so c -> b is removed
    • Ignore forward edges a->c, main->c
    • The DFS of the acyclic graph on the right will produce an order file main -> b -> c -> d -> a -> e -> f after ignoring the forward edges.

Collecting order files for Android Apps (Java, Kotlin)

The order file instrumentation and profile data collection is only enabled for C/C++ applications. As a result, it cannot benefit Java or Kotlin applications. However, Android apps that ship compiled C/C++ libraries can benefit from order file.

To generate order file for libraries that are used by Java/Kotlin applications, we need to invoke the runtime methods (called as part of order file instrumentation) at the right places. There are three functions that users have to call:

    • __llvm_profile_set_filename(char *f): Set the name of the file where profraw data will be dumped.
    • __llvm_profile_initialize_file: Initialize the file set by __llvm_profile_set_filename
    • __llvm_orderfile_dump: Dumps the profile(order file data) collected while running instrumented binary

Similarly, the compiler and linker flags should be added to build configurations. We provide template build system files e.g, CMakeLists.txt to compile with the correct flags and add a function to dump the order files when the Java/Kotlin application calls it.

# CMakeLists.txt set(GENERATE_PROFILES ON) #set(USE_PROFILE "${CMAKE_SOURCE_DIR}/demo.orderfile") add_library(orderfiledemo SHARED orderfile.cpp) target_link_libraries(orderfiledemo log) if(GENERATE_PROFILES) # Generating profiles require any optimization flag aside from -O0. # The mapping file will not generate and the profile instrumentation does not work without an optimization flag. target_compile_options( orderfiledemo PRIVATE -forder-file-instrumentation -O2 -mllvm -orderfile-write-mapping=mapping.txt ) target_link_options( orderfiledemo PRIVATE -forder-file-instrumentation ) target_compile_definitions(orderfiledemo PRIVATE GENERATE_PROFILES) elseif(USE_PROFILE) target_compile_options( orderfiledemo PRIVATE -Wl,--symbol-ordering-file=${USE_PROFILE} -Wl,--no-warn-symbol-ordering ) target_link_options( orderfiledemo PRIVATE -Wl,--symbol-ordering-file=${USE_PROFILE} -Wl,--no-warn-symbol-ordering ) endif()

We also provide a sample app to dump order files from a Kotlin application. The sample app creates a shared library called “orderfiledemo” and invokes the DumpProfileDataIfNeeded function to dump the order file. This library can be taken out of this sample app and can be repurposed for other applications.

// Order File Library #if defined(GENERATE_PROFILES) extern "C" int __llvm_profile_set_filename(const char *); extern "C" int __llvm_profile_initialize_file(void); extern "C" int __llvm_orderfile_dump(void); #endif void DumpProfileDataIfNeeded(const char *temp_dir) { #if defined(GENERATE_PROFILES) char profile_location[PATH_MAX] = {}; snprintf(profile_location, sizeof(profile_location), "%s/demo.output", temp_dir); __llvm_profile_set_filename(profile_location); __llvm_profile_initialize_file(); __llvm_orderfile_dump(); __android_log_print(ANDROID_LOG_DEBUG, kLogTag, "Wrote profile data to %s", profile_location); #else __android_log_print(ANDROID_LOG_DEBUG, kLogTag, "Did not write profile data because the app was not " "built for profile generation"); #endif } extern "C" JNIEXPORT void JNICALL Java_com_example_orderfiledemo_MainActivity_runWorkload(JNIEnv *env, jobject /* this */, jstring temp_dir) { DumpProfileDataIfNeeded(env->GetStringUTFChars(temp_dir, 0)); }

# Kotlin Application class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) runWorkload(applicationContext.cacheDir.toString()) binding.sampleText.text = "Hello, world!" } /** * A native method that is implemented by the 'orderfiledemo' native library, * which is packaged with this application. */ external fun runWorkload(tempDir: String) companion object { // Used to load the 'orderfiledemo' library on application startup. init { System.loadLibrary("orderfiledemo") } } }

Limitation

order file generation only works for native binaries. The validation and merging scripts will work for any set of order files.

References

External References

Aligning the user experience across surfaces for Google Pay

Posted by Dominik Mengelt – Developer Relations Engineer

During the last months we've been working hard to align the Google Pay user experience across Web and Android. We are committed to advancing all Google Pay surfaces progressively, and creating a more cohesive experience for your users. In addition, the Google Pay sheets for Android and Chrome on Android now use the latest Material 3 design system with Web to follow in early 2024.

UX improvements on Android

Aligning the bottom sheets on Android and Chrome for Android (Mobile Web) led to a ~2.5% increase in conversion rate and a ~39% reduction in errors for users using Google Pay with Chrome on Android[1].

Side by side photos of Gogle Pay sheet on Android and Mobile Web
Figure 1: The identical Google Pay bottom sheets for Android (left) and Chrome on Android (right)


A completely revamped Google Pay sheet on the Web

On the web we aligned the user experience to be the same as on Android. Additionally we gave the Payment Handler window a more minimalistic look. With these changes we are seeing a conversion rate increase of ~9%.[1]

Google Pay displayed inside the new minimalistic Payment Handler window
Figure 2: Google Pay displayed inside the new minimalistic Payment Handler window


No changes required!

Whether you are a merchant integrating Google Pay on your own or through a PSP, you don’t need to make any changes. We've already rolled out these changes to most of our users. This means that your users are likely already benefiting from the new experience or will very soon. For certain features, for example dynamic price updates, Google Pay will temporarily show the previous user experience. We are actively working on migrating all features to benefit from the new updated design.


Getting started with Google Pay

Not yet using Google Pay? Take a look at the documentation to start integrating Google Pay today. Learn more about the integration by taking a look at our sample application for Android on GitHub or use one of our button components for your web integration. When you are ready, head over to the Google Pay & Wallet console and submit your integration for production access.

Follow @GooglePayDevs on X (formerly Twitter) for future updates. If you have questions, tag @GooglePayDevs and include #AskGooglePayDevs in your tweets.


[1] internal Google study

All treats, no tricks: 6 solutions to common developers challenges

Posted by Google for Developers

For many, Halloween is the perfect excuse to dress up and celebrate the things that haunt us. Google for Developers is embracing the spirit of the season by diving into the spine-chilling challenges that spook software developers and engineers. Read on to uncover these lurking terrors and discover the tricks – and treats – to conquer them.


The code cemetery

Resilient code requires regular updates, and when it comes to solving bugs, it’s much easier to find them when there are fewer lines of code. When faced with legacy or lengthy code, consider simplifying and refreshing it to make it more manageable – because no one likes an ancient or overly complex codebase. Here are some best practices.

Start small: Don't try to update your entire codebase at once. Instead, start by updating small, isolated parts of the codebase to minimize the risk of introducing new bugs.

Use a version control system: Track your changes and easily revert to a previous version if necessary.

Consider a refactoring tool: This can help you to make changes to your code without breaking it.

Test thoroughly: Make sure to test your changes thoroughly before deploying them to production. This includes testing the changes in isolation, as well as testing them in conjunction with the rest of the codebase. See more tips about testing motivation below.

Document your changes: Include new tooling, updated APIs, and any changes so other developers understand what you have done and why.


Testing terrors

When you want to build and ship quickly, it’s tempting to avoid writing tests for your code because they might slow you down in the short term. But beware, untested code will come back to haunt you later. Testing is a best practice that can save you time, money, and angst in the long run. Even if you know you should run tests, it doesn’t mean you want to. Use these tips to help make writing tests easier.

Test gamification: Turn test writing into a game. Challenge yourself to write tests faster than your coworker can say "code coverage."

Pair programming: Write tests together with a colleague. It's like having a workout buddy – more fun and motivating.

Set up test automation: Automate tests wherever possible– it's better AND more efficient.


A monster problem: not being able to choose your tech stack

Many developers have strong preferences when it comes to products, but sometimes legacy technology or organizational needs can limit choices. This can be deflating, especially if it prevents you from using the latest tools. If you’re faced with a similar situation, it’s worth expressing your recommendations to your team. Here’s how:

Lobby for change: If the current tech stack really isn't working out, advocate for a change. This may require documentation over a series of events, but you can use that to build your case.

Pitch the benefits: If you’re ready to share your preferences, explain how your tech stack of choice benefits the project, similarly to how optimized code improves performance.

Showcase expertise: Demonstrate your knowledge in your preferred stack, whether it’s through a Proof of Concept or a presentation.

Upskill: If you have to dive into a top-down tech stack that you are not familiar with, consider it a learning opportunity. It’s like exploring a new coding language.

Compromise is key: First, recognize that all of the points above are still well-worth aiming for, but sometimes, you do have to compromise. Think of it as working with legacy code - not ideal, but doable. So if you aren’t able to influence in your favor, don’t be dismayed.


Not a trick: ship your code smarter

The only thing worse than spending the end of the week fixing buggy code isexcept for spending the weekend fixing buggy code when you had other plans. Between less time to react to problems, taking up personal time, and fewer people available to help troubleshoot – shipping code when you don’t have the proper resources in place to help is risky at best. Here are a handful of best practices to help you build a better schedule and avoid the Saturday and Sunday Scaries.

Consider business hours and user impact: Schedule deployments during off-peak times when fewer users will be impacted. For B2B companies, Friday afternoons can minimize disruption for customers, but for smaller companies, Friday deployments might mean spending your weekend fixing critical issues. Pick a schedule that works for you.

Automate testing: Implement automated testing in your development process to catch issues early.

Make sure your staging environment is right: Thoroughly test changes in a staging environment that mirrors production.

Be rollback-ready: Have a rollback plan ready to revert quickly if problems arise.

Monitoring and alerts: Set up monitoring and alerts to catch issues 24/7.

Communication: Ensure clear communication among team members regarding deployment schedules and procedures.

Scheduled deployments: If you’re a team who doesn’t regularly ship at the end of the week, consider READ-ONLY Fridays. Or if necessary, schedule Friday deployments for the morning or early afternoon.

Weekend on-call: Consider a weekend on-call rotation to address critical issues.

Post-deployment review: Analyze and learn from each deployment's challenges to improve processes.

Plan thoroughly: Ensure deployment processes are well-documented and communication is clear across teams and stakeholders.

Evaluate risks: Assess potential business and user impact to determine deployment frequency and timing.


A nightmare come true: getting hacked

Realizing you've been hacked is a heart-stopping event, but even the most tech-savvy developers are vulnerable to attacks. Before it happens to you, remember to implement these best practices.

Keep your systems and software up-to-date: Think of it as patching vulnerabilities in your code.

Use strong passwords: Just like strong encryption, use robust passwords.

Use two-factor authentication: Always add a second layer of security.

Beware of phishermen: Don't take the bait. Be as cautious with suspicious emails as you are with untested code.

Perform security audits: Regularly audit your systems for vulnerabilities, like running code reviews but for your cybersecurity.

Backup plan: Just like version control, maintain backups. They're your safety net in case things go full horror-movie.


The horror: third party data breaches

Data breaches are arguably the most terrifying yet plausible threat to developer happiness. No company wants to be associated with them, let alone the dev who chose the service or API to work with. Here are some tips for minimizing issues with third party vendors to help you avoid this scenario.

Perform due diligence on third-party vendors: Before working with a third-party vendor, carefully review their security practices and policies. Ask about security certifications, vulnerability management practices, and their incident response plan.

Require vendors to comply with security requirements: Create or add your input in a written contract with each third-party vendor that outlines the security requirements that the vendor must meet. This contract should include requirements for data encryption, access control, and incident reporting.

Monitor vendor activity: Ensure vendors comply with the security requirements in the contract by reviewing audit logs and conducting security assessments. Only grant access to data that a vendor needs to perform their job duties to help to minimize the impact of a data breach if the vendor is compromised.

Implement strong security controls: Within your own systems, protect data from unauthorized access through firewalls, intrusion detection systems, and data encryption.

Be wary of third-party APIs: Vet all security risks. Carefully review the API documentation to understand the permissions that are required and to ensure the API uses strong security practices.

Use secure coding practices: Use input validation, escaping output, and strong cryptography.

Keep software up to date: Always update with the latest security patch to help to protect against known vulnerabilities.


Creepin' it real

It’s easy to get spooked knowing what can go wrong, but by implementing these best practices, the chance of your work going awry goes down significantly.

What other spine-chilling developer challenges have you experienced? Share them with the community.

Meta built threads in only 5 months using Jetpack Compose

Posted by Yasmine Evjen – Product Manager, and Florina Muntenescu – Developer Relations Engineer

Following its release in July of 2023, Meta’s Threads became the most rapidly downloaded app ever with over 100 million downloads in its first week. Meta created the new text-based social media platform as a place to build connections and have meaningful conversations. To ensure the app was set up for success at its release and into the future, Threads developers used Jetpack Compose, Android’s modern declarative toolkit for building UI.

An easier way to build UI with Jetpack Compose

Threads is built on top of existing code from its sister app Instagram, which uses Views for its UI development. After positive reports from other Android developers about Compose, and following internal testing and an assessment of the toolkit’s benefits, Threads engineers opted to build the all-new app from scratch using Compose. By using Compose, the team could move faster and better prepare the app for any future updates.

“We decided Jetpack Compose would be our target UI framework going forward,” said Richard Zadorozny, a software engineer at Threads. “We wanted to build the new app UI from scratch using Compose because it would enable us to move faster than refactoring a large application like Instagram.”

Even though most of Threads’ engineers had no prior experience using Compose, they found it easy to get started and learn the new toolkit. With Compose, Threads engineers built and shipped the app in only five months. This greatly exceeded the team’s speed expectations for developing a high-quality Android application — especially of this complexity and scale. The team attributes much of this speed to the flexibility and decoupling Compose provided.

Compose helped Threads engineers streamline the development of new product features. The modular nature of the toolkit let Threads developers iterate on the app as it evolved and teed up the app’s architecture for future development. Compose also helped engineers build user-friendly features that adhered to Material Design guidelines.

Threads was built and shipped in five months, exceeding our speed expectations for building a high-quality Android app of this complexity and scale.”  — Richard Zadorozny, software engineer at Threads

Going all in with Compose

Threads engineers developed almost all of the app’s surfaces using Compose. In the end, they built over 90% of Threads using Compose, including the app’s activity feed, navigation, search, profiles, onboarding page, shared element transitions, media viewer, settings, and more.

While Compose did mostly everything Threads engineers needed it to, it was still easy for them to interoperate with Views as necessary. They used Views for Threads’ videos and the media picker that’s available when creating a new post.

Compose provides modern APIs that ship directly with an app. Because of this, Threads engineers spent less time worrying about backward compatibility, missing features, or differing functionality between different versions of Android. Instead, they could focus their energy on developing a high-quality application.

“Compose’s design encourages a modular, plug-in approach to development,” said Richard. “Modifiers make all sorts of functionality inherently reusable, so you no longer have to subclass complicated ViewGroups or lump all sorts of logic into one place.”

Moving image shows Jetpack Compose/Modifiers code appears on screen

The Threads team used Modifiers for the app’s custom click behaviors and its thread line illustration that appears on the left side of posts. Modifiers also allowed Threads developers to easily add the app’s branding to any elements and ensured they were properly aligned on-screen.

Threads engineers also ensured the app was ready for users across platforms at launch. That meant making sure Threads resizes to work on different devices, like large screens and foldables. The adaptive layouts Compose offers ensure an app responds properly to different screen sizes, orientations, and form factors. This made it easier for the Threads app to “just work” for configuration changes, according to Richard.

For developers who are building new apps, I would definitely recommend using Compose. Not only is it enjoyable… it sets your team up for success in the future.” — Richard Zadorozny, software engineer at Threads

Compose is the ‘future’ of Android UI

Compose offered Threads developers an easier way to design and create UI while preparing the app’s architecture for the future. With its intuitive composables and modern declarative framework, Compose made end-to-end development smooth and gave Threads developers confidence that updating the app would be easy.

Given the positive results the team saw with the release of Threads, Meta plans to expand its use of Compose to some of Instagram’s most important surfaces, like the app’s main feed.

“It’s reached a point where Jetpack Compose can do almost everything you’ll need, and its modular nature makes it easy to make most of the changes you would need to fill the gaps,” said Richard. “I believe Compose is the future of Android UI development, and it’s just fun!”

Get started

Optimize your UI development with Jetpack Compose.

Make the passkey endpoints well-known URL part of your passkey implementation

Posted by Amy Zeppenfeld – Developer Relations Engineer

Passkeys are leading the charge towards a more secure future without passwords. Passkeys are a new type of cryptographic credential that leverages FIDO2 and WebAuthn to provide an authentication mechanism that is phishing-resistant, user friendly, simple to implement, and more secure than password-based authentication. Most major operating systems and browsers now feature full passkey support. Passkeys are expected to replace passwords as the predominant authentication mechanism in the not-too-distant future, and developers are advised to begin implementing passkey-enabled authentication solutions today.

As you implement passkeys in your app or web service, take a moment to implement a passkey endpoints well-known URL.

This is a standardized way to advertise your support for passkeys and optimize user experience. This well-known URL will allow third party services like password managers, passkey providers, and other security tools to direct users to enroll and manage their passkeys for any site that supports them. You can use app-links or deep linking with the passkey-endpoints well-known URL to allow these pages to open directly in your app.

Password management tool usage has been steadily rising, and we expect most providers will integrate passkey management as well. You can allow third party tools and services to direct your users to your dedicated passkey management page by implementing the passkey-endpoints well-known URL.

The best part is that in most cases you can implement this feature in two hours or less! All you need to do is host a simple schema on your site. Check out the example below:

  1. For a web service at https://example.com, the well-known URLwould 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" }

Note: You can decide the exact value of the URLs for both enroll and manage based on your website’s own configuration.

If you have a mobile app, we strongly recommend utilizing deep linking to have these URLs open the corresponding screen for each activity directly in your app to “enroll” or “manage” passkeys. This will keep your users focused and on track to enroll into passkeys.

And that’s it!

Further details and examples can be found in the passkey endpoints well-known URL explainer.

Updates to Google Identity Services (GIS) and migration to the Credential Manager API

Posted by Kateryna Semenova – Developer Relations Engineer, Diego Zavala and Gina Biernacki – Product Managers

Introducing Credential Manager

At Google, we are dedicated to improving the sign in experience across platforms for developers and users. For Android developers, we recently announced the public availability of Credential Manager as the future of authentication on Android. Credential Manager is a new Jetpack library designed to consolidate authentication types for Android developers into a single UI, reducing complexity for your applications while increasing usability. Credential Manager also supports passkeys, creating a unified interface for users and a single API for developers.

Instead of having to integrate with multiple identity providers, developers can now use Credential Manager as a single, unified authentication API. Credential Manager simplifies integration and makes it easier to develop authentication solutions that can work with all password managers, identity providers, and authentication methods.

Implementing Credential Manager with your Android applications will provide a single authentication experience for all Android users, integrated directly with the operating system and aligned with high-trust surfaces such as system login. We encourage all developers to migrate to Credential Manager.

Authentication APIs moving from Google Identity Services to Credential Manager on Android

The authentication APIs from Google Identity Services on Android—which include One Tap sign-in, Credential Saving, Sign in with Google button and Sign-In for Android(GSI) — can all now be implemented using Credential Manager. This enables developers to integrate with a single API for their authentication journeys.

Since these APIs are now generally available in Credential Manager, these individual APIs will be deprecated in Google Identity Services.

Removal of Smart Lock for Passwords

Smart Lock for Passwords, which was deprecated in 2022, will be removed from the Google Play Services SDK in November 2023. To minimize breaking changes that may impact existing integrations, all existing apps in the Play Store will continue to work. New app versions compiled with the new SDK will not be able to access the Smart Lock for Password API, so we encourage all developers to migrate to Credential Manager as soon as possible.

Get started with your migration to Credential Manager

All Android developers should plan their migration to the new Credential Manager API. To assist you in this process, read the following guides and resources:

Share your feedback

We are excited to improve Android authentication with the launch of Credential Manager API, delivering a simple and streamlined UX for secure sign-in methods such as Sign in with Google.

We value your feedback and invite you to share your experience integrating with Credential Manager or any other feedback you might have:

Updates to Google Identity Services (GIS) and migration to the Credential Manager API

Posted by Kateryna Semenova – Developer Relations Engineer, Diego Zavala and Gina Biernacki – Product Managers

Introducing Credential Manager

At Google, we are dedicated to improving the sign in experience across platforms for developers and users. For Android developers, we recently announced the public availability of Credential Manager as the future of authentication on Android. Credential Manager is a new Jetpack library designed to consolidate authentication types for Android developers into a single UI, reducing complexity for your applications while increasing usability. Credential Manager also supports passkeys, creating a unified interface for users and a single API for developers.

Instead of having to integrate with multiple identity providers, developers can now use Credential Manager as a single, unified authentication API. Credential Manager simplifies integration and makes it easier to develop authentication solutions that can work with all password managers, identity providers, and authentication methods.

Implementing Credential Manager with your Android applications will provide a single authentication experience for all Android users, integrated directly with the operating system and aligned with high-trust surfaces such as system login. We encourage all developers to migrate to Credential Manager.


Authentication APIs moving from Google Identity Services to Credential Manager on Android

The authentication APIs from Google Identity Services on Android—which include One Tap sign-in, Credential Saving, Sign in with Google button and Sign-In for Android(GSI) — can all now be implemented using Credential Manager. This enables developers to integrate with a single API for their authentication journeys.

Since these APIs are now generally available in Credential Manager, these individual APIs will be deprecated in Google Identity Services.


Removal of Smart Lock for Passwords

Smart Lock for Passwords, which was deprecated in 2022, will be removed from the Google Play Services SDK in November 2023. To minimize breaking changes that may impact existing integrations, all existing apps in the Play Store will continue to work. New app versions compiled with the new SDK will not be able to access the Smart Lock for Password API, so we encourage all developers to migrate to Credential Manager as soon as possible.


Get started with your migration to Credential Manager

All Android developers should plan their migration to the new Credential Manager API. To assist you in this process, read the following guides and resources:

Share your feedback

We are excited to improve Android authentication with the launch of Credential Manager API, delivering a simple and streamlined UX for secure sign-in methods such as Sign in with Google.

We value your feedback and invite you to share your experience integrating with Credential Manager or any other feedback you might have:

Your input is very valuable as we continue to refine and improve our authentication services.

Simple and secure sign-in on Android with Credential Manager and passkeys

Posted by Diego Zavala, Product Manager

We are excited to announce that the public release of Credential Manager will be available starting on November 1st. Credential Manager brings the future of authentication to Android, simplifying how users sign in to their apps and websites, and at the same time, making it more secure.

Signing in can be challenging - passwords are widely used, and often forgotten. They are reused, phished, and washed, making them less secure. Furthermore, there is a proliferation of ways to log in to apps; passwords, email links, OTP, ‘Sign in with…’, and users carry the burden of remembering what to use where. And for developers, this adds complexity - they need to support multiple sign-in methods, increasing integration and maintenance costs.

To address this, Android is rolling out Credential Manager, which brings support for passkeys, a new passwordless authentication, together with traditional sign-in methods, such as passwords and federated identity, in a unified interface.

Let’s take a look at how it can help make users’ and developers’ lives easier.

1.    Passkeys enable passwordless authentication

Passkeys are the future of online authentication - they are more secure and convenient than passwords. With a passkey, signing in is as simple as selecting the right account and confirming with a device face scan, fingerprint or PIN - that’s it. No need to manually type username or passwords, copy-paste a one-time code from SMS, or tap a link in an email inbox. This has resulted in apps reducing the sign-in time by 50% when they implemented passkeys. Logging in with passkeys is also more secure, as they provide phishing-resistant protection.

Image showing step-by-step passwordless authentication experience to sign in to Shrine app from an Android device

Several apps are already integrated with Credential Manager and support passkeys, including Uber and Whatsapp.

“Passkeys add an additional layer of security for WhatsApp users. Simplifying the way users can securely get into their account will help our users, which is why the Credential Manager API is so important.” – Nitin Gupta, Head of Engineering, WhatsApp

“At Uber, we are relentless in our push to create magical experiences without compromising user safety. Passkeys simplify the user experience and promote accessibility, while enhancing the security that comes from reducing the dependency on traditional passwords. Ultimately this is a win-win for Uber and Uber’s customers.

The Credential Manager offers a developer-friendly suite of APIs that enable seamless integration with our apps, eliminating concerns about device fragmentation. We’ve seen great results from launching passkeys across our apps and encourage all users to adopt passkeys.”Ramsin Betyousef, Sr. Director of Engineering at Uber

2.    All accounts available in a single tap, in a simplified interface

Users often end up with different sign-in methods for the same account - they may use a password on their phone, and a “Sign in with…” on a browser, and then be offered a passkey on their desktop. To simplify users’ lives, Credential Manager lets them choose the account they want, and use smart defaults to pick the best technology to do it (e.g. a passkey, password, or federated identity). That way, users don’t need to think whether they want to sign-in with a password or a passkey; they just choose the account, and they are in.

Let’s take a look at how it works. Imagine that Elisa has 2 accounts on the Shrine app

  • a personal account for which she had a password and just created a new passkey
  • a shared family account with just a password.

To facilitate her experience, Credential Manager shows her 2 accounts and that’s it. Credential Manager uses a password for her family account and a passkey for her personal account (because it’s simpler and safer). Elisa doesn’t need to think about it.

Image showing Credential Manager on an Android device allowing user to choose a saved sign in from list of two accounts

3.    Open to the ecosystem

One of the reasons why users prefer Android is because they are able to customize their experience. In the case of authentication, some users prefer to use the password manager that’s shipped with their device, and others prefer to use a different one. Credential Manager gives users the ability to do so, by being open to any credential provider and allowing multiple enabled at the same time.

Image showing Credential Manager in app allowing user to choose a saved sign in from list of two accounts

Several leading credential providers already integrated with Credential Manager.

"We're at an inflection point in the history of authentication as passkeys represent the perfect balance between ease and security. Since 1Password launched support for passkeys earlier this year, we’ve had over 230,000 passkeys created and see thousands added each day. The data indicates strong user demand but we must continue to prioritize support for apps and services, making it simpler for developers to integrate passkey authentication." – Anna Pobletts, Head of Passwordless at 1Password

“At Enpass, we quickly recognized the potential of passkeys. Thanks to the Android Credential Manager framework, Enpass is fully prepared to serve as a passkey provider for Android 14. This integration empowers our customers to embrace a secure alternative to traditional passwords wherever it's available.” – Vinod Kumar, Chief Technology Officer at Enpass.

How to integrate with Credential Manager?

To get started, take a look at the resources below:

Simple and secure sign-in on Android with Credential Manager and passkeys

Posted by Diego Zavala, Product Manager

We are excited to announce that the public release of Credential Manager will be available starting on November 1st. Credential Manager brings the future of authentication to Android, simplifying how users sign in to their apps and websites, and at the same time, making it more secure.

Signing in can be challenging - passwords are widely used, and often forgotten. They are reused, phished, and washed, making them less secure. Furthermore, there is a proliferation of ways to log in to apps; passwords, email links, OTP, ‘Sign in with…’, and users carry the burden of remembering what to use where. And for developers, this adds complexity - they need to support multiple sign-in methods, increasing integration and maintenance costs.

To address this, Android is rolling out Credential Manager, which brings support for passkeys, a new passwordless authentication, together with traditional sign-in methods, such as passwords and federated identity, in a unified interface.

Let’s take a look at how it can help make users’ and developers’ lives easier.


1.    Passkeys enable passwordless authentication

Passkeys are the future of online authentication - they are more secure and convenient than passwords. With a passkey, signing in is as simple as selecting the right account and confirming with a device face scan, fingerprint or PIN - that’s it. No need to manually type username or passwords, copy-paste a one-time code from SMS, or tap a link in an email inbox. This has resulted in apps reducing the sign-in time by 50% when they implemented passkeys. Logging in with passkeys is also more secure, as they provide phishing-resistant protection.

Image showing step-by-step passwordless authentication experience to sign in to Shrine app from an Android device

Several apps are already integrated with Credential Manager and support passkeys, including Uber and Whatsapp.

“Passkeys add an additional layer of security for WhatsApp users. Simplifying the way users can securely get into their account will help our users, which is why the Credential Manager API is so important.” 
– Nitin Gupta, Head of Engineering, WhatsApp

 

“At Uber, we are relentless in our push to create magical experiences without compromising user safety. Passkeys simplify the user experience and promote accessibility, while enhancing the security that comes from reducing the dependency on traditional passwords. Ultimately this is a win-win for Uber and Uber’s customers.

The Credential Manager offers a developer-friendly suite of APIs that enable seamless integration with our apps, eliminating concerns about device fragmentation. We’ve seen great results from launching passkeys across our apps and encourage all users to adopt passkeys.” 

Ramsin Betyousef, Sr. Director of Engineering at Uber


2.    All accounts available in a single tap, in a simplified interface

Users often end up with different sign-in methods for the same account - they may use a password on their phone, and a “Sign in with…” on a browser, and then be offered a passkey on their desktop. To simplify users’ lives, Credential Manager lets them choose the account they want, and use smart defaults to pick the best technology to do it (e.g. a passkey, password, or federated identity). That way, users don’t need to think whether they want to sign-in with a password or a passkey; they just choose the account, and they are in.

Let’s take a look at how it works. Imagine that Elisa has 2 accounts on the Shrine app

  • a personal account for which she had a password and just created a new passkey
  • a shared family account with just a password.

To facilitate her experience, Credential Manager shows her 2 accounts and that’s it. Credential Manager uses a password for her family account and a passkey for her personal account (because it’s simpler and safer). Elisa doesn’t need to think about it.

Image showing Credential Manager on an Android device allowing user to choose a saved sign in from list of two accounts

3.    Open to the ecosystem

One of the reasons why users prefer Android is because they are able to customize their experience. In the case of authentication, some users prefer to use the password manager that’s shipped with their device, and others prefer to use a different one. Credential Manager gives users the ability to do so, by being open to any credential provider and allowing multiple enabled at the same time.

Image showing Credential Manager in app allowing user to choose a saved sign in from list of two accounts

Several leading credential providers already integrated with Credential Manager.


"We're at an inflection point in the history of authentication as passkeys represent the perfect balance between ease and security. Since 1Password launched support for passkeys earlier this year, we’ve had over 230,000 passkeys created and see thousands added each day. The data indicates strong user demand but we must continue to prioritize support for apps and services, making it simpler for developers to integrate passkey authentication." 
– Anna Pobletts, Head of Passwordless at 1Password

 

“At Enpass, we quickly recognized the potential of passkeys. Thanks to the Android Credential Manager framework, Enpass is fully prepared to serve as a passkey provider for Android 14. This integration empowers our customers to embrace a secure alternative to traditional passwords wherever it's available.” 
– Vinod Kumar, Chief Technology Officer at Enpass.


How to integrate with Credential Manager?

To get started, take a look at the resources below:

How KAYAK reduced sign in time by 50% and improved security with passkeys

Posted by Kateryna Semenova, Developer Relations Engineer, Android

Introduction

KAYAK is one of the world's leading travel search engines that helps users find the best deals on flights, hotels, and rental cars. In 2023, KAYAK integrated passkeys - a new type of passwordless authentication - into its Android and web apps. As a result, KAYAK reduced the average time it takes their users to sign-up and sign-in by 50%, and also saw a decrease in support tickets.

This case study explains KAYAK's implementation on Android with Credential Manager API and RxJava. You can use this case study as a model for implementing Credential Manager to improve security and user experience in your own apps.

If you want a quick summary, check out the companion video on YouTube.


Problem

Like most businesses, KAYAK has relied on passwords in the past to authenticate users. Passwords are a liability for both users and businesses alike: they're often weak, reused, guessed, phished, leaked, or hacked.

“Offering password authentication comes with a lot of effort and risk for the business. Attackers are constantly trying to brute force accounts while not all users understand the need for strong passwords. However, even strong passwords are not fully secure and can still be phished.” – Matthias Keller, Chief Scientist and SVP, Technology at KAYAK

To make authentication more secure, KAYAK sent "magic links" via email. While helpful from a security standpoint, this extra step introduced more user friction by requiring users to switch to a different app to complete the login process. Additional measures needed to be introduced to mitigate the risk of phishing attacks.


Solution

KAYAK's Android app now uses passkeys for a more secure, user-friendly, and faster authentication experience. Passkeys are unique, secure tokens that are stored on the user's device and can be synchronized across multiple devices. Users can sign in to KAYAK with a passkey by simply using their existing device's screen lock, making it simpler and more secure than entering a password.

“We've added passkeys support to our Android app so that more users can use passkeys instead of passwords. Within that work, we also replaced our old Smartlock API implementation with the Sign in with Google supported by Credential Manager API. Now, users are able to sign up and sign in to KAYAK with passkeys twice as fast as with an email link, which also improves the completion rate" – Matthias Keller, Chief Scientist and SVP, Technology at KAYAK


Credential Manager API integration

To integrate passkeys on Android, KAYAK used the Credential Manager API. Credential Manager is a Jetpack library that unifies passkey support starting with Android 9 (API level 28) and support for traditional sign-in methods such as passwords and federated authentication into a single user interface and API.

Image of Credential Manager's passkey creation screen.
Figure 1: Credential Manager's passkey creation screens.

Designing a robust authentication flow for apps is crucial to ensure security and a trustworthy user experience. The following diagram demonstrates how KAYAK integrated passkeys into their registration and authentication flows:

Flow diagram of KAYAK's registration and authentication processes
Figure 2:KAYAK's diagram showing their registration and authentication flows.

At registration time, users are given the opportunity to create a passkey. Once registered, users can sign in using their passkey, Sign in with Google, or password. Since Credential Manager launches the UI automatically, be careful not to introduce unexpected wait times, such as network calls. Always fetch a one-time challenge and other passkeys configuration (such as RP ID) at the beginning of any app session.

While the KAYAK team is now heavily invested in coroutines, their initial integration used RxJava to integrate with the Credential Manager API. They wrapped Credential Manager calls into RxJava as follows:

override fun createCredential(request: CreateCredentialRequest, activity: Activity): Single<CreateCredentialResponse> { return Single.create { emitter -> // Triggers credential creation flow credentialManager.createCredentialAsync( request = request, activity = activity, cancellationSignal = null, executor = Executors.newSingleThreadExecutor(), callback = object : CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException> { override fun onResult(result: CreateCredentialResponse) { emitter.onSuccess(result) } override fun onError(e: CreateCredentialException) { emitter.tryOnError(e) } } ) } }

This example defines a Kotlin function called createCredential() that returns a credential from the user as an RxJava Single of type CreateCredentialResponse. The createCredential() function encapsulates the asynchronous process of credential registration in a reactive programming style using the RxJava Single class.

For a Kotlin implementation of this process using coroutines, read the Sign in your user with Credential Manager guide.

New user registration sign-up flow

This example demonstrates the approach KAYAK used to register a new credential, here Credential Manager was wrapped in Rx primitives.


webAuthnRetrofitService .getClientParams(username = /** email address **/) .flatMap { response -> // Produce a passkeys request from client params that include a one-time challenge CreatePublicKeyCredentialOption(/** produce JSON from response **/) } .subscribeOn(schedulers.io()) .flatMap { request -> // Call the earlier defined wrapper which calls the Credential Manager UI // to register a new passkey credential credentialManagerRepository .createCredential( request = request, activity = activity ) } .flatMap { // send credential to the authentication server } .observeOn(schedulers.main()) .subscribe( { /** process successful login, update UI etc. **/ }, { /** process error, send to logger **/ } )

Rx allowed KAYAK to produce more complex pipelines that can involve multiple interactions with Credential Manager.

Existing user sign-in

KAYAK used the following steps to launch the sign-in flow. The process launches a bottom sheet UI element, allowing the user to log in using a Google ID and an existing passkey or saved password.

Image of bottom sheet for passkey authentication
Figure 3:Bottom sheet for passkey authentication.

Developers should follow these steps when setting up a sign-in flow:

  1. Since the bottom sheet is launched automatically, be careful not to introduce unexpected wait times in the UI, such as network calls. Always fetch a one-time challenge and other passkeys configuration (such as RP ID) at the beginning of any app session.
  2. When offering Google sign-in via Credential Manager API, your code should initially look for Google accounts that have already been used with the app. To handle this, call the API with the setFilterByAuthorizedAccounts parameter set to true.
  3. If the result returns a list of available credentials, the app shows the bottom sheet authentication UI to the user.
  4. If a NoCredentialException appears, no credentials were found: No Google accounts, no passkeys, and no saved passwords. At this point, your app should call the API again and set setFilterByAuthorizedAccounts to false to initiate the Sign up with Google flow.
  5. Process the credential returned from Credential Manager.
Single.fromSupplier<GetPublicKeyCredentialOption> { GetPublicKeyCredentialOption(/** Insert challenge and RP ID that was fetched earlier **/) } .flatMap { response -> // Produce a passkeys request GetPublicKeyCredentialOption(response.toGetPublicKeyCredentialOptionRequest()) } .subscribeOn(schedulers.io()) .map { publicKeyCredentialOption -> // Merge passkeys request together with other desired options, // such as Google sign-in and saved passwords. } .flatMap { request -> // Trigger Credential Manager system UI credentialManagerRepository.getCredential( request = request, activity = activity ) } .onErrorResumeNext { throwable -> // When offering Google sign-in, it is recommended to first only look for Google accounts // that have already been used with our app. If there are no such Google accounts, no passkeys, // and no saved passwords, we try looking for any Google sign-in one more time. if (throwable is NoCredentialException) { return@onErrorResumeNext credentialManagerRepository.getCredential( request = GetCredentialRequest(/* Google ID with filterByAuthorizedOnly = false */), activity = activity ) } Single.error(throwable) } .flatMapCompletable { // Step 1: Use Retrofit service to send the credential to the server for validation. Waiting // for the server is handled on a IO thread using subscribeOn(schedulers.io()). // Step 2: Show the result in the UI. This includes changes such as loading the profile // picture, updating to the personalized greeting, making member-only areas active, // hiding the sign-in dialog, etc. The activities of step 2 are executed on the main thread. } .observeOn(schedulers.main()) .subscribe( // Handle errors, e.g. send to log ingestion service. // A subset of exceptions shown to the user can also be helpful, // such as user setup problems. // Check out more info in Troubleshoot common errors at // https://developer.android.com/training/sign-in/passkeys#troubleshoot )


“Once the Credential Manager API is generally implemented, it is very easy to add other authentication methods. Adding Google One-Tap Sign In was almost zero work after adding passkeys.” – Matthias Keller

To learn more, follow the guide on how to Integrate Credentials Manager API and how to Integrate Credential Manager with Sign in with Google.


UX considerations

Some of the major user experience considerations KAYAK faced when switching to passkeys included whether users should be able to delete passkeys or create more than one passkey.

Our UX guide for passkeys recommends that you have an option to revoke a passkey, and that you ensure that the user does not create duplicate passkeys for the same username in the same password manager.

Image of KAYAK's UI for passkey management
Figure 4:KAYAK's UI for passkey management.

To prevent registration of multiple credentials for the same account, KAYAK used the excludeCredentials property that lists credentials already registered for the user. The following example demonstrates how to create new credentials on Android without creating duplicates:


fun WebAuthnClientParamsResponse.toCreateCredentialRequest(): String { val credentialRequest = WebAuthnCreateCredentialRequest( challenge = this.challenge!!.asSafeBase64, relayingParty = this.relayingParty!!, pubKeyCredParams = this.pubKeyCredParams!!, userEntity = WebAuthnUserEntity( id = this.userEntity!!.id.asSafeBase64, name = this.userEntity.name, displayName = this.userEntity.displayName ), authenticatorSelection = WebAuthnAuthenticatorSelection( authenticatorAttachment = "platform", residentKey = "preferred" ), // Setting already existing credentials here prevents // creating multiple passkeys on the same keychain/password manager excludeCredentials = this.allowedCredentials!!.map { it.copy(id = it.id.asSafeBase64) }, ) return GsonBuilder().disableHtmlEscaping().create().toJson(credentialRequest) }

And this is how KAYAK implemented excludeCredentials functionality for their Web implementation.

var registrationOptions = { 'publicKey': { 'challenge': self.base64ToArrayBuffer(data.challenge), 'rp': data.rp, 'user': { 'id': new TextEncoder().encode(data.user.id), 'name': data.user.name, 'displayName': data.user.displayName }, 'pubKeyCredParams': data.pubKeyCredParams, 'authenticatorSelection': { 'residentKey': 'required' } } }; if (data.allowCredentials && data.allowCredentials.length > 0) { var excludeCredentials = []; for (var i = 0; i < data.allowCredentials.length; i++) { excludeCredentials.push({ 'id': self.base64ToArrayBuffer(data.allowCredentials[i].id), 'type': data.allowCredentials[i].type }); } registrationOptions.publicKey.excludeCredentials = excludeCredentials; } navigator.credentials.create(registrationOptions);

Server-side implementation

The server-side part is an essential component of an authentication solution. KAYAK added passkey capabilities to their existing authentication backend by utilizing WebAuthn4J, an open source Java library.

KAYAK broke down the server-side process into the following steps:

  1. The client requests parameters needed to create or use a passkey from the server. This includes the challenge, the supported encryption algorithm, the relying party ID, and related items. If the client already has a user email address, the parameters will include the user object for registration, and a list of passkeys if any exist.
  2. The client runs browser or app flows to start passkey registration or sign-in.
  3. The client sends retrieved credential information to the server. This includes client ID, authenticator data, client data, and other related items. This information is needed to create an account or verify a sign-in.

When KAYAK worked on this project, no third-party products supported passkeys. However, many resources are now available for creating a passkey server, including documentation and library examples.


Results

Since integrating passkeys, KAYAK has seen a significant increase in user satisfaction. Users have reported that they find passkeys to be much easier to use than passwords, as they do not require users to remember or type in a long, complex string of characters. KAYAK reduced the average time it takes their users to sign-up and sign-in by 50%, have seen a decrease in support tickets related to forgotten passwords, and have made their system more secure by reducing their exposure to password-based attacks. Thanks to these improvements, ​​KAYAK plans to eliminate password-based authentication in their app by the end of 2023.

“Passkeys make creating an account lightning fast by removing the need for password creation or navigating to a separate app to get a link or code. As a bonus, implementing the new Credential Manager library also reduced technical debt in our code base by putting passkeys, passwords and Google sign-in all into one new modern UI. Indeed, users are able to sign up and sign in to KAYAK with passkeys twice as fast as with an email link, which also improves the completion rate." – Matthias Keller


Conclusion

Passkeys are a new and innovative authentication solution that offers significant benefits over traditional passwords. KAYAK is a great example of how an organization can improve the security and usability of its authentication process by integrating passkeys. If you are looking for a more secure and user-friendly authentication experience, we encourage you to consider using passkeys with Android's Credential Manager API.