Web3 Session with NextAuth.js

In a decentralized application, a user is often identified by a Cryptocurrency wallet such as Metamask. However, since Metamask works by injecting a script into the page, it is only available on the client, cutting off the ability to use getServerSideProps to fetch user data.

We can solve this by pairing a NextAuth.js session with a convenient hooks library called WAGMI. We will need to configure NextAuth.js with the CredentialsProvider:

import NextAuth from 'next-auth'
import { utils } from 'ethers'
import CredentialsProvider from 'next-auth/providers/credentials'

export default NextAuth({
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        address: {
          label: 'Address',
          type: 'text',
          placeholder: '0x0',
        },
      },
      async authorize(credentials) {
        if (Boolean(utils.getAddress(credentials?.address!))) {
          return null
        }
        return {
          id: credentials?.address,
        }
      },
    }),
  ],
  session: {
    strategy: 'jwt',
  },
  jwt: {
    secret: process.env.JWT_SECRET,
  },
  callbacks: {
    async session({ session, token }) {
      session.address = token.sub
      return session
    },
  },
  secret: process.env.NEXT_AUTH_SECRET,
  pages: {
    signIn: '/',
    signOut: '/',
    error: '/',
    newUser: '/',
  },
})

Doing this will allow us to login users with a session:

// NextAuth.js signIn will help us create a session
import { signIn } from 'next-auth/react'
// hooks that allow to use metamask informations
import { useConnect, useAccount } from 'wagmi'

Function Login(){
  const [{ data: connectData }, connect] = useConnect()
  const [{ data: accountData }] = useAccount()

  const handleLogin = async () => {
    try {
      const callbackUrl = '/protected'
      if (accountData?.address) {
        signIn('credentials', { address: accountData.address, callbackUrl })
        return
      }
      const { data, error } = await connect(connectData.connectors[0])
      if (error) {
        throw error
      }
      signIn('credentials', { address: data?.account, callbackUrl })
    } catch (error) {
      window.alert(error)
    }
  }
  ... rest of your component

Please install Metamask to use this example.