Skip to content

random-bits-studio/rainbowkit-use-siwe-auth

Repository files navigation

UseSIWE + RainbowKit = ❤️

This package is a RainbowKit authentication adapter for UseSIWE.

This is by far the easiest way to add Sign-In with Ethereum support to your application!

Table of Contents

Installation

To install rainbowkit-use-siwe-auth and it's dependencies run the following command:

npm install @randombits/rainbowkit-use-siwe-auth @randombits/use-siwe @rainbow-me/rainbowkit wagmi ethers iron-session

Getting Started

Setup RainbowKit

Follow the instructions on the RainbowKit Docs to setup Rainbowkit in your app. In the end you should have an _app.tsx (if using Next.js) that looks something like this:

import '@/styles/globals.css'
import '@rainbow-me/rainbowkit/styles.css'
import type { AppProps } from 'next/app'
import {
  getDefaultWallets,
  RainbowKitProvider,
} from '@rainbow-me/rainbowkit';
import { configureChains, createClient, WagmiConfig } from 'wagmi';
import { mainnet, polygon, optimism, arbitrum } from 'wagmi/chains';
import { publicProvider } from 'wagmi/providers/public';

const { chains, provider } = configureChains(
  [mainnet, polygon, optimism, arbitrum],
  [publicProvider()]
);

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

const wagmiClient = createClient({
  autoConnect: true,
  connectors,
  provider
})

export default function App({ Component, pageProps }: AppProps) {
  return (
    <WagmiConfig client={wagmiClient}>
      <RainbowKitProvider chains={chains}>
        <Component {...pageProps} />
      </RainbowKitProvider>
    </WagmiConfig>
  );
}

...and you should have added a <ConnectButton /> somewhere in your application.

Setup UseSIWE

Follow the Getting Started instructions in the UseSIWE Docs for configuring Iron Session, Setting up the API routes, and wrapping your app with <SiweProvider>.

Once completed, your application tree should now contain the following:

my-project
├── lib
│   └── ironOptions.ts <----
├── package.json
├── pages
│   ├── _app.tsx
│   ├── _document.tsx
│   ├── api
│   │   ├── auth
│   │   │   └── [[...route]].ts <----
│   │   └── hello.ts
│   └── index.tsx
├── public
└── styles

...and the providers wrapping your application should look like this:

export default function App({ Component, pageProps }: AppProps) {
  return (
    <WagmiConfig client={wagmiClient}>
      <SiweProvider>
        <RainbowKitProvider chains={chains}>
          <Component {...pageProps} />
        </RainbowKitProvider>
      </SiweProvider>
    </WagmiConfig>
  );
}

Add the UseSIWE authentication adapter

One more provider and this pyramid is complete! Add the RainbowKitUseSiweProvider inside of the SiweProvider so that it contains the RainbowKitProvider. Example below:

import RainbowKitUseSiweProvider from '@randombits/rainbowkit-use-siwe-auth';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <WagmiConfig client={wagmiClient}>
      <SiweProvider>
        <RainbowKitUseSiweProvider>
          <RainbowKitProvider chains={chains}>
            <Component {...pageProps} />
          </RainbowKitProvider>
        </RainbowKitUseSiweProvider>
      </SiweProvider>
    </WagmiConfig>
  );
}

Technically thats it! Now when you run your app and click on the ConnectButton and connect a wallet, a second modal will pop up asking you to sign a message to complete the SIWE flow.

To learn how to check if a user is authenticated, read on!

Check to see if a user is authenticated

Client-side

Check to see is a user is authenticated with the useSession hook from UseSiwe like in the example below:

import { useSession } from "@randombits/use-siwe";

export const AuthCheck = () => {
  const { isLoading, authenticated, address } = useSession();

  if (isLoading) return <p>Loading...</p>;
  if (!authenticated) return <p>Not authenticated</p>;
  return <p>{address} is Authenticated</p>;
};

Server-side

For API routes, wrap your API handler with withIronSessionApiRoute and check to see if req.session.address is set. If a user is authenticated, req.session.address will be set to their address, otherwise it will be undefined.

import ironOptions from '@/lib/ironOptions'
import { withIronSessionApiRoute } from 'iron-session/next/dist'
import type { NextApiHandler } from 'next'

const handler: NextApiHandler = (req, res) => {
  if (!req.session.address) return res.status(401).send("Unauthorized");
  res.status(200).send(`Hello, ${req.session.address}!`);
}

export default withIronSessionApiRoute(handler, ironOptions);

Taking action on Sign-In and Sign-Out

The RainbowKitUseSiweProvider component takes two optional props; onSignIn and onSignOut. You may pass a function that will be called after a successful sign-in or sign-out; for instance to redirect a user to a different page.

import RainbowKitUseSiweProvider from '@randombits/rainbowkit-use-siwe-auth';
import { useRouter } from 'next/router';

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();
  const onSignIn = () => router.push('/dashboard');
  const onSignOut = () => router.push('/');

  return (
    <WagmiConfig client={wagmiClient}>
      <SiweProvider>
        <RainbowKitUseSiweProvider onSignIn={onSignIn} onSignOut={onSignOut}>
          <RainbowKitProvider chains={chains}>
            <Component {...pageProps} />
          </RainbowKitProvider>
        </RainbowKitUseSiweProvider>
      </SiweProvider>
    </WagmiConfig>
  );
}