This post is about how to use Magic link authentication with NextAuth.js to enable passwordless authentication in a Next.js app.
While NextAuth.js supports passwordless auth via the
NextAuth.js
requires configuring your database for magic email links).
Setup
Install the necessary NextAuth
and Magic
packages
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.
// 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 thensignIn
viaNextAuth
. You can extend this customsignIn
page to support social login providers (using Magic or NextAuth).
// 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.
// .env
NEXT_PUBLIC_MAGIC_PK=pk_live_XXXXXXX
MAGIC_SK=sk_live_XXXXXXX
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 to get an additional 3,000 free logins!
Top comments (6)
Will this be able to handle reauthentication with Magic? Since like the session is independent of Magic, but is managed by next-auth.
Yes, you can add custom logic to the Provider's
authorize
function or implement one of next-auth's callbacks depending on what you're trying to achieve.Might also be worth reading this guide from Magic's docs.
Hey Nick. I signed up for Magic.link with the link provided but only got 1k MAUs instead of 3k ;(
You need to use this import 'next-auth/react' instead of 'next-auth/client' on the signin.tsx page.
Also, there appears to be all sorts of typescript issues. Resolved below:
Hi Nick - this is great. I'm working on a small project and was curious if you had any advice on how I may use Stytch's passwordless email magic links with NextAuth.js - its a similar solution: stytch.com/docs/api/send-by-email
I'm working through the credentials provider as well but getting hung up and I'm not sure where exactly - any advice would be greatly appreciated!!
i tried to use this method, but couldn't make it work. can you provide git link with js extension?
Running in to issue and cannot resolve. Can you help out?
Added screenshot