0% found this document useful (0 votes)
182 views47 pages

Passkeys Handbook

Uploaded by

brayjuanico
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
182 views47 pages

Passkeys Handbook

Uploaded by

brayjuanico
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 47

E-book

Ebook

The passkeys
handbook
Author: Philippe De Ryck
Ebook The passkeys handbook

The guide to Welcome to our journey into the world of passkeys.

this ebook Passkeys represent a significant leap forward,


offering a user-friendly and highly secure alternative
to traditional passwords. By leveraging cryptographic
techniques, passkeys eliminate many vulnerabilities
associated with password-based systems, such as
password spraying, credential stuffing, and phishing
attacks. The major players that initiated passkey
support were Apple, Microsoft, and Google. But
today, the entire industry has embraced passkeys as
the future of user authentication.

This ebook is crafted for a broad audience, ranging


from curious users who wish to understand how
passkeys enhance their online security to developers
and software architects seeking to implement
passkey technology in their applications.

Throughout this ebook, we will explore passkeys from


multiple perspectives to understand their importance,
how they work, and how they can be integrated and
adopted in web applications. Here's an overview of
the chapters in this ebook:

1. The why and what of passkeys: This chapter sets


the stage by addressing the challenges of secure
user authentication and introducing the concept
of passkeys as a solution.
Ebook The passkeys handbook

2. Using passkeys to secure your account: In this


chapter, we view passkeys through the lens of the
user, detailing how to create, use, and manage
passkeys across different devices.

3. The technicalities of passkeys: This chapter


dives into the developer's world, discussing the
Web Authentication API, and providing practical
examples for integrating passkeys into web
applications.

4. Integrating passkeys in your applications: In


this chapter, we bring everything together from
an architect's perspective, offering insights into
adopting passkeys within applications, addressing
implementation strategies, and exploring solutions
like OpenID Connect and Auth0 for seamless
integration.

As we navigate through these chapters, we dig


deeper and deeper into the technicalities of
passkeys. At the end of this ebook, you will have a
solid understanding of the security properties of
passkeys and their underlying mechanics. You will
also have learned how to implement passkeys in your
applications, along with the best practice strategy of
using OpenID Connect to offload user authentication
to a central provider.

We hope this ebook will be an essential guide to help


you build secure user authentication with passkeys.
Ebook The passkeys handbook 4

Contents 5 Chapter 1: The why and what of passkeys

Challenges to secure user authentication

The state of user authentication

Introducing passkeys

Summary

11 Chapter 2: Using passkeys to secure your account

Using passkeys for authentication

Registering a passkey for authentication

Adding a passkey to existing accounts

The underlying mechanics of passkeys

Summary

22 Chapter 3: The technicalities of passkeys

The JavaScript APIs behind passkeys

Integrating passkeys into authentication forms

Exploring the end-to-end passkey flows

Summary

35 Chapter 4: Integrating passkeys in your applications

Gradual passkey adoption

Passkey authentication across different applications

Implementing passkeys with Auth0

Summary

45 Passkeys: A one page summary


Ebook The passkeys handbook 5

Chapter #1 In this first chapter, we explore why securing user authentication is


such a challenge. We investigate how the security challenges for user
authentication created the perfect circumstances for passkeys to emerge.
The why
By the end of this chapter, you will understand why we need passkeys. You
and what will also have gained a high-level idea of what passkeys are so that we can
of passkeys dive into the technical details in the upcoming chapters.

Let's start by taking an honest look into the security of user authentication.

Challenges to secure user authentication

User authentication is the cornerstone of application security. Unfortunately,


it is also one of the weakest links. During the authentication process, the
user provides an identifier of who they claim to be, which can correspond
to their real-world identity or an online persona. This first step is known as
identification and can be performed by anyone. That's why we combine this
identification step with authentication, where the user is asked to prove they
are indeed who they claim to be.

The traditional way of handling authentication is using passwords, where


the user provides a secret they know, which is associated with the account
they claim is theirs. While this approach may make sense on an abstract
level, history has clearly shown us that this does not work on a practical
level. Authentication systems today are bombarded by attacks, and
thousands of user accounts are breached every second.

Let's take a closer look at common challenges to password-based user


authentication.

Password spraying
A typical user will rely on their memory to keep track of passwords. Unfortunately,
most users are not gifted with a perfect memory, which implies that the
password should be something they can remember over time. As a result, users
often choose simple passwords (e.g., P@ssw0rd! ), predictable passwords
(e.g., their name), or easy-to-remember passwords (e.g., dictionary words).

Attackers have figured this out and explicitly target this behavior in
password spraying attacks. In such an attack, attackers simply try to log
in as any user with a user's identifier and a password from a list of simple
passwords. Running such an attack with thousands or millions of known
passwords is bound to yield some successful authentication attempts.
Ebook The passkeys handbook 6

Credential stuffing
A second challenge to password-based authentication lies in the numbers. A
typical user has dozens or hundreds of accounts across numerous different
websites. Keeping track of unique passwords for each website in memory is
impossible. As a result, users resort to reusing passwords across websites.

This behavior becomes problematic when the attackers breach one application and
obtain a list of usernames and passwords. Attackers use this data in a credential
stuffing attack, trying to log in to applications using a previously stolen username/
password combination. Credential stuffing attacks are much more likely to succeed
than password spraying attacks, making them significantly more dangerous.

Phishing
User authentication typically relies on the user entering their information in an
authentication form hosted by the application. During this process, there is no
explicit step where the user verifies that they are visiting the legitimate application.

Attackers abuse this pattern using phishing attacks, where they host a
malicious authentication form that looks exactly like the legitimate application.
Suppose the attacker can trick the user into visiting that malicious form and
entering their credentials. In that case, the attacker obtains a known-good
set of credentials, which they can easily abuse whenever they want.

The scale of user authentication across the Internet has only exacerbated
these problems, and the statistics on attacks against user authentication
are mind boggling.

For example, Okta's 2023 report on the State of Secure Identity states that
24.3% of login attempts on their Customer Identity Cloud are considered
credential stuffing.

Percentages do not fully reflect the severity of this problem, so let's try with
an absolute number: In 2022, Microsoft blocked 1,287 password attacks
every second. That means that in the time it took you to read this statistic,
Microsoft has seen more than 10,000 attacks on password-based user
authentication.
Ebook The passkeys handbook 7

The state of user authentication

While these weaknesses of password-based user authentication


are problematic, there are ways we can mitigate these issues. The
most effective defense strategy is enforcing the use of Multi-Factor
Authentication (MFA). With MFA, knowing a password becomes one of the
authentication factors. However, to complete the authentication process,
the user must provide a second authentication factor. More advanced
authentication systems assess the potential risk of a specific user's
authentication, allowing them to ask for even more than two authentication
factors.

As a user yourself, you are undoubtedly familiar with common MFA


mechanisms. Nonetheless, the list below gives a brief overview of typical
MFA implementations:

• SMS codes: The application sends a random code to the user's phone
using SMS. The user has to provide this code during the authentication
process. This mechanism relies on having access to the user's phone.

• Authenticator app codes: This mechanism relies on an authenticator


app installed on the user's phone or in their password manager.
Popular examples include Authy and Google Authenticator. When
the user sets up MFA for their account using such an authenticator
app, the app gains the capability to generate unique one-time codes.
During authentication, the user provides the latest code generated
by the app and the backend verifies that this is indeed a valid code.
This mechanism relies on having access to the authenticator app that
generates the one-time codes.

• Push notifications: When setting up MFA, the user installs an


