
Add passkey authentication to your existing Node.js app
Passkeys are the new standard login method for web authentication but so far there are very few developer tutorials that show how to add them on a code level. In the following, we will change the password-based login of our Node.js app to a passwordless, passkey-first authentication with Corbado's web component. The web component is an easy to integrate, lightweight authentication layer with great out-of-the-box passkey UX.
GitHub repository link:
Let's get the whole picture first! We use a dockerized setup with two containers:
- The first container runs our Node.js application
- The second container hosts the database with our existing users.

Our folder structure looks like this:
Start the system with docker compose up.
Our frontend runs on http://localhost:19915 with the login page looking like this:

To integrate the Corbado web component, we follow these steps:
1. Create project in the Corbado developer panel
2. Integrate web component in the frontend
3. Make use of Corbado session management
4. (Optional) Enable password-based login as fallback
5. (Optional) Make your local application reachable for Corbado via Corbado CLI
We start by heading over to the Corbado developer panel and creating an account. We are welcomed by the following screen. We select 'Web app' and press „Next“.

We want to integrate our existing users, so we select "Yes".

Afterwards, we find ourselves at the overview of the developer panel (you need to confirm your account via email if it's your first time).
Head over to the 'Integration guide'. This guides us through all the steps necessary for the integration to work.

In step 1, we have the option to add an authorized origin and create an API secret. We don’t need to manually authorize any origin, so we skip this point and continue with the creation of an API secret. We need the project ID and the generated API secret later in order to communicate with Corbado's API).

In the second, optional step, we configure the webhook. This is needed, so Corbado can communicate with our backend e.g. for checking if a username and password of an existing user match. More details on that later.

We will later set up our webhook at http://localhost:19915/corbado-webhook with ‘p36qLbyRvHoZDT’ and ‘z9aSidcu2xLyoU’as credentials, so we can already enter that here.

In step 3, we add our Application URL, Redirect URL and Relying Party ID. The Application URL is the URL in the frontend where the web component runs. For example, it’s used to forward users to the web component again after they clicked on an email magic link (http://localhost:19915 in our case).
The Redirect URL is the URL that the user gets redirected to after successful authentication. We implement our Redirect URL at http://localhost:19915/api/redirect later, but we can enter it here already.
The Relying Party ID is the domain where we bind our passkeys to. The domain in the browser where a passkey is used must be a matching domain to the Relying Party ID. As we test locally, we set the Relying Party ID to localhost.

Just like that, the project is set up!
Now that we are done with the Corbado settings, let’s get to coding! To start off we put the project ID and the API secret from the last step into our .env file so we don’t have them plaintext in our code.
In the frontend, we integrate the Corbado web component into our login page. For this we need the following script:
Afterwards, we replace our current login form with the one from Corbado (we take the project ID from our environment variables):
Taking a glance at our login page in the browser reveals the Corbado web component in action:

Hint: If you now enter your email address and tried to sign up or login, you will get an error message. That's because we haven't configured the backend yet.
Once a user has successfully authenticated via the Corbado web component in the frontend, he gets sent to the Redirect URL and Corbado initiates a session. We configured this Redirect URL to be http://localhost:19915/api/redirect, remember? We implement this endpoint now.
We initialize the Corbado API client inside our controller. For this we need the project ID and API secret again.
We add a route for the required endpoint in our routeconfig:
Here we will need the Corbado Node.js SDK. Install it with
We are then able to request the current user from the Node.js SDKand add this user to our own database if it is does not exist there yet.
We now know who just logged into our application and redirect them to the logged-in page of our app (the profile page):

If you don't have an existing user base, that's all it takes to integrate passwordless, passkey-first authentication with Corbado!
We 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 is set up in our backend. In the steps before, we set the webhook URL to http://localhost:19915/corbado-webhook as well as the webhook username and password.
Then, 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:
Now we create a route for the webhook:
as well as a controller to make the webhook available. The Corbado Node.js SDK provides some JavaScript code that helps to add the webhooks:
If a user enters their email in the web component, the webhook we just installed gets a call. The webhook function will handle it resulting in a call of the getUserStatus 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.
Done! All our users can now use passkeys, while existing ones have their password as a fallback option. But wait, how can Corbado call endpoints if our backend is only running locally, especially during development?
Using the Corbado CLI, we create a tunnel of our local application to the outside world, so that the webhook URLcan be called. We install it by following the docs Once started, the webhook running on our machine forwards requests from Corbado to our local instance. Analog to the image above, the process is then as follows:

- The browser requests our login page
- The AuthController sends back the HTML page which contains the web component
- After the user has entered the email address, the web component sends it to Corbado.
- Corbado processes it and sends a request to our webhook to check if the user exists.
- The message gets forwarded through Corbado CLI in order to reach our local instance. (If your solution is in production, Corbado can directly call the Webhook controller without the need for the Corbado CLI tunnel because you then have a public address).
- Our webhook controller sends back the requested information.
- The message gets forwarded through Corbado CLI again
- Corbado tells the web component how to react (e.g., ask the user for a password)
To use the CLI we head to the Corbado 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 19915 as our Node.js application is running here).
Now, we can test if our webhook works by going to Settings > Webhooks > Testing in the developer panel, filling out the stub data and running the tests. If everything went right, all tests will pass.

If we now go to http://localhost:19915/login and enter the email of an already existing account, the web component should ask you for a password (if no passkey for the account has been created yet).

Afterwards, users are asked to create a passkey for future logins.
Enjoyed this read?
Stay up to date with the latest news, strategies and insights about passkeys sent straight to your inbox!