Passkey login is a cool new way to sign in to websites and apps without needing any passwords. It uses your device's unlock mechanism like Touch ID or Face ID to authenticate, which makes it way more secure and easier to use than old-school passwords and even those two-factor authentication methods we're used to.
â
â
Typically, to add passkeys to any app, you'd need two things:
a backend to handle the authentication flow and store data about your user's passkeys
a couple of functions on your frontend to bring up & handle the "Sign in with passkey" dialog
Our open-source passkey server is an API you can call from your (Next.js) backend to handle the authentication flow (and storing all relevant data about your user's passkeys).
@teamhanko/passkeys-next-auth-provider is a NextAuth provider that calls this API for you, and lets you bring up the "Sign in with passkey" with a single function call.
@github/webauthn-json is an optional package that makes it easier to work with the WebAuthn API on your frontend.
Not using NextAuth? No worries! We've got a guide that shows you how to implement passkeys to your Next.js app using our Passkey SDK. It works with whatever authentication system you're already using.
Let's get to building. You can either follow the video or go through the guide below.
Install dependencies
After initialising your Next.js app with NextAuth, install the passkey provider for NextAuth from Hanko and webauthn-json package by GitHub.
As weâre using Prisma Adapter by NextAuth, weâll need to also to use session: { strategy: "jwt" } and modify the session to get id from token.sub. This is how the complete code for auth.ts the file looks after adding Hanko passkey provider.
Note that, if you donât plan to use any adapters, you just need to add the passkey provider and donât modify anything.
Your users will have to add passkeys to their account somehow. Itâs up to you how and where you let them do this, but typically this would be a button on an âAccount Settingsâ page.
On your backend, youâll have to call tenant({ ... }).registration.initialize() and .registration.finalize() to create and store a passkey for your user.
On your frontend, youâll have to call create() from @github/webauthn-json with the object .registration.initialize() returned.
create() will return a PublicKeyCredential object, which youâll have to pass to .registration.finalize().
Here we have created a new file named passkey.ts, inside of the server directory.
Alright, now letâs get to creating a âCreate passkeyâ button. This is what kicks off the whole Passkey creation process for that user account.
"use client"
import { finishServerPasskeyRegistration, startServerPasskeyRegistration } from '@/lib/passkey';
import { Button } from './ui/button';
import Passkey from './icons/passkey';
import {
create,
type CredentialCreationOptionsJSON,
} from "@github/webauthn-json";
const RegisterNewPasskey = () => {
async function registerPasskey() {
const createOptions = await startServerPasskeyRegistration();
const credential = await create(createOptions as CredentialCreationOptionsJSON);
await finishServerPasskeyRegistration(credential);
}
return (
<Button
onClick={() => registerPasskey()}
className="flex justify-center items-center space-x-2"
>
<Passkey className="w-4 h-4 mr-2" />
Register a new passkey
</Button>
)
}
export default RegisterNewPasskey
Get credentials from Hanko Cloud
Now, to make it work we're missing one crucial step: getting the Tenant ID and API key secret from Hanko Cloud. For that, navigate over to Hanko, create an account, and set up your organization. Navigate to 'Create new project', select 'Passkey Infrastructure', and provide your project name and URL.
Note: It's recommended to create separate projects for your development and production environments. This way, you can use your frontend localhost URL for the development project and your production URL for the live project, ensuring a smooth transition between environments without the need to modify the 'App URL' later.
Obtain your Tenant ID and create an API key, then add the Tenant ID and API key secret to your backend's '.env' file.
Now that you have your secrets added to your backend's .env file, you should be all set to register a passkey. Go ahead and give it a try!
Add a button to login with a passkey
Now that the passkey is successfully registered, letâs add a âSign in with a passkeyâ button. This will allow users to easily login using their passkey.
"use client"
import Passkey from "./icons/passkey";
import { Button } from "./ui/button";
import { signInWithPasskey } from "@teamhanko/passkeys-next-auth-provider/client";
const SignInWithPasskey = () => {
return (
<Button
onClick={() => signInWithPasskey({
tenantId: process.env.NEXT_PUBLIC_PASSKEYS_TENANT_ID!,
callbackUrl: `${window.location.origin}/dashboard/settings`
})
}
className="mt-4" variant="secondary"
>
<Passkey className="w-4 h-4 mr-2" />
Sign in with a passkey
</Button>
)
}
export default SignInWithPasskey
And thatâs it! You now have a working passkey login, making the authentication process much easier for your users đ
If you want to take a closer look or try it out for yourself, feel free to check out the GitHub starter repo.
If you run into any issues, don't hesitate to shoot us a message on Discord! We'll help you get things up and running.