Keycloak Workshop for Step Up with MFA Biometrics Authentication (Passkeys) and Passwordless experience with Passkey autofill

Martin Besozzi
10 min readJul 20, 2023

In Short

This article primarily explains two main concepts: step-up authentication involving Multi-Factor Authentication (MFA) using Biometric authentication, specifically using Passkeys. It also outlines the process of transitioning to a passwordless experience through the use of Passkeys.

It doesn’t matter whether your current application utilizes authentication via username and password. I will demonstrate how you can also provide Passkey login, offering a remarkably smooth experience through WebAuth Conditional UI or Passkey autofill during the transition to a passwordless login. Additionally, we will explore how we can gradually encourage existing users to register a passkey during the sign-in process 😄.

All resources are available on the following workshop ❤️ on GitHub. Therefore this article is more focused on the practical aspect, allowing you to try deploying the sample project with one click in a Docker Compose environment.

Step-up + MFA with Biometric Authentication (Passkeys)

As the first step, if you are not familiar with the basic concept of step-up authentication with OpenID Connect, please check out my previous article. It will provide the groundwork and an overview of the idea

So, you’re still here. I will now explain the new additions I’ve made to the PoC that haven’t been described yet. I decided to use Passkeys based on FIDO Alliance and W3C standards. Passkeys replace passwords with cryptographic key pairs, offering strong credentials, protection from server leaks, and defense against phishing. In the workshop, I use Passkeys as a 2-factor authentication method for the Global Bank Portal, leveraging the security capabilities of devices like Touch ID and Face ID. Additionally, I implement a passwordless experience in the Bank Loan Portal using Passkeys.
Keycloak supports WebAuthn, and Passkeys are essentially the platform’s implementation of WebAuthn. I won’t go into too much detail here because there is a lot of content available.

Another cool thing — at least for me, the sample shows how to implement OAuth 2.0 Step-up Authentication based on the OAuth 2.0 Step-up Authentication Challenge Protocol standard (RFC 9470) - thanks to 🖤 Vittorio . This enables the API to implement step-up authentication by understanding the required Authentication Context Level (acr). If the level is not sufficient, the API informs the client that it needs to trigger step-up authentication. This significantly improves the user experience, as demonstrated in the demo.

Passworless experience with Passkey autofill

When moving from passwords to passkeys, there might be challenges. User experience matters a lot. The default “modal” experience might not be good in some cases. But using the passkey with autofill (WebAuth Conditional UI) feature can improve the login process. Currently, Keycloak doesn’t have this feature, but I’ve made a custom SPI authenticator called keycloak-webauthn-conditional-mediation to add it.

As they say, a picture speaks a thousand words. In the demo, you’ll be able to see the difference between the Out-of-the-Box Passkey login [1] experience and the Passkey Autofill feature [2]:

Default Passkey Login experience [1]
Improved Passkey with Autofill [2]

Progressive passkey enrollment during the sign-up or sign-in process

In the latest version of the workshop, we added the feature of allowing the user to decide when they want to register the Passkey with the custom SPI (WebAuthn Authenticator Conditional Enrollment), either during the sign-up or sign-in process.

If the user doesn’t have any passkey registered, it will be a common scenario when transitioning from a password-based to a passwordless experience. The sign-in process will ask the user if they want to upgrade to a Passkey to improve the security and UX experience.

Workshop Overview

Components Summary

In the demo, you will log in to the Global Bank Portal, which is an SPA integrated with Keycloak using OpenID Connect. It supports login with one-factor username and password, and two-factor with Passkeys. Only authenticated users with MFA Passkeys can access and manage their bank accounts.
On the backend side, the Bank Account API is a Spring Boot application protected by OAuth 2.0, acting as an OAuth2 Resource Server. The API follows the standard (RFC 9470) to trigger the OAuth step-up authentication challenge if the presented access token offers insufficient authentication based on the acr claim. Additionally, the Bank Portal is capable of handling unauthorized access gracefully and performs the step-up authentication process in a lovely way.

The Bank Loan Portal is a Vue application integrated with Keycloak using OpenID Connect. The Portal is authenticated with Keycloak, providing a passwordless experience with Passkeys.

Workshop Architecture

On the Keycloak side, I’ve configured the authentication flow to support Web Authn and the following authentication level mapping:

Keycloak ACR to Loa Mapping
Keycloak Browser MFA with Passkeys (Web Authn)

I’ve split the login process into several steps. First of all, I only request the username. Please avoid asking the user too many things in the first step, like the traditional username and password form — we are not in the eighties anymore 😄. With just the username, it’s enough to personalize the login user experience in the next steps. Then, if needed, I’ve requested the completion of the remaining MFA login steps. This flow utilizes the OOTB Webauthn Passwordless Authenticator since passkeys autofill is unnecessary.

The OOTB Keycloak Browser Passwordless appears as follows:

Keycloak OOTB Browser Passwordless

In the demo, I utilized the custom SPI called “webauthn conditional mediator.” This allows me to offer Passkey autofill while retaining the option for login through a username and password. Also we use the custom SPI “WebAuthn Conditional Enrollment “ , which will help by asking the user if they want to move to a passwordless experience with a Passkey.

