A web application that demonstrates a NextJS App Router project implementing user authentication with Passkeys using SimpleWebAuthn.
-
SimpleWebAuthn allows for TypeScript WebAuthN integration.
-
Drizzle allows for TypeScript type-safe data modelling and database querying connection. For usage with Prisma please refer to the
with-prisma
branch -
renchris' fork of the Iron Session V8 branch allows Iron Session to be used with React Server Components and NextJS Server Actions.
This project follows closely to Ian Mitchell's NextJS WebAuthN demo with the implementation being with NextJS App Router and Server Action Iron Session.
To take this this demo to a secure production application, you will additionally need to create a multi-step registration flow.
Per his note from his demo and blog:
If you're following this guide to add WebAuthn to an application you intend to ship, it's important to create a multi-step registration flow instead of asking for a username and email upfront. When you call create, the browser will create the login - your application can't remove it later. If your server validation fails (let's say because the username was registered by someone else) the browser will still have created an account.
Instead of having a registration all on one page, create a new model called Account, move username and email to that, and create a one-to-one relationship with the User model. Then, after a user clicks register and creates a User, prompt them for an email and username on a second page to create a new Account model. Any validation failures on this step won't impact the newly created User model and its Credential!
-
Install project dependencies.
Run
pnpm install
-
Set up the Iron Session instance:
Create an
.env
file that contains the values to your secret cookie password needed for encrypted cookies with Iron Session.SECRET_COOKIE_PASSWORD=passwordpasswordpasswordpassword
-
Set up the Drizzle database :
We are using SQLite for our local file database. For an alternative database, you may set up your
drizzle/db.ts
anddrizzle.config.ts
differently for the appropriate database and driver.Initialize the new SQLite database
Run
pnpm push
You have now generated the database file
sqlite.db
can now access a UI view of the tablesRun
pnpm drizzle-kit studio
-
Run the web application.
Run
pnpm dev
-
Register a User.
Click
Register
to go to the Register page. Enter in a username and email for your passkey credential. Click Register.A prompt from the browser will come up that says
Create a passkey for localhost
with your email as the passkey identifier. Click Continue.A prompt from the device system will come up that says
"Your Browser" is trying to verify your identity on localhost. Touch ID or enter your password to allow this.
Enter in your Touch ID.Your passkey will now have been registered into the database.
-
Login the User.
Click
Login
to go to the Login page. Enter the email you used during the Register step. Click Login.A prompt from the device system will come up that says
"Your Browser" is trying to verify your identity on localhost. Touch ID or enter your password to allow this.
Enter in your Touch ID.You will now be re-directed to the Admin Page. The authenticated content will show your User ID number.
-
Logout the User.
From the Admin Page, click the
Logout
button. The cookies of your user session will be cleared and you will be re-directed to the Home Page.
A quick look at the top-level files and directories where we made our feature changes in the project.
lib
├── auth.ts
├── cookieActions.ts
├── database.ts
├── login.ts
├── register.ts
└── session.ts
drizzle
├── db.ts
└── schema.ts
src
└── app
└── components
├── LoginPage.tsx
├── LogoutButton.tsx
└── RegisterPage.tsx
-
/lib
: This directory will contain all of theuse server
internal functions that our components and functions will use. -
lib/auth.ts
: This file contains the functions that create or modify the data or datatype of our variables so that they can be correctly passed into our function parameters and Prisma database. -
lib/cookieActions.ts
: This file contains the functions that read and write encrypted cookies to and from cookie storage. -
lib/database.ts
: This file contains the functions that read and write data to and from our Prisma database. -
lib/login.ts
: This file contains the functions that are involved with the user log in process. -
lib/register.ts
: This file contains the functions that are involved with the user registration process. -
lib/session.ts
: This file sets up the Iron Session object used by the session functions in thelib/cookieAction.ts
file. -
/drizzle
: This directory will contain the drizzle files to set up and define our Drizzle database instance and tables. -
drizzle/db.ts
: This file sets up the Drizzle database instance that is used by Drizzle functions in the/lib/database.ts
and/lib/login.ts
files. -
/drizzle/schema.ts
: This is the configuration file that sets up Drizzle table data model definitions. -
/src/app
: This directory will contain all of the code related to what you will see on the front-end of the site.src
is a convention for “source code” andapp
is the convention for “app router”. -
src/app/components/LoginPage.tsx
: Thisuse client
file contains the page component and client-side functions for the Login Page. -
src/app/components/LogoutButton.tsx
: Thisuse client
file contains the logout button component that clears the cookies and redirects the user to the Home Page when clicked. -
src/app/components/RegisterPage.tsx
: Thisuse client
file contains the page component and client-side functions for the Register Page.
Thank you to Matthew Miller for the creation and maintenance of the SimpleWebAuthn library and Ian Mitchell for his NextJS and WebAuthN blog that made creating this project possible.