app on their phone that is uniquely linked to their account. During
authentication, the app will prompt the user to confirm the
authentication. This mechanism relies on having access to the user's
phone.

• Key-based authentication: When setting up MFA, the user generates a


key specific for this application. During the authentication process, the
user needs to sign a challenge with the previously generated key. These
keys are stored on a particular device (e.g., a laptop or a USB security
key), so this mechanism relies on having access to that device.
Ebook The passkeys handbook 8

With all of these options, it is tempting to consider the security challenges


of user authentication solved. Unfortunately, this is not the case.
Implementing MFA with these mechanisms is definitely better than simply
relying on passwords, but the problem has not gone away. The attack
patterns shift from simultaneously targeting millions of users towards more
targeted attacks, but they are far from gone.

Digging deep into the attack patterns against these MFA mechanisms is out
of scope for this ebook, so we'll list a few common attack scenarios instead.
Attackers abuse SIM Swapping attacks to take control of a user's phone
number, allowing them to bypass SMS-based MFA. Online phishing attacks
trick the user into giving valid SMS codes or OTP codes to the attacker,
allowing them to bypass these code-based MFA mechanisms. Attackers
also abuse MFA fatigue to trick the user into approving a push notification
associated with an authentication attempt of the attacker, allowing them to
bypass this mechanism as well.

If we map these attack patterns to our list of MFA mechanisms, none of


these attacks target key-based authentication. That is not an oversight
but an inherent property of how key-based authentication works under
the hood. We'll get to those details later, but for now, it is essential to
understand that a key is typically uniquely linked to a specific application.
Because of this unique link, key-based authentication is phishing-resistant.
If the user ends up on a phishing site (e.g., l1nkedin.com instead of
linkedin.com), the key from the legitimate application ( linkedin.com ) will
not be associated with the phishing site (l1nkedin.com).

Problem solved, right?

Yes! Key-based authentication is considered a robust and secure


authentication mechanism. Unfortunately, key-based authentication is not
that practical to use. As we explained before, this mechanism relies on a
secret key stored somewhere. This key can be stored on the local device or
on a USB security key. Regardless of where the key is stored, this exact key
must be present during the authentication step.

The requirement to have the key present makes key-based authentication


hard to manage. Storing the key in the browser works perfectly fine as long
as the user only works from this browser. However, users often use multiple
devices like computers, tablets, or phones. To make this work, they would
have to register a key for each device, which is highly impractical.
Ebook The passkeys handbook 9

USB security keys don't suffer from this problem. They are portable by
nature, and modern versions even support wireless communication
with handheld devices, so users can even use a USB security key when
authenticating to the application on their phone. While USB security keys
solve all the technical challenges, they still have one significant downside:
cost. A USB security key can cost between $25 and $100, and it is often
recommended to have a backup key as well. These solutions are often used
in a corporate setting, where the company is willing to invest in security.
However, expecting a typical user to purchase such USB security keys for
everyday use is a pipe dream.

And that brings us to passkeys.

Introducing passkeys

Passkeys aim to make key-based authentication available as the primary


authentication mechanism, thereby entirely deprecating passwords. They
were first introduced in 2022 by a consortium consisting of Apple, Google,
and Microsoft. The three tech giants vouched to enable everything needed
for passkeys to succeed.

This image here shows the conceptual idea behind passkeys with a
passkey stored on the user's local device, such as their laptop. As you
can see, authentication is entirely handled using previously created
keys, without the need for the user to provide any credentials to the web
application. Don't worry if this does not make sense yet. We will definitely
dive deeper into passkeys in upcoming chapters.

The conceptual idea behind passkeys.

1. I want to login with a passkey


2. Sure, you can use a stored passkey
3. I am example.com, can you sign this
and give me the metadata associated
with your key?
4. Sign the challenge with the private key
5. Signature and key metadata
6. The backend looks up the public key,
verifies the signature to authenticate
the user associated with this key
Ebook The passkeys handbook 10

Under the hood, passkeys rely on the exact key-based authentication


mechanism discussed in the previous section. However, one of the
significant challenges we discussed earlier was the portability of the
keys. Unless you invest in expensive USB security keys, there is no
straightforward way to use the same key across different devices.

Additionally, passkeys are discoverable by nature. Concretely, this means


that the browser and native apps can discover if you have passkeys for a
specific application. As a result, they can simplify the UI, allowing you to
select a passkey and log in with a single action.

Sounds great! So, let's take a look at what was needed to bring passkeys
to life.

As a first step, OS vendors added support for using various devices as


authenticators. For example, your OS enables the creation and storage of
passkeys, which may even be linked to the security chip in your computer.
Apple and Google added support for passkeys on their mobile OSes,
allowing users to use their phones to store and manage passkeys. With that
upgrade, your existing phone gained the same capability as an expensive
USB security key without needing to connect it to a computer.

A second significant improvement is the way storage is handled for


passkeys, especially when it comes to synchronization across devices.
Phones, tablets, desktops, and laptops typically allow the user to save
their passkeys to a keychain linked to their platform account (e.g., Apple
ID, Google Account, Microsoft Account), enabling synchronization of
passkeys through platform security mechanisms, such as iCloud keychain
or Google Password Manager. Similarly, password managers have added
support for acting as a passkey authenticator, automatically bringing easy
synchronization across all your devices.

Finally, note that passkeys can also be tied to a specific device, making
it possible to use the passkey on that device without syncing it to other
devices. This type of passkey is aptly named a device-bound passkey.
Such passkeys are less practical for consumer-centric services, but
they are useful for securing business applications, where a user always
authenticates from the same device.
Ebook The passkeys handbook 11

Next to solving portability, passkeys also aim to deliver a smooth and


seamless user experience. To ensure users can easily authenticate with
passkeys, browsers have implemented new features to enable passkeys in
authentication forms. We discuss these implementation details later in this
ebook.

Summary

Key-based authentication that is tied to a specific website solves most


security challenges with user authentication on the web. However, using
key-based authentication was difficult due to the clunky user experience,
the lack of portable keys, or the need to buy expensive hardware.

Passkeys solve all these challenges by enabling the browser to rely on


an authenticator to handle keys. Authenticators can be mobile phones,
password managers, or USB security keys. Combined with an optimized
user experience in the browser, passkeys bring key-based authentication as
a way to eradicate passwords.

Now that we know what passkeys are supposed to do, it's about time that
we dive into the use of passkeys in practice. In the next chapter, we explore
passkeys from the user's perspective.

Chapter #2 In the previous chapter, we covered the why and what of passkeys. In this
first deep-dive chapter, we approach passkeys from the user's perspective.

Using passkeys We explore the following topics:

to secure your • Creating and using passkeys as a user.

account • The mechanics and security properties of passkeys.

• Practical usage scenarios, including synchronizing passkeys across


devices.

By the end of this chapter, you will understand how users can leverage
passkeys to secure their accounts.
Ebook The passkeys handbook 12

Using passkeys for authentication

The goal of passkeys is to replace passwords as the primary authentication


mechanism, so it should not come as a surprise that the user experience
of passkeys is highly optimized. Let's explore how a proper passkey
implementation makes authentication a smooth and frictionless experience
using the learnpasskeys.io playground.

In a typical passkey authentication scenario, the following steps happen:

1. The user clicks the Sign In button.

2. The frontend application triggers the passkey authentication, asking the


user to confirm authentication with one of their passkeys.

3. The frontend submits the authentication data to the backend, which,


after successful verification, considers the user authenticated.

The screenshots below illustrate a passkey scenario where the browser


delegates authentication to the user's phone.

The user is prompted to select The phone prompts the user