Here’s what you’ll see in the demo:

This flow supports the following features, thanks to the custom SPI:

  • Enabling passkey autofill when supported by the browser.
  • Displaying the “Sign with passkeys” button if passkey autofill is not available.
  • If Passkeys (Webauthn) are not supported, it will present the traditional username and password login option.
  • The user will be asked to register a Passkey if they don’t have any passkey registered.

It will be used for the Bank Loan portal. The app becomes cooler as it removes the need for passwords and simplifies the login experience with this cool feature of passkeys autofill.

Workshop Sequence diagram

Here is a sequence diagram that explains how the components work together.

Scenario: Step-Up MFA with Passkeys for Managing Global Bank Accounts

In the case of the Global Bank Portal (use cases #1 and #2). It’s quite easy to follow.

Global Bank step-up authn sequence diagram

Not too much to add here, but I love the communication between the Bank API and Portal to be on the same page regarding the desired authentication mechanism based on the acr claim (step #2). This means that the API will return a 401 Unauthorized status code along with the WWW-Authenticate header containing the error message ‘insufficient_authentication_level’ and the defined acr_values, indicating to the Bank Portal what ACR value to request from the identity provider. On the client side, the Bank Portal is able to interpret this error and redirect the user to perform the step-up authentication.

Then, the possibility of doing MFA biometric authentication with Passkeys (step #3). In my case, my Mac Touch ID is making me cry tears of happiness.

Points #1 and #2 follow the OpenID Connect Standard, where the Bank portal specifies the desired authentication mechanism (acr claim) to the IdP. You can also use the acr_values parameter in the authentication request for that.

Test Cases

Here is a summary of the main test cases, but you can try deploying the workshop yourself:

The workshop has Global Bank portal (Use case 1 and 2) with the following requirements:

  • Supports OIDC login with one-factor username and password and two factor (MFA) with Passkeys
  • Only authenticated user with MFA Passkeys can access and manage their bank accounts
  • If the user is not authenticated with MFA when managing bank accounts, it triggers the step-up to MFA :) in a lovely way thanks to the step-up authn challenge provided by the API.

The Bank Loan portal (Case 3) has the following requirements:

  • Supports OIDC login with Passkeys → A super nice way to do the login with Passkeys autofill
  • Only authenticated users can view and apply for the loans

Use case 1: Sign up on the Bank Portal

1.1) Access to the Bank Portal and proceed with user registration:

1.2) Complete the user information (step 1):

3) Click in Upgrade to Passkey button. You can do it during the sign-up or sign-in process.

4) Register the Passkeys (step 2):

1.4) You will see the Bank Portal Home

Use case 2 : Sign in to the Bank Portal for Managing Bank Accounts

2.1. Access to the Bank Portal and Sign In:

2.2 Complete the username and password (1 factor):

2.3 You will see to the Bank Portal Home and Go to the Identity Profile section and check your ACR claim: loa1

2.4 Go to the Manage Bank Accounts. You will see that Authn Level is not enough, with a lovely modal that handles the step-up authentication based on the access denied information. Proceed to access with MFA

2.5 Complete the user name and password (1 factor):

2.6 Choose your passkey (in the MFA scenario, Passkey will appear as a modal), and then proceed to verify your identity, in this case with Touch ID (2 factor). In the passwordless scenario, you’ll enjoy an enhanced experience with passkey autofill (use case #3).

2.7 You will see the Bank Account Information since you have signed in with MFA. Go to the Identity Profile section and check your ACR claim: loa2

Use case 3: Sign in Passkey autofill on the Bank Loan (Web)

3.1 Access the Bank Loan Portal and sign in

3.2 Simply click on the username, it will the passkey with autofill 🥰 and choose the passkey.

3.3 Verify your identity and the you will see will see the Loan portal home:

(Optional) If the user doesn’t have any passkey registered, it will be a common scenario when transitioning from a password-based to a passwordless experience. The sign-in process will ask the user if they want to upgrade to a passkey.

Use case 4: Sign in passwordless default experience on the Bank Loan Portal

Here are additional examples using the OOTB Keycloak Browser Passwordless feature, providing you with a better understanding of the default user experience.

Here is the login with passkey in default modal experience:

Passkey login with modal experience

Here is the login with passkey in a mobile app:

Passkey login with modal experience (mobile)

Conclusions

I hope you’ve enjoyed the article. In my opinion, this approach solves several problems in modern applications. Having the opportunity to use these standards will improve security and enhance the user experience.

Transitioning from password-based to passkey-based authentication, I believe that the passkey autofill feature enhances the user experience. This integration allows you to incorporate login within your existing username and password form. On the other hand, enabling the feature of progressive passkey registration in the sign-in process will improve the user experience in the transition to passwordless for existing users.

That’s all for this post!

Thank you for reading. If you like it, then share it!

Follow me on Twitter and GitHub: @embesozzi

Reference

--

--

Martin Besozzi

IAM Architect and Java Developer. I have been working for 16 years focused on IAM Space, Digital User, API Authz. #Keycloak #ForgeRock Twitter / GH: @embesozzi