Example Repository
Authsignal Passkeys with NextAuth in a Next.js Pages Router app.
Create a new Next.js application
Setup NextAuth with Email Magic Link Sign In
Users will need to create an account and sign in before they can create a passkey. To set up NextAuth with email magic link sign in, follow the NextAuth Email Provider Docs.Install Authsignal SDKs
Install the Authsignal web and node SDKs:We recommend disabling 1Password browser extensions during development as they can intercept error
messages from Authsignal’s SDKs.
Add Authsignal secret key, tenant ID, and region API host
Get the secret key, tenant ID, and region API host to your from Authsignal Portal and add them to your.env.local
file.
.env.local
Creating a passkey
1
Backend - Track an action
Create a protected endpoint that tracks an action using Authsignal’s Node Server SDK. In our example, we’ve created
an endpoint called
enroll-passkey
that checks the session token before tracking an action. Pass the token returned by the track
call to your frontend.pages/api/auth/enroll-passkey.ts
2
Frontend - Initiate the passkey enrollment flow
In your app’s frontend, call the
signUp
function using Authsignal’s Web SDK, passing the token returned in step 1.pages/index.tsx
3
Backend - Validate the result
Create a protected endpoint that validates the result token from step 2.Pass the result token returned from Authsignal’s passkey.signUp in step 2 to your backend, validating the result of the enrollment server-side. In our example, we’ve
created an API route called
callback
that checks the NextAuth session token, then validates the result token passed in from step 2.pages/api/auth/callback.ts
4
Frontend - Handle result
Notify the user of the success or failure of the passkey addition.The full frontend enrollment logic is shown below:
pages/index.tsx
pages/index.tsx
You can remove passkeys along with other authenticators in the Authsignal
Portal. You will also need to remove it from your device,
e.g. in chrome://settings/passkeys.
Signing in with a passkey
Now that the user has enrolled a passkey, the next time they sign in we would like to give them the option to use their passkey instead of email magic link.1
Frontend - Enable passkey autofill
First, we need to ensure that the input field has the value Then we call 
We pass this token to NextAuth’s The full sign in logic code is shown below:
username webauthn
in the autocomplete attribute.The
webauthn
value in the autocomplete
attribute is required for autofill to work.webauthn
can be combined with other typical autocomplete
values, including username
and current-password
, but must appear at the end to consistently trigger conditional UI across browsers.pages/signin.tsx
authsignal.passkey.signIn
when the page loads. This will initialize the input field for passkey autofill, which means if a user has enrolled a passkey then they should be able to select it when focusing the text field.
On success, this will return a token that we will validate on the backend.pages/signin.tsx

signIn
method, specifying that we want to use the credentials
provider. This uses the token to validate the result of the challenge server-side and logs in the user.
We’ve set redirect: false
so that we can handle errors manually, on the current page.pages/signin.tsx
pages/signin.tsx
2
Backend - Create NextAuth session
We need to add a CredentialsProvider to the providers array in You’ll notice that we’ve also added a JWT strategy to the session option. We need to enable JWT session tokens for NextAuth’s CredentialsProvider to work,
according to NextAuth’s documentation.
authOptions
within the [...nextauth]
API route.
This will validate the signInToken from step 1 using Authsignal’s validateChallenge
method.
On successful validation, NextAuth will create a session containing user information that you specify. Returning null
will throw an error.pages/api/auth/[...nextauth].ts