> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hanko.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js with NextAuth (now known as Auth.js)

> Learn how to add passkeys to your Next.js app which already uses Auth.js for authentication.

<Steps>
  <Step title="Install passkey provider">
    Once you’ve initialized your Next app with [NextAuth](https://authjs.dev/reference/nextjs), install passkey provider and the [webauthn-json](https://github.com/github/webauthn-json) package.

    <CodeGroup>
      ```bash npm theme={null}
      npm i @teamhanko/passkeys-next-auth-provider @github/webauthn-json
      ```

      ```bash pnpm theme={null}
      pnpm add @teamhanko/passkeys-next-auth-provider @github/webauthn-json
      ```

      ```bash bun theme={null}
      bun add @teamhanko/passkeys-next-auth-provider @github/webauthn-json
      ```

      ```bash yarn theme={null}
      yarn add @teamhanko/passkeys-next-auth-provider @github/webauthn-json
      ```
    </CodeGroup>
  </Step>

  <Step title="Get your tenant ID and API key">
    Get your tenant ID and API key from [Hanko Cloud](https://cloud.hanko.io/organizations) and add them to your `.env.local` file.

    ```sh .env.local theme={null}
    PASSKEYS_API_KEY=your-api-key
    NEXT_PUBLIC_PASSKEYS_TENANT_ID=your-tenant-id
    ```

    <Note>
      If you're self-hosting:

      1. make sure to pass the `baseUrl` to both
         `tenant` (in `[...nextauth].ts`) and
         `signInWithPasskey()` (in your component).
      2. get your tenant ID via the admin API {/* TODO link to admin API */}
    </Note>
  </Step>

  <Step title="Add `PasskeyProvider`">
    ```ts app/api/auth/[...nextauth]/route.ts theme={null}
    import {
      tenant,
      PasskeyProvider,
    } from "@teamhanko/passkeys-next-auth-provider";

    export default NextAuth({
      providers: [
        PasskeyProvider({
          tenant: tenant({
            apiKey: process.env.PASSKEYS_API_KEY,
            tenantId: process.env.NEXT_PUBLIC_PASSKEYS_TENANT_ID,
          }),
          async authorize({ userId }) {
            const user = db.users.find(userId);

            // Do more stuff

            return {
              id: user.id,
              name: user.username,
            };
          },
        }),
      ],
    });
    ```
  </Step>

  <Step title="Allow your users to register passkeys as a login method for their account">
    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()`.

    **Backend:**

    ```ts lib/passkey-registration.ts theme={null}
    "use server";

    // This is *your* server-side code; you need to implement this yourself.
    // NextAuth takes care of logging in the user after they have registered their passkey.

    import { authOptions } from "./app/api/auth/[...nextauth]/route.ts"; // Only required because of a NextAuth limitation
    import { getServerSession } from "next-auth";
    import { tenant } from "@teamhanko/passkeys-next-auth-provider";

    const passkeyApi = tenant({
      apiKey: process.env.PASSKEYS_API_KEY,
      tenantId: process.env.NEXT_PUBLIC_PASSKEYS_TENANT_ID,
    });

    export async function startServerPasskeyRegistration() {
      const session = await getServerSession(authOptions);
      if (!session?.user?.id) throw new Error("Not logged in");

      const createOptions = await passkeyApi.registration.initialize({
        userId: session.user.id,
        username: session.user.name,
      });

      return createOptions;
    }

    export async function finishServerPasskeyRegistration(credential: any) {
      const session = await getServerSession(authOptions);
      if (!session) throw new Error("Not logged in");

      await passkeyApi.registration.finalize(credential);

      // Now the user has registered their passkey and can use it to log in.
      // You don't have to do anything else here.
    }
    ```

    **Frontend:**

    ```jsx components/PasskeyRegistrationButton.tsx theme={null}
    // This is your client-side code. NextAuth takes care of logging in the user after they have registered their passkey using registerPasskey function.

    "use client"

    import {
      finishServerPasskeyRegistration,
      startServerPasskeyRegistration,
    } from "@/lib/passkey-registration";
    import { create } from "@github/webauthn-json";

    export default function RegisterNewPasskey() {
      async function registerPasskey() {
        const createOptions = await startServerPasskeyRegistration();

        // Open "register passkey" dialog
        const credential = await create(createOptions as any);

        await finishServerPasskeyRegistration(credential);

        // Now the user has registered their passkey and can use it to log in.
      }

      return (
        <button onClick={() => registerPasskey()}>Register a new passkey</button>
      );
    }
    ```
  </Step>

  <Step title="Allow your users to log in with passkeys">
    Let's add a button that triggers the "Sign in with passkey" dialog:

    ```jsx components/PasskeyLoginButton.tsx theme={null}
    "use client";

    import { signInWithPasskey } from "@teamhanko/passkeys-next-auth-provider/client";

    export default function LoginButton() {
    	return (
    		<button onClick={() => signInWithPasskey({ tenantId: process.env.NEXT_PUBLIC_PASSKEYS_TENANT_ID })}>
    			Sign in with passkey
    		</button>
    	);
    }
    ```
  </Step>

  <Step title="Optional: Autofill support">
    If you don't want to add a button just for passkeys, you can add autofill support to your username (or email) fields:

    <Frame>
      <img src="https://mintcdn.com/hanko/djMPan_QCdoQzxwW/images/passkey-api/autofill.png?fit=max&auto=format&n=djMPan_QCdoQzxwW&q=85&s=995be18e2f28875dec33fa343ac518c4" style={{ borderRadius: "0.5rem" }} width="447" height="191" data-path="images/passkey-api/autofill.png" />
    </Frame>

    Clicking on any passkey in the autofill popup will immediately log the user in, going through the same flow as if they had clicked the "Sign in with passkey" button earlier in this guide.

    To add autofill support:

    * add `autoComplete="username webauthn"` to your username field
    * call `signInWithPasskey.conditional()` when the login form loads

    **Example:**

    ```jsx theme={null}
    "use client";

    import { signInWithPasskey } from "@teamhanko/passkeys-next-auth-provider/client";

    export default function LoginForm() {
      // Call signInWithPasskey.conditional() once, when LoginForm mounts.
      //
      // .conditional() returns a cleanup function.
      // Please make sure to return it from useEffect:
      useEffect(() => {
        return signInWithPasskey.conditional({
          tenantId: process.env.NEXT_PUBLIC_PASSKEYS_TENANT_ID,
        });
      }, []);

      return (
        <form>
          {/* Add "webauthn" to input autoComplete: */}
          <input autoComplete="username webauthn" />
          <input type="password" placeholder="Password" />
          <button type="submit">Log in</button>
        </form>
      );
    }
    ```
  </Step>

  <Step title="Try it yourself">
    <Card
      title="Next.js with Auth.js example"
      href="https://github.com/teamhanko/passkeys-example-next-auth"
      icon={
<svg
  xmlns="http://www.w3.org/2000/svg"
  className="icon icon-tabler icon-tabler-external-link"
  width="24"
  height="24"
  viewBox="0 0 24 24"
  strokeWidth="2"
  stroke="currentColor"
  fill="none"
  strokeLinecap="round"
  strokeLinejoin="round"
>
  <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
  <path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6"></path>
  <path d="M11 13l9 -9"></path>
  <path d="M15 4h5v5"></path>
</svg>
}
    >
      Full source code available on our GitHub
    </Card>
  </Step>
</Steps>
