This tutorial shows how to add passkeys to an app that has existing Keycloak users. Corbado's passkey-first web component is used for better passkey experience.
Nicolai
Created: November 23, 2023
Updated: July 24, 2024
We're currently reworking the Corbado Connect approach. Reach out if you want to get early access.
Get early access2. Keycloak Passkey Project Prerequisites
3. Overview: Keycloak Passkey Project
4. Set Up Your Corbado Account and Project
6.1. Configure Environment Variables
6.2. Create User Service Utilizing Keycloak's API
6.3. Create Passkey Login Page
7. Enable Password-Based Login as Fallback
8. Make Your Local Application Reachable for Corbado via Corbado CLI
In this blog post, we'll be walking through the process of adding passkey authentication to an app which already has an existing Keycloak user base. Since Keycloak's passkey implementation and flow isn't very user-friendly and quite difficult to set up, we use Corbado's passkey-first web component that automatically connects to a hosted passkeys backend. To keep all existing accounts and allow these to use passwords as fallback, we connect Corbado to the existing userbase via webhooks. For maximum flexibility and to touch as little as possible in the running system, we still use Keycloak as the main solution for user management (also new user who sign up with a passkey will be stored in Keycloak).
If you want to see the finished code, please have a look at our sample application GitHub repository.
The result looks as follows:
Recent Articles
⚙️
FastAPI Passkeys: How to Implement Passkeys with FastAPI
⚙️
Passkey Tutorial: How to Implement Passkeys in Web Apps
⚙️
Django Passkeys: How to Implement Passkeys with Python Django
⚙️
Laravel Passkeys: How to Implement Passkeys in PHP Laravel
⚙️
Cloudflare Passkeys: Deploying a React Passkey App on Cloudflare Pages
This tutorial assumes basic familiarity with HTML, JavaScript (in particular Node.js / Express), Docker and Keycloak. Lets dive in!
Let's get the whole picture first! We use a dockerized setup with two containers:
Our folder structure looks like this:
├── app.js ├── .env ├── src | ├── controllers | | ├── authController.js # renders views and uses Corbado SDK for sessions | | └── corbadoWebhookController.js # Takes all requests belonging to the Corbado webhook logic | | | ├── routes | | ├── authRoutes.js # All routes belonging to certain views | | └── corbadoWebhookRoutes.js # All routes belonging to the Corbado webhook | | | ├── services | | └── userService.js # Communicates with Keycloak | | | └── views/pages | ├── login.ejs # Login page with the UI Components | └── profile.ejs # Profile page showing user info
If you want to run the example right away, you can follow the Readme of the repository. This tutorial focuses on the technical implementation details.
Become part of our Passkeys Community for updates and support.
JoinVisit the Corbado developer panel to sign up and create your account (youll see passkey sign-up in action here!).
In the appearing project wizard, select Web app as type of app and afterward select Yes (My app already has users) as we already got password-based users. Moreover, providing some details regarding your frontend and backend tech stack as well as the main goal you want to achieve with Corbado helps us to customize and smoothen your developer experience.
Next, we navigate to Settings > General > URLs and set the Application URL, Redirect URL and Relying Party ID to the following values (We will host our app on port 3000):
Finally go to the credentials settings and create an API Secret by clicking on "Add new".
We will use Keycloak's Docker image to create a container which we provide with initial admin credentials. See this Keycloak guide if you want to know more about running Keycloak with Docker. Use the following command to initialize the Keycloak Docker container:
If you now visit localhost:8080/admin you should see Keycloaks admin console.
Use admin as username and also admin as password to login to the site. Switch to Users and click on Add user.
Come up with sample user details, turn on Email verified and click Create.
Switch to the "Credentials" tab and click "Set password".
Select a password, make sure to turn off "Temporary" and press "Save".
We now have got a password-based user in our app. Feel free to create more users with passwords.
For this project, we will use a simple Express app. First create an app.js file with the following code:
Note: We'll create the views in the next steps, but we already set the folder here.
Next, initialize your app with
Select a name you like and put app.js as entry point. Now we need to install Express:
That's it concerning the core of our app. We will now go on to configure the environment variables and then create the pages we need.
We will need the Corbado project ID and API secret in the next steps, so well put it into our environment variables. For this, we create a .env file with the contents of .env.example and paste our values:
To load the environment variables, we will use dotenv. Install it with
Here, we will need Keycloak's Admin API. Install the corresponding client via npm:
We now create the file services/UserService.js and inside initialize the Keycloak API client.
For our communication with Keycloak concerning our existing users, we need to provide our app with four methods:
For each of these tasks, we use a dedicated method of the admin API client and extract the data we want from the methods returned values:
Now to the login page: We first create a template in views/pages/login.ejs and paste the following HTML content:
It contains the Corbado web component as well as a Corbado script which is required for the web component to work.
Next, we need a controller which renders this ejs file when called. Create a file controllers/authController.js with the following methods:
Note: The Corbado project ID and API secret are taken from the environment variables.
The routes are still missing. To keep everything clean, well put our routes in a separate file under routes/authRoutes.js connecting specific routes with the controller methods we just created.
As a last step, we modify the core of our app (app.js) to use these routes:
After successful authentication, the Corbado web component redirects the user to the provided Redirect URL (https://localhost:3000/profile). This page displays information about the user and provides a button to log out. In the views/pages folder add a profile.ejs template with the following content:
In our next step, we will need the Corbado Node.js SDK to get the current user. Make sure to install it with
Next create a profile() method inside controllers/authController.js. As mentioned, we use the Corbado Node.js SDK together with the UserService we created in step 6.2.
Also we need a "/profile" route in our routes/authRoutes.js file:
Subscribe to our Passkeys Substack for the latest news, insights and strategies.
SubscribeWe don't want to lock out our existing users, so we enable password-based authentication as a fallback. Therefore, Corbado needs to communicate with our backend. It does so via a webhook that we will set up in our app. In the steps before, we set the webhook URL to http://localhost:3000/corbado-webhook as well as the webhook username and password. The Corbado Node.js SDK from earlier also provides code that helps to add the webhooks.
We add the username and password of the webhook to our environment variables, so that they are available when we want to authenticate an incoming webhook call.
Next, we create a controller for our webhooks under controllers/corbadoWebhookController.js which makes use of our UserService:
If a user enters their email in the web component, the webhook is triggered. The webhook function will handle it, resulting in a call of the userStatus function. If the user exists, meaning he already has a password, Corbado gives him the option to login via password.
Once the user has entered his password, another webhook call is issued resulting in a call to the verifyPassword function. There we check our database if the given credentials match and send the response back.
Now, we create a route for the webhook controller in a new file called routes/corbadoWebhookRoutes.js:
This file then gets added to our application core:
But how can the Corbado server call our webhooks if we are testing our implementation locally? Thats what well tackle in the next step.
Using the Corbado CLI we can effortlessly create a tunnel from our local instance to the outside world so that the local webhooks can be called by the remote Corbado server. Follow the docs to install the Corbado CLI. You can either download the binary for your OS, or install it directly via go:
Afterwards, we head to the CLIsettings page of the developer panel to copy our CLI secret. We login using the project ID and CLI secret:
Once logged in, we can start our tunnel using the subscribe command (with port 3000 as our Express app is running here).
Once started, the webhook running on our machine forwards requests from Corbado to our local instance. The process is then as follows:
Now, we can test if our webhook works by going to the webhooks testing page, filling out the stub data and running the tests. If everything went right, all tests will pass.
In our sample application on Github, we packed everything together using multiple docker containers, so the only thing you need to do is heading into the root directory and creating a .env file with the contents of .env.example (but your values of course) and executing
When visiting http://localhost:3000 you should see the Corbado web component.
In case you're using a password based Keycloak account, you will be asked for your password:
If the password authentication succeeded, you get the option to create a passkey for future logins:
Afterwards you get redirected to the profile page, where some user info is displayed:
This tutorial showed how easy it is to add passwordless authentication with passkeys to a Keycloak-based app using Corbado. With the webhooks template, the integration of existing password based users is as comfortable as it gets. Besides the passkey-first authentication, Corbado provides easy integration for a wide variety of frameworks and languages. If you want to read more about hooking up your existing users to the Corbado system, read our documentation here, or if you want add Corbado to your new project with no existing users, please see our documentation here.
Table of Contents
Enjoyed this read?
🤝 Join our Passkeys Community
Share passkeys implementation tips and get support to free the world from passwords.
🚀 Subscribe to Substack
Get the latest news, strategies, and insights about passkeys sent straight to your inbox.
We provide UI components, SDKs and guides to help you add passkeys to your app in <1 hour
Start for free