Skip to main content

How to Authenticate Users with RainbowKit

Before Starting

You can start this tutorial if you already have a NextJS dapp with MetaMask sign-in functionality.

RainbowKit Installation

npm install @rainbow-me/rainbowkit

RainbowKit Configuration

Modify pages/_app.jsx:

import { createClient, configureChains, WagmiConfig } from 'wagmi';
import { publicProvider } from 'wagmi/providers/public';
import { mainnet } from "wagmi/chains";
import { SessionProvider } from 'next-auth/react';
import { getDefaultWallets, RainbowKitProvider } from '@rainbow-me/rainbowkit';
import '@rainbow-me/rainbowkit/styles.css';

const { provider, webSocketProvider, chains } = configureChains([mainnet], [publicProvider()]);

const { connectors } = getDefaultWallets({
appName: 'My RainbowKit App',
chains,
});

const client = createClient({
provider,
webSocketProvider,
autoConnect: true,
// added connectors from rainbowkit
connectors,
});

// added RainbowKitProvider wrapper
function MyApp({ Component, pageProps }) {
return (
<WagmiConfig client={client}>
<SessionProvider session={pageProps.session} refetchInterval={0}>
<RainbowKitProvider chains={chains}>
<Component {...pageProps} />
</RainbowKitProvider>
</SessionProvider>
</WagmiConfig>
);
}

export default MyApp;

Authentication with RainbowKit

The logic we're achieving works as this. A user connects his wallet using ConnectButton from rainbowkit. Once the wallet is connected, we get address and chain from the following wagmi hooks: useAccount() and useNetwork(). In case the user is not authenticated, we will start the authentication flow (request and sign message).

  1. Open the pages/signin.jsx file and replace the old Authenticate via MetaMask button with <ConnectButton /> from @rainbow-me/rainbowkit:
import { ConnectButton } from '@rainbow-me/rainbowkit';
...

return (
<div>
<h3>Web3 Authentication</h3>
<ConnectButton />
</div>
);
...
  1. Edit handleAuth() and move it under useEffect():
...
useEffect(() => {
const handleAuth = async () => {
const userData = { address, chain: chain.id, network: 'evm' }

const { data } = await axios.post('/api/auth/request-message', userData, {
headers: {
'Content-Type': 'application/json',
},
})

const message = data.message

const signature = await signMessageAsync({ message })

// redirect user after success authentication to '/user' page
const { url } = await signIn('credentials', {
message,
signature,
redirect: false,
callbackUrl: '/user',
})
/**
* instead of using signIn(..., redirect: "/user")
* we get the url from callback and push it to the router to avoid page refreshing
*/
push(url)
}
// if is not authnenticated, but wallet is connected we request a signing message and sign it
if (status === 'unauthenticated' && isConnected) {
handleAuth()
}
}, [status, isConnected])
...
  1. Update missing imports and add new hooks. This is the final code of pages/signin.jsx:
import { ConnectButton } from '@rainbow-me/rainbowkit'
import { signIn, useSession } from 'next-auth/react'
import { useAccount, useSignMessage, useNetwork } from 'wagmi'
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import axios from 'axios'

function SignIn() {
const { isConnected, address } = useAccount()
const { chain } = useNetwork()
const { status } = useSession()
const { signMessageAsync } = useSignMessage()
const { push } = useRouter()

useEffect(() => {
const handleAuth = async () => {
const userData = { address, chain: chain.id, network: 'evm' }

const { data } = await axios.post('/api/auth/request-message', userData, {
headers: {
'content-type': 'application/json',
},
})

const message = data.message

const signature = await signMessageAsync({ message })

// redirect user after success authentication to '/user' page
const { url } = await signIn('credentials', {
message,
signature,
redirect: false,
callbackUrl: '/user',
})
/**
* instead of using signIn(..., redirect: "/user")
* we get the url from callback and push it to the router to avoid page refreshing
*/
push(url)
}
if (status === 'unauthenticated' && isConnected) {
handleAuth()
}
}, [status, isConnected])

return (
<div>
<h3>Web3 Authentication</h3>
<ConnectButton />
</div>
)
}

export default SignIn

Testing the RainbowKit Connector

Visit http://localhost:3000/signin to test authentication.

  1. Click on Connect Wallet:

Sign In Page

  1. Select and connect a wallet you want to use for authentication from the RainbowKit modal:

RainbowKit Modal

  1. Sign the message:

Signing the Message with MetaMask

  1. After successful authentication, you will be redirected to the /user page:

User Page

  1. Visit http://localhost:3000/user to test the user session's functionality:
  • When a user is authenticated, we show the user's info on the page.
  • When a user is not authenticated, we redirect to the /signin page.
  • When a user is authenticated, we show the user's info on the page, even refreshing after the page. (Explanation: After Web3 wallet authentication, the next-auth library creates a session cookie with an encrypted JWT [JWE] stored inside. It contains session info [such as an address and signed message] in the user's browser.)