Using NextAuth.js with Magic  links
Tags: nextjs, magic, react, webdev
Published: 2021-07-09 23:03:03

![NextAuth.js + Magic](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d269d6x1oyxipjg1n1vz.png)

This post is about how to use [Magic](https://magic.link) link authentication with [NextAuth.js](https://next-auth.js.org) to enable passwordless authentication in a Next.js app.

>While NextAuth.js supports passwordless auth via the `Email` provider, [Magic](https://magic.link) provides blockchain based security and minimal setup that makes it easy to get up and running (`NextAuth.js` requires configuring your database for magic email links).

### Setup
Install the necessary `NextAuth` and `Magic` packages
```bash
yarn add next-auth magic-sdk @magic-sdk/admin
```

### Step 1

To start, we need to create a custom NextAuth `Credentials` provider that will validate the Magic login and return the `User` object.

```typescript
// src/pages/api/auth/[...nextauth].ts

import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import { Magic } from '@magic-sdk/admin';

const magic = new Magic(process.env.MAGIC_SK);

export default NextAuth({
  session: {
    jwt: true,
  },
  pages: {
    // override signIn page so we can integrate with Magic
    signIn: '/auth/signin',
  },
  providers: [
    Providers.Credentials({
      name: 'Magic Link',
      credentials: {
        didToken: { label: 'DID Token', type: 'text' },
      },
      async authorize({ didToken }, req) {
        // validate magic DID token
        magic.token.validate(didToken);

        // fetch user metadata
        const metadata = await magic.users.getMetadataByToken(didToken);

        // return user info
        return { ...metadata };
      },
    }),
  ],
});
```

### Step 2
Next, we need to create a custom `signIn` page. This page will contain a `<form>` that captures the user's email and in the `onSubmit` function will login using the Magic SDK.

> Notice in Step 1 how we specified a custom `signIn` page (`/auth/signin`). This allows us to build a custom login flow with Magic and then `signIn` via `NextAuth`. You can extend this custom `signIn` page to support social login providers (using Magic or NextAuth).

```typescript
// src/pages/auth/signin.tsx

import { useRouter } from 'next/router';
import { signIn } from 'next-auth/client';
import { Magic } from 'magic-sdk';
import { useForm } from 'react-hook-form'; // use your form lib of choice here

// magic-sdk is only availabile in the browser
const magic = typeof window !== 'undefined' && new Magic(process.env.NEXT_PUBLIC_MAGIC_PK || 'a');

export default function SignIn() {
  const router = useRouter();
  const { register, handleSubmit } = useForm();

  const onSubmit = async ({ email }) => {
    if (!magic) throw new Error(`magic not defined`);

    // login with Magic
    const didToken = await magic.auth.loginWithMagicLink({ email });

    // sign in with NextAuth
    await signIn('credentials', {
      didToken,
      callbackUrl: router.query['callbackUrl'] as string,
    });
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email', { required: true })} placeholder="nick@example.com" />

      <button type="submit">Sign in</button>
    </form>
  );
}
```


### Step 3
Make sure to populate your `.env` file with your public/secret keys found on the Magic dashboard.

```typescript
// .env

NEXT_PUBLIC_MAGIC_PK=pk_live_XXXXXXX
MAGIC_SK=sk_live_XXXXXXX
```

![Magic Dashboard Keys](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpt7c5bd4pbpzoble777.jpg)

### Finishing up
You should now be able to login via `Magic` and have `NextAuth.js` handle the rest!

Sign up for Magic [using this link](https://magic.link/invite/r/eEaJK33Fp5mKO372) to get an additional 3,000 free logins!