an authenticator since they for authentication, suggesting
may have multiple devices a passkey stored on their
capable of handling passkeys. phone.

During this authentication process, the user's phone signs a challenge with
a private key stored on the device. So, how did the passkey end up on the
user's phone in the first place?
Ebook The passkeys handbook 13

Registering a passkey for authentication

Before being able to authenticate with a passkey, the user has to create a
passkey and register it with their account for the application. In a passkey-
only world, this would happen when the user creates a new account. Let's
take a look at how that would work in practice using the learnpasskeys.io
playground.

1. The user provides an identifier (e.g., a username or email address) for


their account and initiates the creation of a passkey for authentication.

2. The frontend application triggers the passkey creation, asking the user
to create a passkey on an authenticator (e.g., a phone).

3. The frontend submits the passkey data it received from the


authenticator to the backend application, which stores it with the user's
account.

The screenshots below illustrate a passkey scenario where the user creates
a passkey on their phone through a QR code.

The user is prompted to select an When the user selects their The phone prompts the user
authenticator since they may have phone, they get a QR code to for authentication, which
multiple devices capable of handling launch the passkey creation will rely on biometrics or a
passkeys. process on the phone. PIN code to lock the newly
created passkey.
Ebook The passkeys handbook 14

Adding a passkey to existing accounts

As awesome as passkeys are, it is unfeasible to expect every application


to erase their existing user accounts and walk each of these users through
a new registration procedure. Fortunately, there's no need to do that. The
application can allow users to upgrade their account security by adding a
passkey to an existing account.

Concretely, here's how a user would upgrade their traditional password-


based account to a passkey-based account:

1. Authenticate to the application using the traditional authentication


mechanisms (e.g., password with multi-factor authentication).

2. Navigate to the account settings and create a new passkey, which will
take the user through the same steps as we illustrated before.

3. Sign out and sign in with the passkey.

Once these steps have been completed, the application is confident that
the user's passkey is correctly set up and working as intended. At this point,
the application can prompt the user to disable their traditional password-
based authentication. Doing so would eradicate typical attacks against
password-based authentication, such as phishing, credential stuffing, and
brute force attacks.

The underlying mechanics of passkeys

With all this talk about passkeys and signatures, it has become time
to dive deeper into the mechanics of passkeys. Why is this key-based
authentication so great? And who's responsible for doing what?

Before we dive in, know that this section may not address your hunger for
technical details yet. In the next chapter, we dive into the implementation of
passkeys, including the browser APIs to create and use passkey credentials.

The concept of digital signatures


Digital signatures rely on a cryptographic key pair consisting of a private key
and a public key, as illustrated below. The private key should only be known
to its owner, while the public key can be shared with anyone who needs to
verify a signature.
Ebook The passkeys handbook 15

A cryptographic key pair consisting of a private and a public key.

The private key is used to create a signature of a piece of data, such as a


document or an authentication challenge. The data and key are provided as
input to an asymmetric cryptographic signing algorithm, which spits out the
signature.

Note that signature data is typically represented in a binary format but often
encoded into a format compatible with text-based protocols such as HTTP.
Common encodings are base64 and hex.

Signing data with the private key.


Ebook The passkeys handbook 16

The public key is used to verify signatures. In this step, the party verifying
the signature provides three inputs to a cryptographic verification function:

• The exact data that is supposed to be signed (e.g., the document or


authentication challenge).

• The signature.

• The public key associated with the private key that should have been
used.

A valid signature ensures that the data is an exact match to the data that
was signed. It also ensures that the private key used to sign the data is
uniquely linked to the public key used to verify the signature.

Verifying a signature with the public key.

Key-based authentication
One of the scenarios that can be implemented with digital signatures is
user authentication. By creating a valid signature when requested by the
application, the user can prove that they possess a private key that should
only be known to them. The image below illustrates the process of key-
based user authentication.
Ebook The passkeys handbook 17

User authentication by proving possession of a private key.

1. Provide a challenge
2. Sign the challenge using the private key
3. Authenticate by sending the signature
4. Verify the signature using the user’s
public key

Concretely, the application to which the user is authenticating asks the user
to sign a challenge with their private key. The signature of the challenge
is submitted back to the application. The application now uses the user's
public key to verify the signature, thereby confirming whether the user
possessed the correct private key.

In this exchange, it is important to note that the challenge should be


random and unique for every authentication attempt. If the challenge is
a static or predictable value, an attacker can impersonate the user by
replaying a previously captured signature.

Note that the security properties of key-based authentication are


significantly better than using passwords:

• Passwords are shared with the application on every authentication, but


the private key is only known to the user and never shared.

• Since the private key is not known to the application, there is no risk of it
being compromised in a data breach.

• Signatures and private keys are not susceptible to brute force or


guessing attacks.
Ebook The passkeys handbook 18

Authenticators and keys


In this final step, let's investigate how these details fit together when using
passkeys. The image below shows three actors: the application's backend,
the user's browser, and an authenticator.

The flow of a passkey authentication.

1. Login as the user


2. Generate an authentication challenge
3. Challenge
4. Challenge signature request for
https://example.com
5. Prompt the user to use an existing
key for https://example.com
6. Sign the challenge and create a
response
7. Signature response
8. Signature response
9. Verify signature with stored public key

