In this tutorial, we show how to integrate passkeys into a Node.js (Express) app using Corbado's Node.js SDK & UI components for passwordless authentication.
Lukas
Created: October 16, 2023
Updated: April 30, 2025
We aim to make the Internet a safer place using passkeys. That's why we want to support developers with tutorials on how to implement passkeys.
1. Introduction: Node.js (Express) Passkeys
3. Repository Structure of the Node.js (Express) Passkey Project
4. Set Up Your Node.js (Express) Project
5. Set Up Corbado Passkey Authentication
5.1 Set Up Your Corbado Account and Project
5.3 Set Up Node.js (Express) Backend
5.4 Create the Homepage to Handle Sign-ups and Logins
5.5 Create a Profile Route using the Corbado Node.js SDK
In this blog post, we’ll explore how to implement Node.js (Express) passkeys for secure authentication using Corbado. We'll cover the setup of the Corbado Node.js SDK for the backend part as well as the Corbado web-js package for the frontend part.
If you want to see the finished code, please have a look at our Node.js (Express) passkey example repository on GitHub.
The final authentication page will look as follows:
This tutorial assumes basic familiarity with Node.js, Express, HTML, CSS and JavaScript / TypeScript. Moreover, you need to have Node and NPM installed on your machine. Let's dive in!
Let's first discuss the structure of our project (full Node.js / Express passkeys GitHub repo):
├── .env # Contains all environment variables └── src ├── views | └── pages | ├── login.ejs # The homepage / login page | └── profile.ejs # The profile page ├── app.ts # The main server file ├── authController.ts # Responsible to retrieve profile information in backend └── routes.ts # The routes for our frontend pages
The rest of the files of this project can be ignored for the purpose of this tutorial.
First, open a new directory, create a package.json
and tsconfig.json
file and copy our
content from
package.json
as well as from
tsconfig.json
Next, install all dependencies:
npm i
That's it for now.
To set up Corbado passkey authentication in your Node.js (Express) project, follow the next steps.
Visit the Corbado developer panel to sign up and create your account (you'll see the passkey sign-up in action here!).
After sign-up, a project wizard will guide you through the necessary steps to get everything up and running:
Application URL
and Relying Party ID
. The Application URL
is the URL
where you embed the Corbado UI component. In this example, we
set it to http://localhost:3000
. The Relying Party ID
is the domain (no protocol,
no port and no path) where passkeys should be bound to. Here, it's localhost
(you can
define both values als in the
Settings > General > URLs of the
Corbado developer panel).Afterwards, you'll see the relevant HTML / JavaScript code snippets you need to integrate into the project. The subsequent sections of this article will explain them in detail.
As another step, we create an API secret
which will be needed to request user data from
the Corbado backend. Please create an API secret
in
Settings > Credentials > API secrets.
Afterwards, we need to also get the Frontend API
and Backend API
URLs which we can
find in
Settings > General.
Next, you need to go back to your terminal and should navigate to the root directory of the project:
cd example-passkeys-nodejs
Open the .env
file at the root of your project. Copy and paste the API secret
,
Project ID
as well as both Frontend API
and Backend API
URLs (you can obtain it from
the Corbado developer panel from
Settings > General) The
.env
file should look like this now:
PROJECT_ID=<your-project-id> API_SECRET=<your-api-secret> FRONTEND_API=<your-frontend-api-url> BACKEND_API=<your-backend-api-url>
First, install the Corbado Node.js SDK (if not already done in step 4):
npm i @corbado/node-sdk
To initialize the SDK, we pass our Project ID
, API secret
as well as both
Frontend API
and Backend API
URLs.
import { Request, Response } from "express"; import { SDK, Config } from "@corbado/node-sdk"; import { config as dotenvConfig } from "dotenv"; dotenvConfig(); const projectID = process.env.PROJECT_ID; const apiSecret = process.env.API_SECRET; const frontendAPI = process.env.FRONTEND_API; const backendAPI = process.env.BACKEND_API; const cboConfig = new Config(projectID!, apiSecret!, frontendAPI!, backendAPI!); const sdk = new SDK(cboConfig);
Now, we add two API routes in src/authController.ts
delivering HTML to the client.
The homepage / login page is static while the profile route will provide user information
to our template which is retrieved via the Corbado Node.js SDK from the
short_term_session
cookie (represented as JWT).
export function auth(req: Request, res: Response) { res.render("pages/login"); } export async function profile(req: Request, res: Response) { const shortSession = req.cookies.cbo_short_session; if (!shortSession) { res.redirect("/"); } try { const user = await sdk.sessions().validateToken(shortSession); const cboId = user.userId; const identifiers = await sdk.identifiers().listByUserIdAndType(cboId, "email"); const email = identifiers.identifiers[0].value; res.render("pages/profile", { cboId, email }); } catch (e) { console.log(e); res.redirect("/"); } }
Next, we define our routes in the src/routes.ts
file.
import express from "express"; import { auth, profile } from "./authController"; const router = express.Router(); router.get("/", auth); router.get("/profile", profile); export default router;
Lastly, we'll put everything together in our app.ts file.
We initialize our Node.js (Express) app, use cookieParser
to get access to cookie
headers, set our template rendering engine to ejs
and define our application's port.
import express from "express"; import { config as dotenvConfig } from "dotenv"; import cookieParser from "cookie-parser"; import authRoutes from "./routes"; import { sequelize } from "../models"; dotenvConfig(); const app = express(); app.use(cookieParser()); app.set("views", "./src/views"); app.set("view engine", "ejs"); const port = 3000;
Now, our backend is finished.
5.4 Create the Homepage to Handle Sign-ups and Logins
We'll create two templates, the first of which will be our homepage / login page under
src/views/pages/login.ejs
.
We first load the web-js CSS and JavaScript in the head of the document.
Then, we create a div with the id of corbado-auth
that we'll use to inject the
authentication UI into.
First, load your Corbado project with the setShortSessionCookie: true
option so a cookie
is created on successful login or sign-up.
Then, mount the authentication UI and pass it a callback which will be executed once the user has logged in.
login.ejs
:
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://unpkg.com/@corbado/web-js@2.9.0/dist/bundle/index.css" /> <script src="https://unpkg.com/@corbado/web-js@2.9.0/dist/bundle/index.js"></script> </head> <body> <div id="corbado-auth"></div> <script type="module"> await Corbado.load({ projectId: "<%= process.env.PROJECT_ID %>", setShortSessionCookie: true, }); async function onLoggedIn() { window.location.href = "/profile"; } const authElement = document.getElementById("corbado-auth"); //Element where you want to render CorbadoAuth UI Corbado.mountAuthUI(authElement, { onLoggedIn: onLoggedIn, }); </script> </body> </html>
5.5 Create a Profile Route using the Corbado Node.js SDK
Next, let's create the profile page under src/views/pages/profile.ejs
.
We render the email
and cboId
context in the template and attach an event listener to
the logout button, which in turn uses the Corbado.logout
utility.
profile.ejs
:
<!DOCTYPE html> <html> <head> <head> <link rel="stylesheet" href="https://unpkg.com/@corbado/web-js@2.9.0/dist/bundle/index.css" /> <script src="https://unpkg.com/@corbado/web-js@2.9.0/dist/bundle/index.js"></script> </head> </head> <body> <div> <h1>User profile:</h1> <p>Email: <%= email %></p> <p>Name: <%= cboId %></p> <button id="logoutButton">Logout</button> </div> <script type="module"> await Corbado.load({ projectId: "<%= process.env.PROJECT_ID %>", setShortSessionCookie: true, }); document .getElementById("logoutButton") .addEventListener("click", async () => { await Corbado.logout(); window.location.href = "/"; }); </script> </body> </html>
Now, we have our whole application all set up.
Use the following command to run the application:
npm run start
You'll be able to access the application under http://localhost:3000
and see our
authentication UI.
Create an account and take a look at the profile page under /profile
, where you'll be
redirected to.
This tutorial showed how easy it is to add passwordless authentication with passkeys to a Node.js app using Corbado. Besides the passkey-first authentication, Corbado provides simple session management, that we used for a retrieval of basic user data. If you want to read more about how Corbado's session management works, please check the docs here.
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.
Related Articles
Symfony Passkeys: How to Implement Passkeys in PHP Symfony
Lukas - April 17, 2023
Vercel Passkeys: Deploying Passkeys in Next.js App on Vercel
Nicolai - September 19, 2023
Table of Contents