Building the Frontend

Build the frontend for your app.

Frontend Setup

We will use Next.js to build the frontend.

Move out of your contract folder and back into the zksync-webauthn folder. Make a new Next.js project for the frontend using the configuration below:

npx create-next-app@latest
✔ What is your project named? … frontend
✔ Would you like to use TypeScript? Yes
✔ Would you like to use ESLint? Yes
✔ Would you like to use Tailwind CSS? No
✔ Would you like to use `src/` directory? Yes
✔ Would you like to use App Router? (recommended) No
✔ Would you like to customize the default import alias (@/*)? No

Move into your projects and install the dependencies below:

cd frontend
npm install @simplewebauthn/browser@10.0.0 @simplewebauthn/server@10.0.1 cbor@9.0.2 ethers@5.7.2 zksync-ethers@5.1.0

Environment Variables

Make a new file called .env.local, copy the variables below, and add the deployed contract addresses from the previous section.

NEXT_PUBLIC_AA_FACTORY_ADDRESS=
NEXT_PUBLIC_NFT_CONTRACT_ADDRESS=
NEXT_PUBLIC_PAYMASTER_ADDRESS=

Home Page

We can start by replacing the home page with one that has four links for each page we will make.

Replace your frontend/src/pages/index.tsx file with the code below.

Layout

Next, let's create the Layout component used in the home page.

Create a src/components folder and a new file inside called Layout.tsx.

mkdir src/components
touch src/components/Layout.tsx

Copy the Layout component below to your file:

Styles

Let's also update the styles in src/styles/globals.css.

API

Let's setup our API endpoints next. We need to do this because some parts of the WebAuthn ceremonies must run on a server instead of the client.

Get Registration Options

Create a new file in the src/pages/api folder called get-registration-options.ts.

touch src/pages/api/get-registration-options.ts

The get-registration-options endpoint is used to generate the options input object for the WebAuthn registration ceremony.

These options tell the browser how to configure the registration, including setting the name of the passkey, the domain of the application making the request, what signature types are allowed, and whether user verification is required.

It's important here that we are only allowing -7 as the only supported algorithm ID, as this represents the same signature scheme that is supported by the P256Verify precompile contract we are calling in the smart account validation.

You can learn more about the options available for registration in the simplewebauthn docs.

Get Authentication Options

Create another file in the src/pages/api folder called get-authentication-options.ts.

touch src/pages/api/get-authentication-options.ts

The get-authentication-options endpoint is used to generate the options input object for the WebAuthn authentication ceremony. You can learn more about the options available for authentication in the simplewebauthn docs.

The challenge passed in through the request body represents the transaction data that would normally be signed by a wallet like Metamask. The generateAuthenticationOptions method converts the challenge to a base64 URL for the options input object.

If you want to verify that the final base64URL challenge in the options is indeed equal to the input challenge, you can use the isoBase64URL method provided by @simplewebauthn.

import { isoBase64URL } from '@simplewebauthn/server/helpers';
const decodedChallenge = isoBase64URL.toUTF8String(options.challenge);

Utils

Next, let's make a new folder called utils where we can add some utility functions.

mkdir utils

String Conversions

Create a new file inside the utils folder called string.ts to help handle some string conversions we will need to work with hex values and base64 URLs.

touch utils/string.ts

Transaction Utils

Next, create a file called tx.ts.

touch utils/tx.ts
Note: you may need to update the import at the top from zksync-ethers/build/utils to zksync-ethers/build/src/utils.

This file contains helper functions to get data for a transaction and register a new passkey in a smart account.

  • The getTransaction function constructs a transaction that is sponsored by the paymaster contract using the getPaymasterOverrides function.
  • The registerKeyInAccount function sends a transaction to register a new passkey as the r1Owner of a smart account. The updateR1Owner function must be called by the smart contract account itself. Because there is no passkey registered yet, the original wallet that created the account must sign the transaction, which we can pass as a custom signature. This means that wallet passed into this function must be the same wallet created in the "Create Account" page.

WebAuthn Utils

The last file we need to add in the utils folder is called webauthn.ts.

touch utils/webauthn.ts

This file contains helper functions to start the WebAuthn authentication process and send transactions with a WebAuthn signature.

  • The authenticate function gets the authentication options from the endpoint we set up earlier, and passes them into the startAuthentication method provided by @simplewebauthn.
  • The signAndSend function takes the response from the authentication process and processes it so it's in the correct format for the smart account contract. Remember that the WebAuthn authentication process returns three pieces of information that we need for validation: the WebAuthn signature, the authenticatorData, and the clientData. The getSignatureFromAuthResponse function encodes the rs values from the signature along with the authenticatorData and the clientData into one signature that can be passed into the smart contract via the transaction customData.customSignature.
  • The getDataToSign function generates a signed digest of a transaction.
  • The getRS function extracts the rs values from the WebAuthn signature and normalizes the values to ensure they are in the lower half of the secp256r1 curve. This is required to pass the malleability check in the smart account contract.
  • The getPublicKeyFromAuthenticatorData function extracts the public key from the authenticatorData returned from the registration process.

Let's complete the frontend in the next section.


Made with ❤️ by the ZKsync Community