If you're looking to add Passkey Login to your existing Next.js app, you're in the right place! Passkey login is a secure and user-friendly authentication method that eliminates the need for traditional passwords. By implementing passkeys, you can provide your users with a seamless and secure login experience.
In this tutorial, we'll walk you through the step-by-step process of adding a passkey login into your Next.js app using the Hanko Passkey API. We'll assume that you already have a Next.js app with authentication set up, so we won't focus on that here. Instead, we'll dive straight into adding Passkeys to your existing auth system.
If you're already using NextAuth for authentication in your Next.js app, we've got you covered! Check out our guide on integrating passkey login using our NextAuth Passkey Provider.
So, let's get started on upgrading your app's login experience! To make it easier for you to follow along, we have created a sample app that you can use as a reference throughout the tutorial.
We'll be implementing passkey functionality in two phases.
Make sure you have the passkey SDK installed.
To enable passkey registration, we define two server actions: startServerPasskeyRegistration and finishServerPasskeyRegistration.
The startServerPasskeyRegistration function retrieves the user session, extracts the user ID and email, and initializes the passkey registration process using the passkeyApi.registration.initialize method from the SDK. It returns the registration options to the client.
The finishServerPasskeyRegistration function finalizes the passkey registration by calling passkeyApi.registration.finalize with the provided credentials.
Now, it's time to implement passkey registration on the frontend. For that, we define an registerPasskey function that will be triggered when the user clicks a button to register a new passkey.
Let's break down the steps involved in the registerPasskey function:
1. We call the startServerPasskeyRegistration server action to initialize the passkey registration process. This function returns the registration options needed to create a new passkey credential.
2. We use the create function from the @github/webauthn-json library to create a new passkey credential based on the registration options received from the server. The create function prompts the user to authenticate using their device's passkey mechanism (e.g., Touch ID, Face ID, or Windows Hello).
3. Once the user successfully authenticates and creates the passkey credential, we call the finishServerPasskeyRegistration server action, passing the newly created credential as an argument. This function finalizes the passkey registration process on the server-side.
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!
We'll need to create two more server actions. The startServerPasskeyLogin function initializes the passkey login process by calling passkeyApi.login.initialize() and returns the login options to the client.
The finishServerPasskeyLogin function finalizes the passkey login by calling passkeyApi.login.finalize() with the provided options and returns the login response.
Now, similar to passkey registration, inside the LoginPasskey component, we define a signInWithPasskey function, that will be triggered when the user clicks the "Sign in with a passkey" button.
Let's break down the steps involved in the signInWithPasskey function:
1. We call the startServerPasskeyLogin server action to initialize the passkey login process. This function returns the assertion options needed to retrieve the passkey credential.
2. We use the get function from the @github/webauthn-json library to retrieve the passkey credential based on the assertion options received from the server. The get function prompts the user to authenticate using their device's passkey mechanism.
3. Once the user successfully authenticates, we call the finishServerPasskeyLogin server action, passing the retrieved credential as an argument. This function finalizes the passkey login process on the server-side and returns the login response.
4. We check if the login response contains a valid token. If not, we return null to indicate a failed login attempt.
5. If the login response contains a valid token, we extract the token and use the getUserID function from @/lib/auth to retrieve the user ID associated with the token.
6. If the user ID is not found, we return null to indicate a failed login attempt.
7. If the user ID is successfully retrieved, we call the loginWithPasskey function, passing the user ID as an argument. This function checks if user with the provided id exists, then creates the session for the user.
Thats it! You now have a fully functional passkey login in your Next.js app, significantly enhancing the user experience and making the authentication process more seamless for your users 🚀 Feel free to reach out to us on Discord, if you get into any issues.
Github Repo: https://github.com/teamhanko/passkeys-nextjs