CINXE.COM

How KAYAK reduced sign in time by 50% and improved security with passkeys - Google Developers Blog

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title> How KAYAK reduced sign in time by 50% and improved security with passkeys - Google Developers Blog </title> <meta property="og:title" content="How KAYAK reduced sign in time by 50% and improved security with passkeys- Google Developers Blog" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="This case study explains KAYAK&#x27;s implementation on Android with Credential Manager API and RxJava." /> <meta content="summary_large_image" name="twitter:card"/> <meta content="Google for Developers Blog - News about Web, Mobile, AI and Cloud" property="twitter:title"/> <meta property="og:title" content="How KAYAK reduced sign in time by 50% and improved security with passkeys" /> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{ "@type": "ListItem", "position": 1, "name": "Google for Developers Blog", "item": "https://developers.googleblog.com/" },{ "@type": "ListItem", "position": 2, "name": "How KAYAK reduced sign in time by 50% and improved security with passkeys", "item": "http://developers.googleblog.com/en/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" }] } </script> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "headline": "How KAYAK reduced sign in time by 50% and improved security with passkeys", "description": "This case study explains KAYAK&#x27;s implementation on Android with Credential Manager API and RxJava.", "image": "https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/45.2e16d0ba.fill-800x400.png", "datePublished": "2023-10-24", "author": [ { "@type": "Person", "name": "Kateryna Semenova", "url": "/en/search/?author=Kateryna+Semenova" } ] } </script> <meta content="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/45.2e16d0ba.fill-1200x600.png" property="og:image"/> <!-- Google Tag Manager --> <script type="text/javascript" src="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/js/analytics.js"></script> <!-- End Google Tag Manager --> <link href="//www.gstatic.com/glue/v27_1/glue.min.css" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/css/dgc_blog.css"> <link rel="icon" href="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/images/favicon.ico" type="image/x-icon"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preload" href="https://fonts.googleapis.com/css2?family=Product+Sans&family=Google+Sans+Display:ital@0;1&family=Google+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Google+Sans+Text:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap" as="style"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Product+Sans&family=Google+Sans+Display:ital@0;1&family=Google+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Google+Sans+Text:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap"> <link href="https://www.gstatic.com/glue/cookienotificationbar/cookienotificationbar.min.css" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/css/blog_detail.css" /> <link rel="stylesheet" href="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/pygmentify/css/default.min.css"> </head> <body id="main-content" class="glue-body "> <!-- Google Tag Manager (noscript) --> <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WVTLDSL " height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> <!-- End Google Tag Manager (noscript) --> <!-- HTML --> <header class="dgc-header"> <div class="dgc-header-inner"> <button class="hamburger" aria-haspopup="true" aria-expanded="false" aria-label="Open Menu"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#menu"></use> </svg> </button> <div class="product-name-wrapper"> <a href="https://developers.google.com/" class="site-logo-link" data-label="Site logo"> <img src="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/images/g-dev.svg" class="site-logo" alt="Google for Developers"> </a> </div> <div class="desktop-nav-wrapper"> <div class="upper-tabs-wrapper"> <div class="upper-tabs"> <nav class="tabs" aria-label="Upper Tabs"> <div class="tab" dropdown> <a href="//developers.google.com/products" class="top-nav-title"> Products </a> <a href="#" role="button" aria-haspopup="true" aria-expanded="true" aria-label="Dropdown menu for Products" class="dropdown-toggle"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#arrow-drop-down"></use> </svg> </a> <div class="tabs-dropdown" aria-label="Submenu"> <div class="tabs-dropdown-content"> <div class="tabs-dropdown-column"> <ul class="tabs-dropdown-section"> <li class="nav-title" aria-label="Develop Menu">Develop</li> <li class="nav-item"> <a href="//developer.android.com"> <div class="nav-item-title"> Android </div> </a> </li> <li class="nav-item"> <a href="//developer.chrome.com"> <div class="nav-item-title"> Chrome </div> </a> </li> <li class="nav-item"> <a href="//chromeos.dev/"> <div class="nav-item-title"> ChromeOS </div> </a> </li> <li class="nav-item"> <a href="//cloud.google.com"> <div class="nav-item-title"> Cloud </div> </a> </li> <li class="nav-item"> <a href="//firebase.google.com"> <div class="nav-item-title"> Firebase </div> </a> </li> <li class="nav-item"> <a href="//flutter.dev"> <div class="nav-item-title"> Flutter </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/assistant"> <div class="nav-item-title"> Google Assistant </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/maps"> <div class="nav-item-title"> Google Maps Platform </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/workspace"> <div class="nav-item-title"> Google Workspace </div> </a> </li> <li class="nav-item"> <a href="//www.tensorflow.org"> <div class="nav-item-title"> TensorFlow </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/youtube"> <div class="nav-item-title"> YouTube </div> </a> </li> </ul> </div> <div class="tabs-dropdown-column"> <ul class="tabs-dropdown-section"> <li class="nav-title" aria-label="Grow Menu">Grow</li> <li class="nav-item"> <a href="//firebase.google.com"> <div class="nav-item-title"> Firebase </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/google-ads"> <div class="nav-item-title"> Google Ads </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/analytics"> <div class="nav-item-title"> Google Analytics </div> </a> </li> <li class="nav-item"> <a href="//developer.android.com/distribute"> <div class="nav-item-title"> Google Play </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/search"> <div class="nav-item-title"> Search </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/web/fundamentals/engage-and-retain/push-notifications"> <div class="nav-item-title"> Web Push and Notification APIs </div> </a> </li> </ul> </div> <div class="tabs-dropdown-column"> <ul class="tabs-dropdown-section"> <li class="nav-title" aria-label="Earn Menu">Earn</li> <li class="nav-item"> <a href="//developers.google.com/admob"> <div class="nav-item-title"> AdMob </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/google-ads/api"> <div class="nav-item-title"> Google Ads API </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/pay"> <div class="nav-item-title"> Google Pay </div> </a> </li> <li class="nav-item"> <a href="//developer.android.com/google/play/billing/"> <div class="nav-item-title"> Google Play Billing </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/interactive-media-ads"> <div class="nav-item-title"> Interactive Media Ads </div> </a> </li> </ul> </div> </div> </div> </div> <div class="tab"> <a href="//developers.google.com/solutions/catalog" class="top-nav-title"> Solutions </a> </div> <div class="tab"> <a href="//developers.google.com/events" class="top-nav-title"> Events </a> </div> <div class="tab"> <a href="//developers.google.com/learn" class="top-nav-title"> Learn </a> </div> <div class="tab" dropdown> <a href="//developers.google.com/community" class="top-nav-title"> Community </a> <a href="#" role="button" aria-haspopup="true" aria-expanded="true" aria-label="Dropdown menu for Community" class="dropdown-toggle"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#arrow-drop-down"></use> </svg> </a> <div class="tabs-dropdown" aria-label="Submenu"> <div class="tabs-dropdown-content"> <div class="tabs-dropdown-column"> <ul class="tabs-dropdown-section"> <li class="nav-title" aria-label="Groups Menu">Groups</li> <li class="nav-item"> <a href="//developers.google.com/community/gdg"> <div class="nav-item-title"> Google Developer Groups </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/gdsc"> <div class="nav-item-title"> Google Developer Student Clubs </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/womentechmakers"> <div class="nav-item-title"> Woman Techmakers </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/experts"> <div class="nav-item-title"> Google Developer Experts </div> </a> </li> <li class="nav-item"> <a href="//www.techequitycollective.com/"> <div class="nav-item-title"> Tech Equity Collective </div> </a> </li> </ul> </div> <div class="tabs-dropdown-column"> <ul class="tabs-dropdown-section"> <li class="nav-title" aria-label="Programs Menu">Programs</li> <li class="nav-item"> <a href="//developers.google.com/community/accelerators"> <div class="nav-item-title"> Accelerator </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/gdsc-solution-challenge"> <div class="nav-item-title"> Solution Challenge </div> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/devfest"> <div class="nav-item-title"> DevFest </div> </a> </li> </ul> </div> <div class="tabs-dropdown-column"> <ul class="tabs-dropdown-section"> <li class="nav-title" aria-label="Stories Menu">Stories</li> <li class="nav-item"> <a href="//developers.google.com/community/stories"> <div class="nav-item-title"> All Stories </div> </a> </li> </ul> </div> </div> </div> </div> <div class="tab"> <a href="//developers.google.com/profile/u/me" class="top-nav-title"> Developer Program </a> </div> <div class="tab"> <a href="//developers.googleblog.com/" class="top-nav-title"> Blog </a> </div> </nav> </div> </div> </div> </div> <div class="dgc-header-search"> <div class="search-wrapper glue-page"> <div class="glue-grid"> <form id="search-form" action="/en/search/" method="get" class="search-content glue-grid__col glue-grid__col--span-4-sm glue-grid__col--span-9-md glue-grid__col--span-7-lg"> <div class="search-input-wrapper"> <svg role="presentation" aria-hidden="true" class="glue-icon search-icon"> <use href="/glue-icon/#search"></use> </svg> <input type="text" name="query" placeholder="Search all articles..." aria-label="Search" class="search-input-field" /> </div> <button class="glue-button glue-button--high-emphasis"> Search </button> </form> <div class="language-selector glue-grid__col glue-grid__col--span-4-sm glue-grid__col--span-3-md glue-grid__col--span-5-lg" aria-label="Select your language preference"> <button type="button" aria-controls="lang-menu-header" aria-haspopup="true"> <svg role="presentation" aria-hidden="true" class="glue-icon lang-icon"> <use href="/glue-icon/#language"></use> </svg> <span class="lang-label">English</span> <svg role="presentation" aria-hidden="true" class="glue-icon lang-icon"> <use href="/glue-icon/#arrow-drop-down"></use> </svg> </button> <div id="lang-menu-header" class="lang-menu hidden" role="menu"> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" aria-current="true" href="/en/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">English</a> </li> <li role="presentation"> <a role="menuitem" lang="es" href="/es/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">Español (Latam)</a> </li> <li role="presentation"> <a role="menuitem" lang="id" href="/id/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">Bahasa Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" href="/ja/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" href="/ko/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">한국어</a> </li> <li role="presentation"> <a role="menuitem" lang="pt-br" href="/pt-br/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">Português (Brasil)</a> </li> <li role="presentation"> <a role="menuitem" lang="zh-hans" href="/zh-hans/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">简体中文</a> </li> </ul> </div> </div> </div> </div> </div> </header> <div class="mobile-drawer" top-level-nav> <nav class="nav-content" aria-label="Side menu"> <div class="mobile-header"> <button class="nav-close-btn nav-btn" aria-label="Close navigation"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#close"></use> </svg> </button> <button class="nav-back-btn nav-btn hidden" aria-label="Back to Menu"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#arrow-back"></use> </svg> </button> <div class="product-name-wrapper"> <a href="https://developers.google.com/" class="site-logo-link" data-label="Site logo"> <img src="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/images/g-dev.svg" class="site-logo" alt="Google for Developers"> </a> </div> </div> <div class="nav-wrapper"> <div class="mobile-nav-top"> <ul class="nav-list"> <li class="nav-item"> <a href="//developers.google.com/products" class="nav-title" data-label="Tab: Products"> <span class="nav-text" tooltip=""> Products </span> </a> <ul class="nav-responsive-tabs"> <li class="nav-item"> <span tabindex="0" class="nav-title products-btn" data-label="More Products"> <span class="nav-text" menu="Products"> More </span> <span class="nav-icon" menu="Products"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#arrow-forward"></use> </svg> </span> </span> </li> </ul> </li> <li class="nav-item"> <a href="//developers.google.com/solutions/catalog" class="nav-title" data-label="Tab: Solutions"> <span class="nav-text" tooltip=""> Solutions </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/events" class="nav-title" data-label="Tab: Events"> <span class="nav-text" tooltip=""> Events </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/learn" class="nav-title" data-label="Tab: Learn"> <span class="nav-text" tooltip=""> Learn </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community" class="nav-title" data-label="Tab: Community"> <span class="nav-text" tooltip=""> Community </span> </a> <ul class="nav-responsive-tabs"> <li class="nav-item"> <span tabindex="0" class="nav-title community-btn" data-label="More Community"> <span class="nav-text" menu="Community"> More </span> <span class="nav-icon" menu="Community"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#arrow-forward"></use> </svg> </span> </span> </li> </ul> </li> <li class="nav-item"> <a href="//developers.google.com/profile/u/me" class="nav-title" data-label="Tab: Developer Program"> <span class="nav-text" tooltip=""> Developer Program </span> </a> </li> <li class="nav-item"> <a href="//developers.googleblog.com/" class="nav-title" data-label="Tab: Blog"> <span class="nav-text" tooltip=""> Blog </span> </a> </li> </ul> </div> <div class="mobile-nav-bottom"> <ul class="nav-list products-submenu" menu="Products" aria-label="Side menu"> <li class="nav-item nav-heading"> <span class="nav-title"> <span class="nav-text"> Develop </span> </span> </li> <li class="nav-item"> <a href="//developer.android.com" class="nav-title"> <span class="nav-text"> Android </span> </a> </li> <li class="nav-item"> <a href="//developer.chrome.com" class="nav-title"> <span class="nav-text"> Chrome </span> </a> </li> <li class="nav-item"> <a href="//chromeos.dev/" class="nav-title"> <span class="nav-text"> ChromeOS </span> </a> </li> <li class="nav-item"> <a href="//cloud.google.com" class="nav-title"> <span class="nav-text"> Cloud </span> </a> </li> <li class="nav-item"> <a href="//firebase.google.com" class="nav-title"> <span class="nav-text"> Firebase </span> </a> </li> <li class="nav-item"> <a href="//flutter.dev" class="nav-title"> <span class="nav-text"> Flutter </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/assistant" class="nav-title"> <span class="nav-text"> Google Assistant </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/maps" class="nav-title"> <span class="nav-text"> Google Maps Platform </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/workspace" class="nav-title"> <span class="nav-text"> Google Workspace </span> </a> </li> <li class="nav-item"> <a href="//www.tensorflow.org" class="nav-title"> <span class="nav-text"> TensorFlow </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/youtube" class="nav-title"> <span class="nav-text"> YouTube </span> </a> </li> <li class="nav-item nav-heading"> <span class="nav-title"> <span class="nav-text"> Grow </span> </span> </li> <li class="nav-item"> <a href="//firebase.google.com" class="nav-title"> <span class="nav-text"> Firebase </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/google-ads" class="nav-title"> <span class="nav-text"> Google Ads </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/analytics" class="nav-title"> <span class="nav-text"> Google Analytics </span> </a> </li> <li class="nav-item"> <a href="//developer.android.com/distribute" class="nav-title"> <span class="nav-text"> Google Play </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/search" class="nav-title"> <span class="nav-text"> Search </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/web/fundamentals/engage-and-retain/push-notifications" class="nav-title"> <span class="nav-text"> Web Push and Notification APIs </span> </a> </li> <li class="nav-item nav-heading"> <span class="nav-title"> <span class="nav-text"> Earn </span> </span> </li> <li class="nav-item"> <a href="//developers.google.com/admob" class="nav-title"> <span class="nav-text"> AdMob </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/google-ads/api" class="nav-title"> <span class="nav-text"> Google Ads API </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/pay" class="nav-title"> <span class="nav-text"> Google Pay </span> </a> </li> <li class="nav-item"> <a href="//developer.android.com/google/play/billing/" class="nav-title"> <span class="nav-text"> Google Play Billing </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/interactive-media-ads" class="nav-title"> <span class="nav-text"> Interactive Media Ads </span> </a> </li> </ul> <ul class="nav-list community-submenu hidden" menu="Community" aria-label="Side menu"> <li class="nav-item nav-heading"> <span class="nav-title"> <span class="nav-text"> Groups </span> </span> </li> <li class="nav-item"> <a href="//developers.google.com/community/gdg" class="nav-title"> <span class="nav-text"> Google Developer Groups </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/gdsc" class="nav-title"> <span class="nav-text"> Google Developer Student Clubs </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/womentechmakers" class="nav-title"> <span class="nav-text"> Woman Techmakers </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/experts" class="nav-title"> <span class="nav-text"> Google Developer Experts </span> </a> </li> <li class="nav-item"> <a href="//www.techequitycollective.com/" class="nav-title"> <span class="nav-text"> Tech Equity Collective </span> </a> </li> <li class="nav-item nav-heading"> <span class="nav-title"> <span class="nav-text"> Programs </span> </span> </li> <li class="nav-item"> <a href="//developers.google.com/community/accelerators" class="nav-title"> <span class="nav-text"> Accelerator </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/gdsc-solution-challenge" class="nav-title"> <span class="nav-text"> Solution Challenge </span> </a> </li> <li class="nav-item"> <a href="//developers.google.com/community/devfest" class="nav-title"> <span class="nav-text"> DevFest </span> </a> </li> <li class="nav-item nav-heading"> <span class="nav-title"> <span class="nav-text"> Stories </span> </span> </li> <li class="nav-item"> <a href="//developers.google.com/community/stories" class="nav-title" aria-label="All Stories"> <span class="nav-text"> All Stories </span> </a> </li> </ul> </div> </div> </nav> <select aria-label="Change language" name="lang-selector" id="lang-selector-nav" class="glue-form__dropdown glue-mobile-nav__lang-dropdown" onchange="window.location.href=this.value" > <option value="/en/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" lang="en" selected > English </option> <option value="/es/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" lang="es" > Español (Latam) </option> <option value="/id/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" lang="id" > Bahasa Indonesia </option> <option value="/ja/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" lang="ja" > 日本語 </option> <option value="/ko/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" lang="ko" > 한국어 </option> <option value="/pt-br/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" lang="pt-br" > Português (Brasil) </option> <option value="/zh-hans/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/" lang="zh-hans" > 简体中文 </option> </select> </div> <div class="backdrop"></div> <div class="blog-detail-container"> <section class="tags-container glue-page glue-spacer-5-top"> <div class="glue-eyebrow"><a href="/en/search/?product_categories=Android">Android</a></div> </section> <section class="heading-container glue-page glue-spacer-1-top"> <h1 class="glue-headline glue-headline--headline-1">How KAYAK reduced sign in time by 50% and improved security with passkeys</h1> </section> <section class="summary-container glue-page glue-spacer-4-top"> <div class="date-time"> <div class="published-date glue-font-weight-medium">OCT 24, 2023</div> </div> </section> <section class="glue-page glue-grid glue-spacer-1-top"> <section class="author-container glue-grid__col glue-grid__col--span-4-sm glue-grid__col--span-10-md"> <div class="author-obj"> <a class="glue-font-weight-medium" href="/en/search/?author=Kateryna+Semenova">Kateryna Semenova</a> <span class="glue-font-weight-medium role">Developer Relations Engineer, Android</span> </div> </section> <section class="social-container glue-grid__col glue-grid__col--span-4-sm glue-grid__col--span-2-md"> <button id="social-button" class="glue-button glue-button--low-emphasis glue-button--icon" aria-haspopup="true" aria-expanded="false"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#share"></use> </svg> <span>Share</span> </button> <ul id="social-menu" class="glue-elevation-level-1" role="menu" aria-labelledby="social-button"> <li> <a href="https://www.facebook.com/sharer/sharer.php?u={url}" title="Share on Facebook" target="_blank" rel="noopener"> <svg role="presentation" aria-hidden="true" class="glue-icon glue-icon--social glue-icon--32px"> <use href="/glue-icon/#post-facebook"></use> </svg> <span>Facebook</span> </a> </li> <li> <a href="https://twitter.com/intent/tweet?text={url}" title="Share on Twitter" target="_blank" rel="noopener"> <svg role="presentation" aria-hidden="true" class="glue-icon glue-icon--social glue-icon--32px"> <use href="/glue-icon/#twitter-x"></use> </svg> <span>Twitter</span> </a> </li> <li> <a href="https://www.linkedin.com/shareArticle?url={url}&amp;mini=true" title="Share on LinkedIn" target="_blank" rel="noopener"> <svg role="presentation" aria-hidden="true" class="glue-icon glue-icon--social glue-icon--32px"> <use href="/glue-icon/#post-linkedin"></use> </svg> <span>LinkedIn</span> </a> </li> <li> <a href="mailto:name@example.com?subject=Check%20out%20this%20site&body=Check%20out%20{url}" title="Send via Email"> <svg role="presentation" aria-hidden="true" class="glue-icon glue-icon--social glue-icon--32px"> <use href="/glue-icon/#email"></use> </svg> <span>Mail</span> </a> </li> <li> <a href="#" title="Get shareable link" data-link="" data-copy-text="Copy Link" data-copied-text="Copied!"> <svg role="presentation" aria-hidden="true" class="glue-icon glue-icon--social glue-icon--32px"> <use href="/glue-icon/#link"></use> </svg> <span></span> </a> </li> </ul> </section> </section> <section class="blocks-container glue-page glue-spacer-3-top"> <div class="block"> <img class="banner-image" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/kayak1.original.png" alt="kayak1" /> <div class="inner-block-content rich-content"> <h2 data-block-key="gvamu"><b>Introduction</b></h2><p data-block-key="99lct"><a href="https://play.google.com/store/apps/details?id=com.kayak.android">KAYAK</a> is one of the world&#x27;s leading travel search engines that helps users find the best deals on flights, hotels, and rental cars. In 2023, KAYAK integrated <a href="https://developers.google.com/identity/passkeys">passkeys</a> - a new type of passwordless authentication - into its Android and web apps. As a result, <b>KAYAK reduced the average time it takes their users to sign-up and sign-in by 50%</b>, and also saw a decrease in support tickets.</p><p data-block-key="11mpn">This case study explains KAYAK&#x27;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.</p><p data-block-key="cujpg">If you want a quick summary, check out the companion <a href="https://youtu.be/moeXZ0rjezQ">video on YouTube</a>.</p> </div> <div class="inner-block-content yt-video"> <div class="glue-video"> <div class="glue-video__container glue-video__container--inline" data-glue-yt-video-vid="moeXZ0rjezQ" > </div> <div class="glue-video__nojs"> <p><a href="https://www.youtube.com/watch?v=moeXZ0rjezQ">Link to Youtube Video</a> (visible only when JS is disabled)</p> </div> </div> </div> <div class="inner-block-content rich-content"> <h3 data-block-key="gvamu"><b>Problem</b></h3><p data-block-key="brmtd">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&#x27;re often weak, reused, guessed, phished, leaked, or hacked.</p><blockquote data-block-key="dbrk4"><i>“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.” </i><b>– Matthias Keller, Chief Scientist and SVP, Technology at KAYAK</b></blockquote><p data-block-key="5o92k">To make authentication more secure, KAYAK sent &quot;magic links&quot; 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.</p><h3 data-block-key="bc1fc"><b>Solution</b></h3><p data-block-key="38qub">KAYAK&#x27;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&#x27;s device and can be synchronized across multiple devices. Users can sign in to KAYAK with a passkey by simply using their existing device&#x27;s screen lock, making it simpler and more secure than entering a password.</p><blockquote data-block-key="77c2v"><i>“We&#x27;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&quot; </i><b>– Matthias Keller, Chief Scientist and SVP, Technology at KAYAK</b></blockquote><h2 data-block-key="6ms18"><b>Credential Manager API integration</b></h2><p data-block-key="9f9ah">To integrate passkeys on Android, KAYAK used the <a href="https://developer.android.com/training/sign-in/passkeys">Credential Manager API</a>. 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.</p> </div> <div class="inner-block-content"> <div class="image-wrapper"> <img class="regular-image" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/kayak2.original.png" alt="kayak2" /> <div class="regular-image-description"> Figure 1: Credential Manager&#x27;s passkey creation screens. </div> </div> </div> <div class="inner-block-content rich-content"> <p data-block-key="gvamu">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:</p> </div> <div class="inner-block-content"> <div class="image-wrapper"> <img class="regular-image" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/kayak3.original.png" alt="kayak3" /> <div class="regular-image-description"> Figure 2:KAYAK&#x27;s diagram showing their registration and authentication flows. </div> </div> </div> <div class="inner-block-content rich-content"> <p data-block-key="gvamu">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.</p><p data-block-key="ajkfn">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:</p> </div> <div class="inner-block-content code-block"> <div class="highlight"><pre class="kotlin"><span></span><span class="kd">override</span><span class="w"> </span><span class="kd">fun</span><span class="w"> </span><span class="nf">createCredential</span><span class="p">(</span><span class="n">request</span><span class="p">:</span><span class="w"> </span><span class="n">CreateCredentialRequest</span><span class="p">,</span><span class="w"> </span><span class="n">activity</span><span class="p">:</span><span class="w"> </span><span class="n">Activity</span><span class="p">):</span><span class="w"> </span><span class="n">Single</span><span class="o">&lt;</span><span class="n">CreateCredentialResponse</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Single</span><span class="p">.</span><span class="na">create</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">emitter</span><span class="w"> </span><span class="o">-&gt;</span> <span class="w"> </span><span class="c1">// Triggers credential creation flow</span> <span class="w"> </span><span class="n">credentialManager</span><span class="p">.</span><span class="na">createCredentialAsync</span><span class="p">(</span> <span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">request</span><span class="p">,</span> <span class="w"> </span><span class="n">activity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">activity</span><span class="p">,</span> <span class="w"> </span><span class="n">cancellationSignal</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span> <span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Executors</span><span class="p">.</span><span class="na">newSingleThreadExecutor</span><span class="p">(),</span> <span class="w"> </span><span class="n">callback</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nc">CredentialManagerCallback</span><span class="o">&lt;</span><span class="n">CreateCredentialResponse</span><span class="p">,</span><span class="w"> </span><span class="n">CreateCredentialException</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="kd">override</span><span class="w"> </span><span class="kd">fun</span><span class="w"> </span><span class="nf">onResult</span><span class="p">(</span><span class="n">result</span><span class="p">:</span><span class="w"> </span><span class="n">CreateCredentialResponse</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">emitter</span><span class="p">.</span><span class="na">onSuccess</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="kd">override</span><span class="w"> </span><span class="kd">fun</span><span class="w"> </span><span class="nf">onError</span><span class="p">(</span><span class="n">e</span><span class="p">:</span><span class="w"> </span><span class="n">CreateCredentialException</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">emitter</span><span class="p">.</span><span class="na">tryOnError</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="p">}</span> </pre></div> </div> <div class="inner-block-content rich-content"> <p data-block-key="gvamu">This example defines a Kotlin function called createCredential() that returns a credential from the user as an RxJava Single of type <a href="https://developer.android.com/reference/android/credentials/CreateCredentialResponse">CreateCredentialResponse</a>. The createCredential() function encapsulates the asynchronous process of credential registration in a reactive programming style using the RxJava Single class.</p><p data-block-key="dact2">For a Kotlin implementation of this process using <a href="https://developer.android.com/kotlin/coroutines">coroutines</a>, read the <a href="https://developer.android.com/training/sign-in/passkeys">Sign in your user with Credential Manager</a> guide.</p><h4 data-block-key="6vh8i"><b>New user registration sign-up flow</b></h4><p data-block-key="9gmug">This example demonstrates the approach KAYAK used to register a new credential, here Credential Manager was wrapped in Rx primitives.</p> </div> <div class="inner-block-content code-block"> <div class="highlight"><pre class="kotlin"><span></span><span class="n">webAuthnRetrofitService</span> <span class="w"> </span><span class="p">.</span><span class="na">getClientParams</span><span class="p">(</span><span class="n">username</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="cm">/** email address **/</span><span class="p">)</span> <span class="w"> </span><span class="p">.</span><span class="na">flatMap</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">response</span><span class="w"> </span><span class="o">-&gt;</span> <span class="w"> </span><span class="c1">// Produce a passkeys request from client params that include a one-time challenge</span> <span class="w"> </span><span class="n">CreatePublicKeyCredentialOption</span><span class="p">(</span><span class="cm">/** produce JSON from response **/</span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">subscribeOn</span><span class="p">(</span><span class="n">schedulers</span><span class="p">.</span><span class="na">io</span><span class="p">())</span> <span class="w"> </span><span class="p">.</span><span class="na">flatMap</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="o">-&gt;</span> <span class="w"> </span><span class="c1">// Call the earlier defined wrapper which calls the Credential Manager UI</span> <span class="w"> </span><span class="c1">// to register a new passkey credential</span> <span class="w"> </span><span class="n">credentialManagerRepository</span> <span class="w"> </span><span class="p">.</span><span class="na">createCredential</span><span class="p">(</span> <span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">request</span><span class="p">,</span> <span class="w"> </span><span class="n">activity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">activity</span> <span class="w"> </span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">flatMap</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="c1">// send credential to the authentication server</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">observeOn</span><span class="p">(</span><span class="n">schedulers</span><span class="p">.</span><span class="na">main</span><span class="p">())</span> <span class="w"> </span><span class="p">.</span><span class="na">subscribe</span><span class="p">(</span> <span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/** process successful login, update UI etc. **/</span><span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/** process error, send to logger **/</span><span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">)</span> </pre></div> </div> <div class="inner-block-content rich-content"> <p data-block-key="gvamu">Rx allowed KAYAK to produce more complex pipelines that can involve multiple interactions with Credential Manager.</p><h4 data-block-key="dhope"><b>Existing user sign-in</b></h4><p data-block-key="ebv0h">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.</p> </div> <div class="inner-block-content"> <div class="image-wrapper"> <img class="regular-image" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/kayak4.original.png" alt="kayak4" /> <div class="regular-image-description"> Figure 3:Bottom sheet for passkey authentication. </div> </div> </div> <div class="inner-block-content rich-content"> <p data-block-key="gvamu">Developers should follow these steps when setting up a sign-in flow:</p><ol><li data-block-key="cd8fd">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 <a href="https://www.w3.org/TR/webauthn-2/#rp-id">RP ID</a>) at the beginning of any app session.</li><li data-block-key="e4g80">When offering <a href="https://developer.android.com/training/sign-in/credential-manager#instantiate-sign-in-request">Google sign-in via Credential Manager API</a>, 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.</li><li data-block-key="71f3o">If the result returns a list of available credentials, the app shows the bottom sheet authentication UI to the user.</li><li data-block-key="cojt0">If a <a href="https://developer.android.com/reference/kotlin/androidx/credentials/exceptions/NoCredentialException">NoCredentialException</a> 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 <a href="https://developer.android.com/training/sign-in/credential-manager#sign-up">Sign up with Google</a> flow.</li><li data-block-key="28bnj">Process the credential returned from Credential Manager.</li></ol> </div> <div class="inner-block-content code-block"> <div class="highlight"><pre class="kotlin"><span></span><span class="n">Single</span><span class="p">.</span><span class="na">fromSupplier</span><span class="o">&lt;</span><span class="n">GetPublicKeyCredentialOption</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">GetPublicKeyCredentialOption</span><span class="p">(</span><span class="cm">/** Insert challenge and RP ID that was fetched earlier **/</span><span class="p">)</span> <span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">flatMap</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">response</span><span class="w"> </span><span class="o">-&gt;</span> <span class="w"> </span><span class="c1">// Produce a passkeys request</span> <span class="w"> </span><span class="n">GetPublicKeyCredentialOption</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="na">toGetPublicKeyCredentialOptionRequest</span><span class="p">())</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">subscribeOn</span><span class="p">(</span><span class="n">schedulers</span><span class="p">.</span><span class="na">io</span><span class="p">())</span> <span class="w"> </span><span class="p">.</span><span class="na">map</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">publicKeyCredentialOption</span><span class="w"> </span><span class="o">-&gt;</span> <span class="w"> </span><span class="c1">// Merge passkeys request together with other desired options,</span> <span class="w"> </span><span class="c1">// such as Google sign-in and saved passwords.</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">flatMap</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="o">-&gt;</span> <span class="w"> </span><span class="c1">// Trigger Credential Manager system UI</span> <span class="w"> </span><span class="n">credentialManagerRepository</span><span class="p">.</span><span class="na">getCredential</span><span class="p">(</span> <span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">request</span><span class="p">,</span> <span class="w"> </span><span class="n">activity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">activity</span> <span class="w"> </span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">onErrorResumeNext</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">throwable</span><span class="w"> </span><span class="o">-&gt;</span> <span class="w"> </span><span class="c1">// When offering Google sign-in, it is recommended to first only look for Google accounts</span> <span class="w"> </span><span class="c1">// that have already been used with our app. If there are no such Google accounts, no passkeys,</span> <span class="w"> </span><span class="c1">// and no saved passwords, we try looking for any Google sign-in one more time.</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">throwable</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">NoCredentialException</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">return</span><span class="nd">@onErrorResumeNext</span><span class="w"> </span><span class="n">credentialManagerRepository</span><span class="p">.</span><span class="na">getCredential</span><span class="p">(</span> <span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">GetCredentialRequest</span><span class="p">(</span><span class="cm">/* Google ID with filterByAuthorizedOnly = false */</span><span class="p">),</span> <span class="w"> </span><span class="n">activity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">activity</span> <span class="w"> </span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="n">Single</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">throwable</span><span class="p">)</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">flatMapCompletable</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="c1">// Step 1: Use Retrofit service to send the credential to the server for validation. Waiting</span> <span class="w"> </span><span class="c1">// for the server is handled on a IO thread using subscribeOn(schedulers.io()).</span> <span class="w"> </span><span class="c1">// Step 2: Show the result in the UI. This includes changes such as loading the profile</span> <span class="w"> </span><span class="c1">// picture, updating to the personalized greeting, making member-only areas active,</span> <span class="w"> </span><span class="c1">// hiding the sign-in dialog, etc. The activities of step 2 are executed on the main thread.</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">.</span><span class="na">observeOn</span><span class="p">(</span><span class="n">schedulers</span><span class="p">.</span><span class="na">main</span><span class="p">())</span> <span class="w"> </span><span class="p">.</span><span class="na">subscribe</span><span class="p">(</span> <span class="w"> </span><span class="c1">// Handle errors, e.g. send to log ingestion service. </span> <span class="w"> </span><span class="c1">// A subset of exceptions shown to the user can also be helpful,</span> <span class="w"> </span><span class="c1">// such as user setup problems. </span> <span class="w"> </span><span class="c1">// Check out more info in Troubleshoot common errors at</span> <span class="w"> </span><span class="c1">// https://developer.android.com/training/sign-in/passkeys#troubleshoot</span> <span class="w"> </span><span class="p">)</span> </pre></div> </div> <div class="inner-block-content rich-content"> <blockquote data-block-key="gvamu"><i>“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.”</i> <b><i>– Matthias Keller</i></b></blockquote><p data-block-key="cahjf">To learn more, follow the guide on how to <a href="https://developer.android.com/training/sign-in/passkeys">Integrate Credentials Manager API</a> and how to <a href="https://developer.android.com/training/sign-in/credential-manager#sign-in">Integrate Credential Manager with Sign in with Google</a>.</p><h2 data-block-key="a7m36"><b>UX considerations</b></h2><p data-block-key="61ve">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.</p><p data-block-key="darqe">Our <a href="https://developer.android.com/design/ui/mobile/guides/patterns/passkeys">UX guide for passkeys</a> 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.</p> </div> <div class="inner-block-content"> <div class="image-wrapper"> <img class="regular-image" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/kayak5.original.png" alt="kayak5" /> <div class="regular-image-description"> Figure 4:KAYAK&#x27;s UI for passkey management. </div> </div> </div> <div class="inner-block-content rich-content"> <p data-block-key="gvamu">To prevent registration of multiple credentials for the same account, KAYAK used the <a href="https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialcreationoptions-excludecredentials">excludeCredentials</a> property that lists credentials already registered for the user. The following example demonstrates how to create new credentials on Android without creating duplicates:</p> </div> <div class="inner-block-content code-block"> <div class="highlight"><pre class="kotlin"><span></span><span class="kd">fun</span><span class="w"> </span><span class="n">WebAuthnClientParamsResponse</span><span class="p">.</span><span class="nf">toCreateCredentialRequest</span><span class="p">():</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="kd">val</span><span class="w"> </span><span class="nv">credentialRequest</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WebAuthnCreateCredentialRequest</span><span class="p">(</span> <span class="w"> </span><span class="n">challenge</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">challenge</span><span class="o">!!</span><span class="p">.</span><span class="na">asSafeBase64</span><span class="p">,</span> <span class="w"> </span><span class="n">relayingParty</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">relayingParty</span><span class="o">!!</span><span class="p">,</span> <span class="w"> </span><span class="n">pubKeyCredParams</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">pubKeyCredParams</span><span class="o">!!</span><span class="p">,</span> <span class="w"> </span><span class="n">userEntity</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WebAuthnUserEntity</span><span class="p">(</span> <span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">userEntity</span><span class="o">!!</span><span class="p">.</span><span class="na">id</span><span class="p">.</span><span class="na">asSafeBase64</span><span class="p">,</span> <span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">userEntity</span><span class="p">.</span><span class="na">name</span><span class="p">,</span> <span class="w"> </span><span class="n">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">userEntity</span><span class="p">.</span><span class="na">displayName</span> <span class="w"> </span><span class="p">),</span> <span class="w"> </span><span class="n">authenticatorSelection</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">WebAuthnAuthenticatorSelection</span><span class="p">(</span> <span class="w"> </span><span class="n">authenticatorAttachment</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;platform&quot;</span><span class="p">,</span> <span class="w"> </span><span class="n">residentKey</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;preferred&quot;</span> <span class="w"> </span><span class="p">),</span> <span class="w"> </span><span class="c1">// Setting already existing credentials here prevents</span> <span class="w"> </span><span class="c1">// creating multiple passkeys on the same keychain/password manager</span> <span class="w"> </span><span class="n">excludeCredentials</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">allowedCredentials</span><span class="o">!!</span><span class="p">.</span><span class="na">map</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nb">it</span><span class="p">.</span><span class="na">copy</span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">it</span><span class="p">.</span><span class="na">id</span><span class="p">.</span><span class="na">asSafeBase64</span><span class="p">)</span><span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="p">)</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">GsonBuilder</span><span class="p">().</span><span class="na">disableHtmlEscaping</span><span class="p">().</span><span class="na">create</span><span class="p">().</span><span class="na">toJson</span><span class="p">(</span><span class="n">credentialRequest</span><span class="p">)</span> <span class="p">}</span> </pre></div> </div> <div class="inner-block-content rich-content"> <p data-block-key="gvamu">And this is how KAYAK implemented excludeCredentials functionality for their Web implementation.</p> </div> <div class="inner-block-content code-block"> <div class="highlight"><pre class="kotlin"><span></span><span class="kd">var</span><span class="w"> </span><span class="nv">registrationOptions</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s">&quot;publicKey&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s">&quot;challenge&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">self</span><span class="p">.</span><span class="na">base64ToArrayBuffer</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="na">challenge</span><span class="p">),</span> <span class="w"> </span><span class="s">&quot;rp&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="na">rp</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;user&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">new</span><span class="w"> </span><span class="n">TextEncoder</span><span class="p">().</span><span class="na">encode</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="na">user</span><span class="p">.</span><span class="na">id</span><span class="p">),</span> <span class="w"> </span><span class="s">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="na">user</span><span class="p">.</span><span class="na">name</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;displayName&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="na">user</span><span class="p">.</span><span class="na">displayName</span> <span class="w"> </span><span class="p">},</span> <span class="w"> </span><span class="s">&quot;pubKeyCredParams&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="na">pubKeyCredParams</span><span class="p">,</span> <span class="w"> </span><span class="s">&quot;authenticatorSelection&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="s">&quot;residentKey&quot;</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;required&quot;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">}</span> <span class="p">};</span> <span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="na">allowCredentials</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="na">allowCredentials</span><span class="p">.</span><span class="na">length</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nv">excludeCredentials</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">[]</span><span class="p">;</span> <span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">var</span><span class="w"> </span><span class="nv">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="na">allowCredentials</span><span class="p">.</span><span class="na">length</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">excludeCredentials</span><span class="p">.</span><span class="na">push</span><span class="p">({</span> <span class="w"> </span><span class="s">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">self</span><span class="p">.</span><span class="na">base64ToArrayBuffer</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="na">allowCredentials</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">.</span><span class="na">id</span><span class="p">),</span> <span class="w"> </span><span class="s">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="na">allowCredentials</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">.</span><span class="na">type</span> <span class="w"> </span><span class="p">});</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="n">registrationOptions</span><span class="p">.</span><span class="na">publicKey</span><span class="p">.</span><span class="na">excludeCredentials</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">excludeCredentials</span><span class="p">;</span> <span class="p">}</span> <span class="n">navigator</span><span class="p">.</span><span class="na">credentials</span><span class="p">.</span><span class="na">create</span><span class="p">(</span><span class="n">registrationOptions</span><span class="p">);</span> </pre></div> </div> <div class="inner-block-content rich-content"> <h2 data-block-key="gvamu"><b>Server-side implementation</b></h2><p data-block-key="evch3">The server-side part is an essential component of an authentication solution. KAYAK added passkey capabilities to their existing authentication backend by utilizing <a href="https://github.com/webauthn4j/webauthn4j">WebAuthn4J</a>, an open source Java library.</p><p data-block-key="b84db">KAYAK broke down the server-side process into the following steps:</p><ol><li data-block-key="714fu">The client requests <a href="https://developer.android.com/training/sign-in/passkeys#format-json-request">parameters</a> 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.</li><li data-block-key="jnt3">The client runs browser or app flows to start passkey registration or sign-in.</li><li data-block-key="6l0a0">The client sends <a href="https://developer.android.com/training/sign-in/passkeys#handle-json-response">retrieved credential information</a> 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.</li></ol><p data-block-key="8fa0d">When KAYAK worked on this project, no third-party products supported passkeys. However, many resources are now available for creating a passkey server, including <a href="https://developer.android.com/training/sign-in/passkeys">documentation</a> and <a href="https://passkeys.dev/docs/tools-libraries/libraries/">library examples</a>.</p><h2 data-block-key="bmf0i"><b>Results</b></h2><p data-block-key="apijl">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.</p><blockquote data-block-key="f6ata"><i>“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.&quot;</i> <b><i>– Matthias Keller</i></b></blockquote><h2 data-block-key="bhbti"><b>Conclusion</b></h2><p data-block-key="30asi">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&#x27;s <a href="https://developer.android.com/training/sign-in/passkeys">Credential Manager API</a>.</p> </div> </div> </section> <section class="navigation-container glue-page glue-spacer-6-top"> <div class="posted-in-section"> <div class="posted-in-section__heading"> <span class="glue-caption"> posted in: </span> </div> <div class="posted-in-section__tags"> <ul> <li> <a href="/en/search/?product_categories=Android" class="glue-caption">Android</a> </li> <li> <a href="/en/search/?technology_categories=Mobile" class="glue-caption">Mobile</a> </li> <li> <a href="/en/search/?technology_categories=Web" class="glue-caption">Web</a> </li> <li> <a href="/en/search/?content_type_categories=Case+Studies" class="glue-caption">Case Studies</a> </li> <li> <a href="/en/search/?content_type_categories=Beginner" class="glue-caption">Beginner</a> </li> <li> <a href="/en/search/?tag=Learn" class="glue-caption">Learn</a> </li> <li> <a href="/en/search/?tag=privacy" class="glue-caption">privacy</a> </li> <li> <a href="/en/search/?tag=Security" class="glue-caption">Security</a> </li> </ul> </div> </div> <div class="buttons-section"> <div class="buttons-section__left"> <a href="/en/grow-your-game-on-google-play-with-the-indie-games-accelerator-submissions-now-open/" class="glue-button--icon glue-elevation-level-1 " aria-label="Previous"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#chevron-left"></use> </svg> </a> <span class="caption ">Previous</span> </div> <div class="buttons-section__right"> <span class="caption ">Next</span> <a href="/en/announcing-the-inaugural-google-for-startups-accelerator-women-founders-program-europe-israel-applications-now-open/" class="glue-button--icon glue-elevation-level-1 " aria-label="Next"> <svg role="presentation" aria-hidden="true" class="glue-icon"> <use href="/glue-icon/#chevron-right"></use> </svg> </a> </div> </div> </section> <section class="related-posts-container glue-page glue-spacer-6-top glue-spacer-3-bottom"> <span class="glue-headline glue-headline--headline-3">Related Posts</span> <div class="related-posts-container__carousel glue-page glue-spacer-5-top"> <div class="glue-carousel glue-carousel--cards glue-carousel-related-posts" aria-label="Related Posts"> <!-- Previous --> <button class="glue-carousel__button glue-carousel__button--prev" aria-label="Go to the previous slide"> <svg role="presentation" aria-hidden="true" class="glue-icon glue-icon--32px"> <use href="/glue-icon/#chevron-left"></use> </svg> </button> <!-- Next --> <button class="glue-carousel__button glue-carousel__button--next" aria-label="Go to the next slide"> <svg role="presentation" aria-hidden="true" class="glue-icon glue-icon--32px"> <use href="/glue-icon/#chevron-right"></use> </svg> </button> <!-- List --> <div class="glue-carousel__viewport"> <div class="glue-carousel__list"> <a class="glue-card glue-carousel__item" href="/en/5-years-of-innovation-student-developers-solving-real-world-problems-using-google-tech/"> <div aria-label="5 Years of Innovation: Student developers solving real-world problems using Google tech" class="glue-card__inner"> <picture class="glue-card__asset"> <img alt="5 Years of Innovation: Student developers solving real-world problems using Google tech" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/GDSC-Social_1.2e16d0ba.fill-800x400.png"> </picture> <div class="glue-card__content"> <div class="glue-card__tags glue-spacer-2-top"> <span class="glue-label">Android</span> <span class="glue-label">Cloud</span> <span class="glue-label">Announcements</span> <span class="glue-label">Community</span> </div> <p class="glue-headline glue-headline--headline-5">5 Years of Innovation: Student developers solving real-world problems using Google tech</p> <div class="glue-card__cta-custom glue-spacer-3-top"> <span class="glue-cta">June 20, 2024</span> <svg aria-hidden="true" class="glue-icon glue-icon--24px" role="presentation"> <use href="/glue-icon/#arrow-forward"></use> </svg> </div> </div> </div> </a> <a class="glue-card glue-carousel__item" href="/en/firebase-demo-day-24/"> <div aria-label="Learn to build and run AI powered apps at Firebase Demo Day ‘24" class="glue-card__inner"> <picture class="glue-card__asset"> <img alt="Learn to build and run AI powered apps at Firebase Demo Day ‘24" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/Newletter_DayOf.2e16d0ba.fill-800x400.jpg"> </picture> <div class="glue-card__content"> <div class="glue-card__tags glue-spacer-2-top"> <span class="glue-label">Firebase</span> <span class="glue-label">Mobile</span> <span class="glue-label">Web</span> <span class="glue-label">Tutorials</span> <span class="glue-label">How-To Guides</span> </div> <p class="glue-headline glue-headline--headline-5">Learn to build and run AI powered apps at Firebase Demo Day ‘24</p> <div class="glue-card__cta-custom glue-spacer-3-top"> <span class="glue-cta">Nov. 19, 2024</span> <svg aria-hidden="true" class="glue-icon glue-icon--24px" role="presentation"> <use href="/glue-icon/#arrow-forward"></use> </svg> </div> </div> </div> </a> <a class="glue-card glue-carousel__item" href="/en/opusclip-achieves-30-percent-cost-savings-in-visual-description-processing-with-gemini-flash/"> <div aria-label="OpusClip achieves 30% cost savings in visual description processing with Gemini Flash" class="glue-card__inner"> <picture class="glue-card__asset"> <img alt="OpusClip achieves 30% cost savings in visual description processing with Gemini Flash" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/OpusClip_metadata.2e16d0ba.fill-800x400.png"> </picture> <div class="glue-card__content"> <div class="glue-card__tags glue-spacer-2-top"> <span class="glue-label">Gemini</span> <span class="glue-label">AI</span> <span class="glue-label">Case Studies</span> <span class="glue-label">Industry Trends</span> </div> <p class="glue-headline glue-headline--headline-5">OpusClip achieves 30% cost savings in visual description processing with Gemini Flash</p> <div class="glue-card__cta-custom glue-spacer-3-top"> <span class="glue-cta">Nov. 20, 2024</span> <svg aria-hidden="true" class="glue-icon glue-icon--24px" role="presentation"> <use href="/glue-icon/#arrow-forward"></use> </svg> </div> </div> </div> </a> <a class="glue-card glue-carousel__item" href="/en/chrome-3p-autofill-services/"> <div aria-label="Chrome on Android to support third-party autofill services natively" class="glue-card__inner"> <picture class="glue-card__asset"> <img alt="Chrome on Android to support third-party autofill services natively" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/Security-GfD.2e16d0ba.fill-800x400.png"> </picture> <div class="glue-card__content"> <div class="glue-card__tags glue-spacer-2-top"> <span class="glue-label">Android</span> <span class="glue-label">Chrome Web</span> <span class="glue-label">Web</span> <span class="glue-label">Announcements</span> <span class="glue-label">Best Practices</span> </div> <p class="glue-headline glue-headline--headline-5">Chrome on Android to support third-party autofill services natively</p> <div class="glue-card__cta-custom glue-spacer-3-top"> <span class="glue-cta">Oct. 24, 2024</span> <svg aria-hidden="true" class="glue-icon glue-icon--24px" role="presentation"> <use href="/glue-icon/#arrow-forward"></use> </svg> </div> </div> </div> </a> <a class="glue-card glue-carousel__item" href="/en/announcing-the-winners-of-the-gemini-api-developer-competition/"> <div aria-label="Announcing the Winners of the Gemini API Developer Competition!" class="glue-card__inner"> <picture class="glue-card__asset"> <img alt="Announcing the Winners of the Gemini API Developer Competition!" src="https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/Gemini_Competition_G4D_-_meta.2e16d0ba.fill-800x400.png"> </picture> <div class="glue-card__content"> <div class="glue-card__tags glue-spacer-2-top"> <span class="glue-label">Mobile</span> <span class="glue-label">Web</span> <span class="glue-label">Case Studies</span> <span class="glue-label">Community</span> </div> <p class="glue-headline glue-headline--headline-5">Announcing the Winners of the Gemini API Developer Competition!</p> <div class="glue-card__cta-custom glue-spacer-3-top"> <span class="glue-cta">Nov. 21, 2024</span> <svg aria-hidden="true" class="glue-icon glue-icon--24px" role="presentation"> <use href="/glue-icon/#arrow-forward"></use> </svg> </div> </div> </div> </a> </div> </div> <!-- Navigation dots --> <div class="glue-carousel__navigation" aria-label="Choose a page" data-glue-carousel-navigation-label="Selected tab $glue_carousel_page_number$ of $glue_carousel_page_total$"> </div> </div> </div> </section> </div> <div class="footer-linkboxes__wrapper"> <nav class="footer-linkboxes" aria-label="Footer links"> <ul class="footer-linkboxes__list"> <li class="footer-linkbox"> <span class="footer-linkbox-heading"> Connect </span> <ul class="footer-linkbox-list"> <li class="footer-linkbox-list__item"> <a href="//googledevelopers.blogspot.com" class="footer-linkbox-list__link"> Blog </a> </li> <li class="footer-linkbox-list__item"> <a href="https://goo.gle/googlefordevs" class="footer-linkbox-list__link"> Instagram </a> </li> <li class="footer-linkbox-list__item"> <a href="https://goo.gle/gdevs-li" class="footer-linkbox-list__link"> LinkedIn </a> </li> <li class="footer-linkbox-list__item"> <a href="https://goo.gle/gdevs-tw" class="footer-linkbox-list__link"> Twitter </a> </li> <li class="footer-linkbox-list__item"> <a href="https://goo.gle/developers" class="footer-linkbox-list__link"> YouTube </a> </li> </ul> </li> <li class="footer-linkbox"> <span class="footer-linkbox-heading"> Programs </span> <ul class="footer-linkbox-list"> <li class="footer-linkbox-list__item"> <a href="//www.womentechmakers.com" class="footer-linkbox-list__link"> Women Techmakers </a> </li> <li class="footer-linkbox-list__item"> <a href="//developers.google.com/community/gdg" class="footer-linkbox-list__link"> Google Developer Groups </a> </li> <li class="footer-linkbox-list__item"> <a href="//developers.google.com/community/experts" class="footer-linkbox-list__link"> Google Developer Experts </a> </li> <li class="footer-linkbox-list__item"> <a href="//developers.google.com/community/accelerators" class="footer-linkbox-list__link"> Accelerators </a> </li> <li class="footer-linkbox-list__item"> <a href="//developers.google.com/community/gdsc" class="footer-linkbox-list__link"> Google Developer Student Clubs </a> </li> </ul> </li> <li class="footer-linkbox"> <span class="footer-linkbox-heading"> Developer consoles </span> <ul class="footer-linkbox-list"> <li class="footer-linkbox-list__item"> <a href="//console.developers.google.com" class="footer-linkbox-list__link"> Google API Console </a> </li> <li class="footer-linkbox-list__item"> <a href="//console.cloud.google.com" class="footer-linkbox-list__link"> Google Cloud Platform Console </a> </li> <li class="footer-linkbox-list__item"> <a href="//play.google.com/apps/publish" class="footer-linkbox-list__link"> Google Play Console </a> </li> <li class="footer-linkbox-list__item"> <a href="//console.firebase.google.com" class="footer-linkbox-list__link"> Firebase Console </a> </li> <li class="footer-linkbox-list__item"> <a href="//console.actions.google.com" class="footer-linkbox-list__link"> Actions on Google Console </a> </li> <li class="footer-linkbox-list__item"> <a href="//cast.google.com/publish" class="footer-linkbox-list__link"> Cast SDK Developer Console </a> </li> <li class="footer-linkbox-list__item"> <a href="//chrome.google.com/webstore/developer/dashboard" class="footer-linkbox-list__link"> Chrome Web Store Dashboard </a> </li> </ul> </li> </ul> </nav> </div> <div class="footer-utility__wrapper"> <div> <nav class="footer-sites" aria-label="Other Google Developers websites"> <a href="https://developers.google.com/" class="site-logo-link" data-label="Site logo"> <img src="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/images/g-dev.svg" class="site-logo" alt="Google for Developers"> </a> <ul class="footer-sites-list"> <li class="footer-sites-item"> <a href="//developer.android.com" class="footer-sites-link"> Android </a> </li> <li class="footer-sites-item"> <a href="//developer.chrome.com/home" class="footer-sites-link"> Chrome </a> </li> <li class="footer-sites-item"> <a href="//firebase.google.com" class="footer-sites-link"> Firebase </a> </li> <li class="footer-sites-item"> <a href="//cloud.google.com" class="footer-sites-link"> Google Cloud Platform </a> </li> <li class="footer-sites-item"> <a href="//developers.google.com/products" class="footer-sites-link"> All products </a> </li> <li class="footer-sites-item"> <button aria-hidden="true" class="glue-cookie-notification-bar-control footer-sites-link"> Manage cookies </button> </li> </ul> </nav> <nav class="footer-utility-links"> <ul class="footer-utility-list"> <li class="footer-utility-item"> <a href="//developers.google.com/terms/site-terms" class="footer-utility-link"> Terms </a> </li> <li class="footer-utility-item"> <a href="//policies.google.com/privacy" class="footer-utility-link"> Privacy </a> </li> </ul> <div class="language-selector footer" aria-label="Select your language preference"> <button type="button" aria-controls="lang-selector-footer" aria-haspopup="true"> <svg role="presentation" aria-hidden="true" class="glue-icon lang-icon"> <use href="/glue-icon/#language"></use> </svg> <span class="lang-label">English</span> <svg role="presentation" aria-hidden="true" class="glue-icon lang-icon"> <use href="/glue-icon/#arrow-drop-down"></use> </svg> </button> <div id="lang-selector-footer" class="lang-menu hidden" role="menu"> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" aria-current="true" href="/en/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">English</a> </li> <li role="presentation"> <a role="menuitem" lang="es" href="/es/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">Español (Latam)</a> </li> <li role="presentation"> <a role="menuitem" lang="id" href="/id/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">Bahasa Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" href="/ja/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" href="/ko/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">한국어</a> </li> <li role="presentation"> <a role="menuitem" lang="pt-br" href="/pt-br/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">Português (Brasil)</a> </li> <li role="presentation"> <a role="menuitem" lang="zh-hans" href="/zh-hans/how-kayak-reduced-sign-in-time-by-50-and-improved-security-with-passkeys/">简体中文</a> </li> </ul> </div> </div> </nav> </div> </div> <script src="https://www.youtube.com/player_api"></script> <script src="//www.gstatic.com/glue/v27_1/glue.min.js"></script> <script type="text/javascript" src="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/js/dgc_blog.js"></script> <script src="https://www.gstatic.com/glue/cookienotificationbar/cookienotificationbar.min.js" data-glue-cookie-notification-bar-category="2A" data-glue-cookie-notification-bar-site-id="developers.googleblog.com"> </script> <script src="https://storage.googleapis.com/gweb-developer-goog-blog-cms-assets/f677f1b8-8552-4a23-83cc-57c36dbaf782/js/blog_detail.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10