The role of the backend and the browser do not need much additional
One concrete implementation of explanation, but the authenticator does. The authenticator is a key store
the CTAP protocol supports the
with the ability to handle private keys securely. The authenticator is
user scanning a QR code with
their smartphone. Under the hood, responsible for generating and storing keys, authenticating the user when
scanning the QR code allows the a key is being used, and interacting with the browser to exchange the
client, in this case the browser, to
provide the phone with temporary challenge and response. The exact protocol used between the browser and
cryptographic key information. The the authenticator is the Client To Authenticator Protocol 2 (CTAP2), which
browser then uses these keys to
encrypt a Bluetooth beacon, also is defined by the FIDO2 standard. This protocol embeds the necessary
known as a BLE advertisement, and security mechanisms to ensure proximity of the authenticator and to resist
sends it out from the local device
(i.e., the user's computer). When the
phishing attacks.
phone is in proximity to the laptop,
it receives this beacon and uses the Until now, we mostly talked about using a phone as an authenticator, and
keys from the QR code to decrypt there's a good reason: a phone is a highly convenient authenticator. Most
the message. Once that has been
completed, both the browser and the users have a smartphone, and they carry it around everywhere they go.
phone use a tunnel service to set up Additionally, modern phones can protect passkeys with biometric or PIN-
a reliable communication channel.
More details about this protocol can
based verification. Finally, phones can easily communicate with the browser
be found in section 11.5.1 of the FIDO over Bluetooth to exchange passkey and signature information.
CTAP 2.2 protocol.
Ebook The passkeys handbook 19

Note that a user's phone is a highly convenient authenticator to use in


combination with passkeys. Most users have a smartphone, and they
carry it around everywhere they go. As a result, the phone is seen as a
portable, cross-platform authenticator. This type of authenticator is ideal
for authenticating the same web application on multiple computers.
Additionally, modern phones can protect passkeys with biometric or PIN-
based verification.

However, with passkeys, nothing prevents the user from having more than
one passkey for an account on a web application. In fact, it is actually
recommended that the application prompts the user to set up a passkey
for their local device after using the phone-based authenticator. Such a
scenario would look like this:

1. The user registers a phone-based passkey and uses it to authenticate


to the application. The application detects the use of a cross-platform
passkey and asks the user if they prefer setting up an additional
passkey for their current device.

2. The user creates a new passkey for their local device (e.g., using
Windows Hello or Apple's Touch ID).

3. The application registers this new passkey for the user's account.

4. The next time the user wishes to authenticate, the browser will allow
the user to authenticate with the local passkey, removing the need to
interact with the phone.

Note that the protocols that enable passkeys offer the necessary
implementation flexibility as well. For example, modern password managers,
such as Bitwarden and OnePassword, have also incorporated passkey
support into their products. Not only does this make it easy to manage
passkeys, but it also automatically enables synchronization of passkeys
between multiple devices. A similar feature is supported by platform-
based authenticators, such as Google Password Manager or Apple iCloud
Keychain.

The fact that passkeys are handled in a software-based authenticator


has little to no impact on the user experience. The user is still prompted to
select a passkey from the set of passkeys known for this particular website.
Instead of taking their phone, they can complete the process from within
their browser or password manager.
Ebook The passkeys handbook 20

Finally, note that the exact mechanism to authenticate the user during the
authentication process is at the discretion of the authenticator. Phones
typically use biometrics or PINs for this purpose, but other authenticators
can choose a different user authentication mechanism.

Associating passkeys with web applications


Each time an application prompts the user to create a passkey, a new
passkey will be generated on one of the user's authenticators. In the
example we discussed earlier in this chapter, the user relied on their phone
as the authenticator. In practice, this means that the user's phone will have
a bunch of passkeys, all for different applications.

So how does the phone keep track of all these passkeys? Simple! A
passkey is associated with the domain of the application that is creating it.
If the application runs on https://passkeys.example.com/register , the
passkey can be associated with passkeys.example.com or optionally with
example.com . The passkey will never be associated with content running
on a different location.

This restriction plays a crucial role when triggering a passkey-based


authentication flow. When the frontend asks the browser to find a suitable
passkey for authentication, it includes the same domain used when
registering a passkey. The browser will ensure that the provided domain
is either the domain or the parent domain of the running application. If the
application provides a different value than one of these, the browser will
refuse the passkey authentication attempt.

Finally, the user can have more than one passkey for a specific application.
The association of a passkey with These passkeys can belong to a single account or multiple accounts. The
an application's domain also plays
exact details of how to handle such a scenario are left at the discretion of
a crucial role in phishing prevention.
Should the attacker succeed in the authenticator. However, a typical way to handle this is by prompting the
luring the user to a phishing page, user to ask which passkey they want to use.
they are restricted to using their
domain or parent domain in a
passkey authentication request.
Syncing passkeys across devices
So, for https://l1nkedin.com , Since passkeys are intended to be used as a primary authentication factor,
there is no way the attacker can
portability is a crucial feature. Passkeys will only succeed when the user has
launch a passkey authentication
request using the credentials them available when needed.
associated with linkedin.com. As
a result, the phishing attempt will be Portability comes very naturally for passkeys stored on a physical device
unsuccessful.
that is carried by the user. Software-based authenticators offer support for
syncing passkeys as well.
Ebook The passkeys handbook 21

In password managers, syncing is enabled by including passkeys in a vault,


which is synced across devices. OS-level mechanisms, such as Google
Password Manager and iCloud keychain also offer support for syncing your
passkeys between devices, such as phones and computers. Such a sync
mechanism not only enhances the user experience. It also ensures that
passkeys remain available when a device is upgraded or lost.

The screenshots below show how we can use the passkey we created on
our phone earlier to authenticate on our macOS computer with Safari.

Summary

Passkeys offer a smooth user experience while using robust key-based


authentication mechanisms. Browsers rely on the CTAP2 protocol to interact
with authenticators, enabling the signing of a challenge to prove possession
of a private key.

Key-based authentication eliminates the security challenges we discussed


in the first chapter. Threats such as password spraying, credential stuffing,
and traditional or online phishing attacks are effectively mitigated by the use
of passkeys.

In the next chapter, we focus on passkeys from a developer's perspective.


We will dive into the browser APIs, the newly introduced form controls, and
the details of the underlying protocols.
Ebook The passkeys handbook 22

Chapter #3 In previous chapters, we have discussed how to use passkeys to securely


authenticate to applications. We have been light on details, and purposefully
so. In this chapter, we put on our developer hat and look at passkeys from a
The technicalities technical perspective. We will dive deep into the Web Authentication API, its
of passkeys configuration parameters, and the UI elements to support passkeys.

Throughout this chapter, we will show code snippets on how to use


passkeys in practice. If you want to play around with passkeys yourself,
there are a few useful options:

• Get your hands dirty and build a simple demo with an HTML registration
page and an HTML login page.

• Use the webauthn.me Debugger to play around with all the different API
configuration parameters and see their effect in your browser.

• Visit the Passkey playground to see the detailed parameters and


metadata of authenticators, requests, and responses.

By the end of this chapter, you will know everything you need to know to
handle passkeys in your frontend applications.

The JavaScript APIs behind passkeys

Passkeys are built on top of the Web Authentication API, which has been
around longer than passkeys have. The Web Authentication API, WebAuthn
in short, offers a JavaScript API to enable key-based authentication for web
applications. Even before passkeys were introduced, WebAuthn enabled
key-based authentication with authenticators such as Yubikeys, either as
primary or secondary authentication factors.

In this section, we dive into the details of the Web Authentication API but
limit ourselves to concepts relevant to enabling passkeys. We refer to MDN
for a complete reference on the Web Authentication API.
Ebook The passkeys handbook 23

Creating a new passkey


The Web Authentication API On a high level, creating a passkey is ridiculously straightforward. The code
(WebAuthn) and FIDO2 are closely
example shows you how to use WebAuthn to trigger the creation of a new
related technologies that play crucial
roles in the movement toward credential by calling the navigator.credentials.create function. This
secure and passwordless web call will pop up the browser UI (see image below), prompting the user to
authentication.
select an authenticator and create a new key pair.
WebAuthn is a web standard
published by the World Wide Web
Consortium (W3C) that specifies how
web applications can integrate client-
const credential = await navigator.credentials.create({
side authenticators. It allows users
publicKey: {
to log in to Internet accounts using
...
their preferred devices (e.g., security
}
keys, mobile phones, or biometrics)
});
without needing a password. This
standard focuses on defining the
browser-based JavaScript API, along
with all the data objects that are
being passed back and forth. Note
that the standard does not cover the
interactions between the frontend This is the pop-up window shown by the browser when the create method is executed.
and the backend to implement this
authentication mechanism.

FIDO2 is the overarching standard


that includes WebAuthn as one
of its core components. FIDO2 is
a set of technologies developed
by the FIDO Alliance that enables
users to leverage common devices
to easily authenticate to online
services in both mobile and
desktop environments. FIDO2
encompasses the WebAuthn API for
web authentication and the Client
to Authenticator Protocol (CTAP) for
communication between devices
(such as a phone and a laptop).

Unfortunately, the omitted details hidden behind the ... add quite a bit
of complexity. The code snippet below shows a typical configuration for
creating a new passkey to authenticate users to a web application. Let's
take a closer look at the different parameters that are provided here.
Ebook The passkeys handbook 24

const credential = await navigator.credentials.create({


publicKey: {
rp: {
id: 'app.example.com',
name: 'Example App',
},
user: {
id: Uint8Array.from(serverData.userHandle, (c) => c.charCodeAt(0)),
name: userData.email,
displayName: userData.name,
},
pubKeyCredParams: [
{ type: 'public-key', alg: -7 },
{ type: 'public-key', alg: -257 },
],
challenge: Uint8Array.from(serverData.challenge, (c) => c.charCodeAt(0)),
authenticatorSelection: {
residentKey: "required",
requireResidentKey: true,
}

}
});

Let's explore each of these configuration parameters in more detail.

• The rp field identifies the relying party, which is the web application
registering the passkey.

• The name property is a human-readable identifier.

• The id value typically matches the application's full hostname, even


though a parent domain is also allowed.

• The user field contains metadata about the user to which this passkey
belongs.

• The name contains a username.

• The displayName contains a human-readable name.

• The id parameter represents a user handle that identifies the user


during web authentication flows. This handle should not contain
Personal Identifiable Information (PII). In this case, the server
provides the user handle during passkey registration. We revisit
this at the end of this chapter when we discuss an entire end-to-
end passkey registration flow.
Ebook The passkeys handbook 25

• The pubKeyCredParams allows us to define which signature algorithms


we deem acceptable. The numerical values refer to specific algorithm
configurations, but there's some confusion as to which concrete
algorithms apply to which specific use cases. What matters here is to
know that the algorithms selected here enable almost all authenticators.

• The challenge contains a challenge that needs to be signed after creating


the key pair. This allows the backend to verify that the credential was
successfully generated and that signature validation works as intended.
The backend provides the challenge, as we will elaborate on later.

• The authenticatorSelection determines which authenticators are


acceptable to this particular application. In this configuration, we require
an authenticator to have a resident key. Concretely, this means that the
authenticator should be able to store keys that contain user information,
which makes them discoverable during the authentication process. This
requirement ensures that older USB security keys that only support MFA
are excluded from the authenticator selection process.

Finally, the create function returns a promise, which resolves into an object
of the type PublicKeyCredential . This object contains the metadata
about the generated key pair, along with the signature of the challenge. Note
that the response does not include the private key, which is securely stored
within the authenticator.

The generated key data is sent to the server, where it is stored with the
user's account. We discuss these details later in this chapter.

Authenticating with a passkey


Implementing authentication with passkeys is more straightforward than
handling registration, as we no longer need to provide the configuration
details. The code snippet below illustrates how to ask the browser to sign a
challenge using one of the existing authenticators. The image below shows
the effect of calling get .

const credential = await navigator.credentials.get({


publicKey: {
rpId: 'app.example.com',
challenge: Uint8Array.from(serverData.challenge, (c) => c.charCodeAt(0)),
}
});
Ebook The passkeys handbook 26

This is the pop-up window shown by the browser when the get method is executed.

The code above will trigger the browser to display the UI, allowing the user
to select a relevant authenticator associated with app.example.com . After
the user has approved the authentication, for example, using FaceID, the
authenticator will sign the challenge using the private key for
app.example.com .

Just like with registration, this function returns a promise that resolves
into an object of the type PublicKeyCredential . The data contains
information about the key, the user information embedded in the key, and
the signature of the challenge.

The generated key data is sent to the backend, which will verify the
signature before considering the user as authenticated. We discuss these
details later in this chapter.

Different types of passkeys


The Web Authentication API, and by extension passkeys, support different
types of authenticators that can be used. In this section, we dive deeper
into the configuration options, enabling you to make an informed choice for
your use cases.

During the registration of a passkey, the options for the create function
include an authenticatorSelection property. This property allows fine-
grained configuration of the desired type of passkey that should be created.
Omitting the value allows both types of authenticators.
Ebook The passkeys handbook 27

The authenticatorAttachment property


One field of this authenticatorSelection property is
authenticatorAttachment . This field supports two values: platform or
cross-platform .

Setting this to platform explicitly states that the passkey authenticator


should be built into the platform (e.g., a Macbook using the macOS keychain
and fingerprint authentication). The value cross-platform requires the
authenticator to be explicitly portable (e.g., a phone or a USB security key).
Omitting the value allows both types of authenticators.

The userVerification property


Another field of this authenticatorSelection property is
userVerification . This field supports three values: required ,
preferred , or discouraged .

When set to required , any use of the authenticator for signing a challenge
requires verifying the user's identity. Example verification procedures
are biometric scans, prompting for a master secret, or a PIN code. When
set to preferred , verification of the user should be obtained, but can
be skipped by the authenticator if it is deemed infeasible (e.g., a closed
laptop lid preventing access to a fingerprint sensor). Finally, when set to
discouraged , the authenticator can operate without verifying the user's
identity.

The default setting is preferred , and the exact setting mostly depends on
the sensitivity of the application. Whenever possible, it is recommended to
set this to required .

Integrating passkeys into authentication forms

In the previous chapter, we have illustrated how the browser handles


passkey authentication using modal dialog windows. While this native UI
makes it easy to implement passkey support, it can also significantly impact
the user experience.

One example scenario comes to light when an application supports


both passwords and passkey authentication. Imagine the user opting for
passkeys, but the browser realizes that there are no passkeys known for the
current application. This would leave the user stuck with having to abort the
operation in the modal dialog and then continue with traditional password
authentication.
Ebook The passkeys handbook 28

To avoid this negative impact on the user experience, browser vendors have
implemented support for a conditional UI, supported by a feature called
conditional mediation. This new concept allows the frontend to start passkey
authentication on the condition that the browser can find a passkey for
the current application. The UI typically allows the user to select one of the
passkeys as an autocomplete option underneath the form element.

Concretely, this means that a traditional authentication form can be


augmented with passkey support for users who have created a passkey. For
users with no passkeys, the user experience remains the same as before
(until they register a passkey with their account).

Let's look at how to implement this conditional UI step by step.

Passkey autocomplete
The first step to enable the conditional UI is to tell the browser where to show
the option to use a known passkey. In the code snippet below, you can see
that we have added webauthn to the form field's autocomplete attribute.

<input type="text" name="username" autocomplete="username webauthn" ...>

The image below shows what it will look like when we tell the browser to
start the passkey autocomplete process. Note that the browser can auto-
discover passkeys, so there's no need to fill out a user identifier. In the next
topic, we will explain how to trigger this UI from JavaScript.

Showing passkeys with form-based autocomplete.


Ebook The passkeys handbook 29

Launching the conditional authentication request


Once the page is loaded, the application can automatically start the
passkey authentication flow with conditional mediation enabled. This
will ensure that if passkeys can be found, they will be shown as an
autocomplete option on the authentication form. When the user selects
one of these passkeys, the browser UI will take over and complete the
verification process as needed for this particular passkey (e.g., completing a
check with your phone, scanning your finger, etc.).

To start the flow with conditional mediation enabled, we need to add the
property mediation:'conditional' to our authentication step. The code
snippet below shows how to do that.

const credential = await navigator.credentials.get({


publicKey: {
rpId: 'app.example.com',
challenge: Uint8Array.from(serverData.challenge, (c) => c.charCodeAt(0)),
},
mediation: 'conditional'
});

If you're building applications that need to support older browsers, you


may need to add feature detection before launching a conditional passkey
authentication flow. The code snippet below shows how to check if
conditional mediation is supported.

await PublicKeyCredential.isConditionalMediationAvailable();

Exploring the end-to-end passkey flows

Now that we have covered most of the details surrounding passkeys, we


can zoom out and look at all the steps in an end-to-end registration and
authentication flow. We will focus on the details that are returned by the
create and get operations of the Web Authentication API, along with the
interactions between the frontend and the backend.

Before we dive into the different steps of the flow, there are two important
concepts we need to clarify: an attestation and an assertion.
Ebook The passkeys handbook 30

Attestation and assertion


The TL;DR version of attestation vs assertion goes as follows. Attestations
are used during the registration phase and represent the authenticator
acknowledging the creation of a new key pair. Assertions are used during
the authentication phase and represent the authenticator proving that a
specific key was used.

Now, there's a bit more to it, so let's look into the details of both.

Attestation
Every authenticator comes with a built-in cryptographic certificate and
corresponding key pair, which is uniquely linked to the authenticator
manufacturer. For example, a certain batch of Yubikey 5C authenticators
will have the same attestation certificate built-in, defining the properties of
the authenticator and the manufacturer. The same holds for other types of
authentications, such as mobile phones. All models of Samsung Galaxy S8
phones produced at a certain time or in a certain production run will have
the same attestation certificate issued by Samsung.

When an authenticator generates a key pair during the registration


phase, it will also sign the newly generated key material using the private
key associated with its attestation certificate. This ensures that the key
is effectively associated with a specific authenticator. If desired, the
relying party that is asking for a new passkey can use this information
to ensure the passkey is being generated by an approved authenticator.
The attestation data follows a specific, predefined format, as defined in
the Web Authentication API spec. It is, however, important to note that
in many passkey scenarios, the relying party does not really care which
authenticator has been used. In that case, the attestation certificate is not
used and no restrictions on the type of authenticator are enforced.

Assertion
Assertions are a bit more straightforward. An assertion is basically the
response given by the Web Authentication API when asked to sign a
challenge with a key from the authenticator. The assertion object contains a
couple of specific properties, which we will discuss later when talking about
the authentication flow.

Note that the signature used in the assertion response is generated by the
private key of the passkey, while the signature in the attestation response is
generated by the private key built into the authenticator.
Ebook The passkeys handbook 31

An end-to-end passkey registration flow


The image below illustrates an end-to-end passkey registration flow. Let's
discuss the different steps one by one.

The end-to-end flow of a passkey registration.

1. Enter email address for registration


2. Start registration with email
3. Generate a user handle and registration
challenge
4. User handle and challenge
5. Challenge signature request for
https://example.com
6. Prompt the user to select an authenticator
for https://example.com
7. Create key pair, sign the challenge and
issue a response
8. Credential details response
9. Credential details response
10. Verify signature with provided public key
and store the new user with their public key

When registering a new passkey, the WebAuthn API call expects a user
handle. It is a common practice to defer the generation of these user
handles to the backend. To facilitate this, the registration flow starts by
prompting the user for their email address and name information (step 1).
This information is then included in a request to the backend (step 2).

Between steps 2 and 3, the backend can decide to require the user to verify
their email address. For example, the backend can send a secret code
to the email address and prompt the user to enter this code through the
browser. Since this step is optional, we have not shown it on the diagram
here.

Once the backend decides to move forward with registering a passkey for
the user, it needs to provide the browser with a user handle. This handle
should not contain any PII that allows it to be linked to a user. Typical
implementations generate an opaque value that is associated with the
user's account. There are notadditional guidelines that specify the exact
contents of this opaque value. One option is to generate random unique
identifiers as the user handles and store them with the user's account.
Ebook The passkeys handbook 32

Another option is to use the email address to calculate an HMAC and use
that value as the user handle. Whatever you decide to use, keep in mind that
the user handle is provided during authentication and is typically used to
look up a user account from the user store.

When the frontend receives the user handle and challenge (step 4), it can
launch the passkey registration request by calling the
navigator.credentials.create function (step 5). The user may be
prompted to select an authenticator (step 6), after which a key pair is
generated, and the challenge is signed (step 7). The browser receives the
response from the authenticator (step 8) and exposes it to the frontend
as the PublicKeyCredential . This PublicKeyCredential contains a
response property, which is an AuthenticatorAttestationResponse
during the registration phase.

In the next step, the frontend is supposed to collect this credential


information and send it to the backend (step 9). There is no standardized
way to format this request, but a best practice is to encode the binary data
to make it safe to transport in HTTP requests. The list below shows which
properties of the PublicKeyCredential response should be included and
how you should handle them:

• id : This property contains a base64url-encoded ID of the created passkey.

• response.clientDataJSON : This property contains contextual


information about the passkey creation from the client side. Values
include the challenge used and the origin of the client. The value is an
ArrayBuffer , so it requires encoding before including it in the request.

• response.attestationObject : This property contains the public key


from the key pair created by the authenticator. Additionally, this property
can contain information from the authenticator's manufacturer, enabling
enterprise-level controls over which authentications are acceptable.
Typical web-based passkey scenarios will not rely on this feature. The
value is an ArrayBuffer , so it requires encoding before including it in
the request.

• authenticatorAttachment : The property indicating if the passkey was


platform or cross-platform. The meaning of these values is the same as the
values provided during the creation of the passkey, as discussed earlier.

• type : This field is always set to public-key for passkeys.


Ebook The passkeys handbook 33

Finally, in step 10, the backend is responsible for handling the passkey
registration information. Going into the nitty gritty details of validating the
binary data would take us too far for this ebook. Instead, we'll highlight the
backend's responsibilities:

• Validate the incoming data and ensure all properties match the
expectations for the generated passkey.

• Verify the provided signature using the included public key and
challenge to ensure that the passkey was effectively used to sign the
challenge.

• Store the newly created public key and passkey metadata along with
the user's details.

Note that the first two of these responsibilities are typically handled by a
server-side passkey library, absolving developers from the responsibility
of handling these details themselves. Some libraries require the entire
PublicKeyCredential object as input. In that case, it is recommended to
base64url-encode the whole object and ship it to the server.

An end-to-end passkey authentication flow


The image below illustrates an end-to-end passkey authentication flow with
additional details for conditional mediation. Let's discuss the different steps
one by one.

The end-to-end flow of a passkey authentication.

1. Get a challenge
2. Generate an authentication challenge
3. Challenge
4. Start conditional mediation
5. The user selects an exisitng passkey
6. Challenge signature request for
https://example.com
7. Sign the challenge and create a
response
8. Signature response
9. Signature response
10. Verify signature with stored public key
Ebook The passkeys handbook 34

Before the frontend can trigger passkey authentication with conditional


mediation, it needs to obtain a challenge from the backend (steps 1, 2, and 3).
With this challenge, the frontend starts the conditional UI (step 4), allowing
the user to use the autocompletion feature to select a passkey (step 5).
This user interaction triggers the signing (steps 6 and 7), which results in a
PublicKeyCredential object being returned to the frontend (step 8). This
PublicKeyCredential again contains a response property, which is now
an AuthenticatorAssertionResponse .

In the next step, the frontend is supposed to collect this credential information
and send it to the backend (step 9). Similar to the registration phase, there is
no standardized way to format this request, but a best practice is to encode
the binary data to make it safe to transport in HTTP requests. The list below
shows which properties of the PublicKeyCredential response object
should be included and how you should handle them:

• id : This property contains a base64url-encoded ID of the created passkey.

• response.clientDataJSON : This property contains contextual


information about the passkey creation from the client side. Values
include the challenge used and the origin of the client. The value is an
ArrayBuffer , so it requires encoding before including it in the request.

• response.authenticatorData : This property contains information


about the authenticator, including whether the authenticator verified
the user's presence or required the user to authenticate locally to the
authenticator. The value is an ArrayBuffer , so it requires encoding
before including it in the request.

• response.signature : The signature of the challenge that needs to


be verified by the backend. The value is an ArrayBuffer , so it requires
encoding before including it in the request.

• response.userHandle : The user handle that has been provided earlier.


This allows the backend to query its database for the correct user
information. The value is an ArrayBuffer , so it requires encoding before
including it in the request.

• authenticatorAttachment : The property indicating if the passkey was


platform or cross-platform . The meaning of these values is the same
as discussed earlier.

• type : This field is always set to public-key for passkeys.


Ebook The passkeys handbook 35

Finally, in step 10, the backend is responsible for handling the passkey
authentication information. Going into the nitty gritty details of validating the
binary data would take us too far for this ebook. Instead, we'll highlight the
backend's responsibilities:

• Validate the incoming data and ensure all properties match the expectations
for the user's passkey (e.g., ensure it matches a stored credential).

• Verify the provided signature using the registered public key.

Similar to the registration phase, these responsibilities are handled by


server-side passkey libraries, absolving developers from the burden
of handling these details themselves. Some libraries require the entire
PublicKeyCredential object as input. In that case, it is recommended to
base64url-encode the whole object and ship it to the server.

Summary

Passkeys are built on two core building blocks: the Web Authentication
API for handling credentials and new UI features to enable a smooth user
experience. By using conditional mediation, frontends can rely on the
browser's autocomplete UI to allow users to select a known passkey during
authentication. With these features, passkeys have everything they need to
fully replace passwords as the primary authentication mechanism.

In the next and final chapter, we look into ways to integrate passkeys
into your application architecture. We also explore using Auth0 to adopt
passkeys with little development effort.

Chapter #4 In previous chapters, we have covered what passkeys are, how users can
authenticate with them, and how developers can integrate support in
applications.
Integrating
In this final chapter, we approach passkeys from the perspective of a
passkeys in your software architect. We explore challenges and best practices for adopting
applications and integrating passkeys into our application. Concretely, we will tackle:

• Strategies to move towards passkey adoption.

• Challenges when using passkeys in multiple applications.


Ebook The passkeys handbook 36

• Simplifying your passkey adoption process using OpenID Connect.

• Setting up Auth0 to use passkeys.

By the end of this chapter, you will be ready to use passkeys efficiently in
any application. Let's get started and wrap things up with everything you
need to know about bringing passkeys into the real world.

Gradual passkey adoption

One of the major challenges with passkeys is enabling support and


gradually driving adoption forward among your users. While early adopters
will applaud you for adding support for passkeys, the reality remains that
most users will have no clue what they mean. It will likely take a few years
before passkeys become mainstream enough for large-scale adoption.
So, how do you handle these scenarios? What can you do to drive passkey
adoption without impacting the users who are not ready to start using
passkeys?

In this section, we discuss a couple of scenarios that may be helpful to you.


Of course, your mileage will vary, so you will need to evaluate what may
work for you.

Offering passkeys alongside password-based authentication


If you want to offer passkey authentication as an alternative to password-
based authentication, the conditional mediation mechanism is the perfect
approach. It allows a single authentication form to support both types of
authentication.

Alternatively, you could switch to an identifier-first authentication flow. In


such a flow, the user first enters their identifier, such as an email address.
When a user has a passkey, this identifier field can support autocompletion.
When the user manually enters an email address, they do not have a
passkey, so the form prompts them for a password.

Going full out on passkeys


If you can turn off passwords and adopt passkeys across the board, you are
in a lucky position.

In that case, you can choose whether you want to use conditional mediation
or not. When not using conditional mediation and directly starting a passkey
authentication, the browser will start the popup-based flow, allowing
the user to select a passkey from the available options. When using the
Ebook The passkeys handbook 37

conditional mediation mechanism, the browser can use its autocomplete UI


to present the user with a dropdown list of known passkeys, which may be
a more desirable user experience.

While our recommendation would be to drop conditional mediation and


simply prompt for passkeys, we recommend that you try both approaches
and see which suits you best.

Passkey authentication across different applications

As discussed in the previous chapters, passkeys are tied to the application's


domain. As a result, they can only be used on that particular domain.
Concretely, this means that if a passkey is created for app1.example.com ,
it is impossible to use that exact passkey on app2.example.com , even if
the backends share the same database of users and credentials.

In this section, we'll explore two patterns that enable you to implement
passkey authentication in different applications without creating an
individual passkey for each application.

Sharing passkeys between sibling applications


Passkeys have a built-in relaxation that allows them to be created for a
parent domain. So, for our concrete example from before, with
app1.example.com and app2.example.com , it is possible to create and
use passkeys for example.com .

Concretely, we can enable this behavior by using example.com as the ID


of the relying party in both the navigator.credentials.create and
navigator.credentials.get functions. The browser enforces this
restriction, effectively preventing you from using a different domain than the
exact parent of your application's domain.

Finally, note that while this mechanism is supported, a long history of


subdomain-based attacks has taught us that sharing critical resources
across sibling applications is not a recommended pattern. If you have that
requirement, we recommend looking into OpenID Connect as a by-design
Single Sign-On solution.
Ebook The passkeys handbook 38

Implementing passkeys with OpenID Connect


Configuring passkeys for the Now that we have come this far, I'm sure you have started to wonder how
parent domain can also be used
you will implement passkey support in your applications. While the JS APIs
as a safeguard against future
infrastructure updates. for passkeys may be simple, getting all the details right is quite challenging.
Let's assume you are generating And we have yet to talk about handling keys and signatures on the
and using passkeys on backend, which is a whole other can of worms.
login.example.com . However, over
time, you would like to transition
To be honest, we do not recommend implementing passkeys directly in
towards the auth.example.com
domain instead. With tightly-scoped your application. Instead, we recommend offloading authentication from
passkeys, this would require the your application to a central identity provider using the OpenID Connect
re-initialization of all passkeys for all
users. However, this transition would protocol. This way, your application no longer has to worry about managing
work seamlessly if the passkeys are users, handling authentication, and implementing user interfaces to
scoped to example.com .
support authentication. All it needs to do is start an OpenID Connect
Note that this relaxation only works
within the same parent domain.
(OIDC) flow using one of the many libraries available for free.
Moving towards example.io would
still invalidate all registered passkeys. When you use OIDC to offload authentication, the identity provider handles
the user authentication process. Consequentially, they will implement
all the details for handling passkeys, both on the frontend and on the
backend. Once the user is authenticated, your application will receive the
identity token with information about the authenticated user, and you're all
done.

Finally, using OIDC makes it trivial to enable passkey-based single sign-on


for a suite of applications, even across different parent domains.

Sounds good, right? In the next section, we look at setting up passkey


authentication with Auth0, a cloud-based identity provider that excels at
user identity.

Implementing passkeys with Auth0

The starting point for what we will cover in this section is an application
already integrated with Auth0 using OpenID Connect. Our Auth0 tenant
uses the default configuration, which relies on email addresses and
passwords for authentication. We will enable passkeys and try it out to get
a good idea of the effort it takes to implement passkey support.
Ebook The passkeys handbook 39

Configuring Auth0 to use passkeys


To enable passkeys for our Auth0 tenant, we need to make two changes.
The first change involves moving towards an identifier-first authentication
flow. The second change is enabling passkeys in our user database. Let's
dive in.

The image below shows how to enable the identifier-first authentication flow.
It is a predefined setting on the Authentication Profile configuration page.

Enabling an identifier-first login flow on Auth0.


Ebook The passkeys handbook 40

Next, we have to configure a database (connection in Auth0 terminology)


to support passkeys next to passwords. You can do that by opening the
connection's settings page and toggling the passkeys authentication
method.

Enabling passkey support for an Auth0 connection.

That's all it takes to enable passkey support in Auth0.

Using passkeys with Auth0


Let's take a look at the effect of our changes.

When we now want to log in or sign up for our application, we end up at the
following Auth0 page. Note how this page already shows the option to sign
in with a passkey.

The button at the bottom indicates passkey support.


Ebook The passkeys handbook 41

But first, let's create a user account. Clicking the Sign up link sends us here,
where we can provide an email address.

Sign up by providing an email address.

Once we have done that, we get prompted to create a passkey. Clicking


the "Create passkey" button will take us through the steps we have covered
before, where we can select an authenticator and create a passkey.

Create a passkey for our newly created account.


Ebook The passkeys handbook 42

Now that we have an account, we can try logging in with a passkey. First,
we have to log out from Auth0 to terminate the active session. When we
try to log in to our application, we end back up at Auth0. If we now click the
username field, we will be prompted to load a passkey using conditional
mediation.

Passkey authentication at Auth0 using conditional mediation.

Of course, we can also click the button "Continue with a passkey" to start
the passkey authentication process without conditional mediation. Note
that the passkey policy for the database allows you to configure if you want
to use both conditional mediation and the button, or just one of both.

Migrating from passwords to passkeys


Now that we have enabled passkeys, you might be wondering how we can
upgrade our existing user accounts to also start using passkeys. Within
Auth0, this feature is known as progressive enrollment, and it is enabled by
default.

Progressive enrollment allows users to authenticate with a username and


password, after which they are prompted to create a passkey for their
account. The image below shows this message, which will start the passkey
registration process.
Ebook The passkeys handbook 43

Upgrading an existing account with a passkey.

If you do not want to enable this prompt for existing users, you can turn it
off in the passkey settings for your database.

Disabling passwords for passkey users


As we have discussed before, passkeys are an excellent authentication
mechanism, but passwords are not going away anytime soon. Migration
towards passkeys will take quite some time, and in the meantime, we still
have passwords associated with our accounts. So, what can you do to
disable passwords?

There are two options here. The first option assumes you start from a clean
slate and want to rely solely on passkeys for authentication. At the time of
writing, Auth0 does not support disabling password-based authentication
through the dashboard. However, you can implement this yourself using
actions.

Concretely, this involves creating a new custom action and adding it to the
login flow. In this action, we can check if the user attempted to authenticate
with a password and reject that authentication attempt.

The second option handles existing databases, where only some users
have enabled passkeys. In that scenario, we also use an action, but
the logic is more complicated. Concretely, we would check if the user
Ebook The passkeys handbook 44

logged in with a password. If that is the case, we query the available


authentication methods for this user using the Auth0 Management API.
If the authentication methods for a user list a passkey, we can reject the
password-based authentication and inform them to use passkey-based
authentication instead.

Note that neither approach can affect the login experience UI. However,
they both effectively prevent password-based authentication, either for the
legitimate user or an attacker abusing their credentials.

Summary

While each application is different, adopting passkeys will likely be a


long-term process where passwords and passkeys will co-exist. It is
recommended that you allow tech-savvy users to adopt passkeys, disable
passwords, and re-evaluate how to proceed with the remainder of the users
over time.

As you have discovered in this ebook, implementing passkeys can be


challenging, but it's definitely worth it. If you offload authentication with
OpenID Connect, the responsibility of implementing passkeys shifts toward
the central identity provider.

And the best part? Auth0 offers passkey support out of the box.
Ebook The passkeys handbook 45

Passkeys: A one page summary

What are passkeys? Passkeys are a new form of user authentication designed to replace traditional passwords. Instead
of sending a text-based secret, the password, from the browser to the backend, passkeys rely on
a key-based authentication mechanism. Passkeys use cryptographic signatures to prove the user
possesses a secret key stored on an authenticator.

Passkeys are a concrete implementation of FIDO2/WebAuthn, which defines discoverable key-


based user authentication. The CTAP2 protocol defines how the client can communicate with an
authenticator, such as a mobile phone, a USB security key, or credential management software. The
Web Authentication API defines how a website can invoke passkey functionality in the browser.

What are the security The underlying cryptographic mechanics of passkeys effectively mitigate various threats against user
properties of passkeys? authentication. Here's an overview of the security properties of passkeys:

Contrary to passwords, passkeys do not need the backend application to store sensitive information.
Data breaches do not impact the security of passkey-based authentication.

• Passkeys are not vulnerable to brute-force attacks, password spraying attacks, and credential
stuffing attacks.

• Passkeys are uniquely linked to a website's domain, making them invulnerable to phishing
attempts. Even the most advanced online phishing attacks are effectively mitigated by passkeys.

• Passkeys that require user verification provide the same properties as multi-factor authentication
in a single authentication step. They rely on something the user possesses (i.e., a cryptographic
key) and biometrics or a PIN to unlock the key.

How can you implement Implementing passkeys impacts the application on three different levels: HTML,
passkeys? frontend JavaScript code, and backend code. The list below summarizes what
is needed to implement passkey registration and authentication.

• The frontend code needs to offer a way for a user to register a new passkey. To handle this, the
underlying Web Authentication API offers the navigator.credentials.create operation.

• The backend needs to accept public passkey data from the frontend and store this information
with the user data in the database.

• It is recommended that the frontend HTML augments the authentication form with an
autocomplete="webauthn" property to support the passkey autofill UI.

• The frontend code needs to launch passkey authentication using the


navigator.credentials.get operation offered by the Web Authentication API. Note that the
frontend can choose to enable the conditional UI with this call.

• The backend needs to accept the passkey signature and verify that the signature is indeed
correct and made by a passkey that is known for this particular user. The backend additionally
verifies the origin associated with the user's authentication. If everything checks out, the user is
now authenticated.

How do you integrate Given the complexity of implementing passkeys, it is recommended to offload this responsibility to a
passkeys into your central identity provider. Concretely, this means that the application implements user authentication
application? with OpenID Connect. Running an OpenID Connect flow will require the user to authenticate at the
identity provider, where they can use a passkey.
Auth0 supports passkeys out-of-the-box on all their plans, including the free plan. Enabling passkey
support in Auth0 is a matter of clicking a few options in the dashboard with zero code effort.

Wrapping up We hope you got everything you wanted out of this ebook. What's left is to set up your Auth0 tenant
with passkeys and help your users towards a future with secure authentication.
Ebook The passkeys handbook 46

Disclaimer These materials and any recommendations within are not legal,
privacy, security, compliance, or business advice. These materials
are intended for general informational purposes only and may not
reflect the most current security, privacy, and legal developments
nor all relevant issues. You are responsible for obtaining legal,
security, privacy, compliance, or business advice from your own
lawyer or other professional advisor and should not rely on the
recommendations herein. Okta is not liable to you for any loss
or damages that may result from your implementation of any
recommendations in these materials. Okta makes no representations,
warranties, or other assurances regarding the content of these
materials. Information regarding Okta’s contractual assurances to its
customers can be found at okta.com/agreements.

Any products, features, or functionality referenced in this material that


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

About Okta
Okta is the World’s Identity Company. As the leading independent Identity partner, we free everyone to safely use any technology —
anywhere, on any device or app. The most trusted brands trust Okta to enable secure access, authentication, and automation. With
flexibility and neutrality at the core of our Okta Workforce Identity and Customer Identity Clouds, business leaders and developers
can focus on innovation and accelerate digital transformation, thanks to customizable solutions and more than 7,000 pre-built
integrations. We’re building a world where Identity belongs to you. Learn more at okta.com.

Auth0 is a foundational technology of Okta and its flagship product line — Okta Customer Identity Cloud. Developers can learn more
and create an account for free at Auth0.com.
Okta Inc.
100 First Street
San Francisco, CA 94105
[email protected]
1-888-722-7871

You might also like