SIWE — Next.js Implementation
ConnectKit provides a simple way to add Sign In With Ethereum (SIWE) to your Next.js app.
1. Install
Once you've set up ConnectKit, install our SIWE helper package to your Next.js project.
Terminal
npm install connectkit-next-siwe viem@">=2.13.3"
2. Configure
Our SIWE package includes session handling and route helpers. You'll need to configure them before they can be used. We recommend creating two separate utility files that you can import into other areas of your app for easily retrieving session data.
The apiRoutePrefix refers to a new directory you'll create inside your pages/api directory for the SIWE-specific routes.
@/utils/siweClient.ts
import { configureClientSIWE } from "connectkit-next-siwe";export const siweClient = configureClientSIWE({apiRoutePrefix: "/api/siwe", // Your API route directorystatement: "Sign In With Ethereum to prove you control this wallet.", // optional});
The server configuration needs to be separate from the client so it does not get built into the frontend bundle.
You can import your ckConfig from your Web3Provider component to bring over your chains and transports configuration to use as the public client to verify the SIWE signature.
@/components/Web3Provider.tsx
import { getDefaultConfig } from "connectkit";export const ckConfig = getDefaultConfig({ ... });
@/utils/siweServer.ts
import { configureServerSideSIWE } from "connectkit-next-siwe";import { ckConfig } from "@/components/Web3Provider";export const siweServer = configureServerSideSIWE({config: {chains: ckConfig.chains,transports: ckConfig.transports,},session: {cookieName: "connectkit-next-siwe",password: process.env.SESSION_SECRET,cookieOptions: {secure: process.env.NODE_ENV === "production",},},});
You'll also want to set up an environment variable called SESSION_SECRET — a randomly generated, strong password of at least 32 characters. This is used to encrypt the browser cookie used by the session. Alternatively, you can set the session secret directly with session: { password: ... } } when using configureServerSideSIWE.
.env
SESSION_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Add a new catch-all API route to your app inside the directory that we configured with apiRoutePrefix.
It's important that this file be named [...route].ts to take advantage of Next's dynamic routing and because our package expects route as a named query parameter.
pages/api/siwe/[...route].ts
import { siweServer } from "@/utils/siweServer"; // Your path to siweServer.tsexport default siweServer.apiRouteHandler;
Once configured, wrap your Next.js app using the <siweClient.Provider> component, just like you've done previously with the ConnectKitProvider. This lets ConnectKit know that you're using SIWE and how to talk to your API routes.
pages/_app.tsx
import { ConnectKitProvider, SIWESession} from "connectkit";import { siweClient } from "@/utils/siweClient";<siweClient.Provider// Optional parametersenabled={true} // defaults truenonceRefetchInterval={300000} // in milliseconds, defaults to 5 minutessessionRefetchInterval={300000}// in milliseconds, defaults to 5 minutessignOutOnDisconnect={true} // defaults truesignOutOnAccountChange={true} // defaults truesignOutOnNetworkChange={true} // defaults trueonSignIn={(session?: SIWESession) => void}onSignOut={() => void}><ConnectKitProvider>/* Your App */</ConnectKitProvider></siweClient.Provider>
And that's it—the ConnectKit modal will now automatically walk your users through how to Sign In With Ethereum after connecting their wallet to your app.
If you wish to not show the SIWE page in the ConnectKit modal, you can set enabledSiweRedirect to false on the ConnectKitProvider options.
Example
Let's wire up a simple token-gated page. We want to make sure that this page only returns data server-side when the user has verified ownership of their wallet using SIWE and the wallet has collected a specific token.
pages/collectors-only.tsx
import type { GetServerSideProps, NextPage } from "next";import { siweServer } from "@/utils/siweServer";const walletHasToken = async (address: string): Promise<boolean> => {return // Your implementation of token-gated logic goes here}export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {const { address } = await siweServer.getSession(req, res);if (!address || !(await walletHasToken(address))) {return {redirect: {permanent: false,destination: '/login', // Redirect if wallet does not have the required token},};}return {props: {},};});const CollectorsOnlyPage: NextPage = () => {return <>Welcome, collector.</>;};export default CollectorsOnlyPage;
And that's it—we'll leave it to you to implement your token-gating logic in walletHasToken.
API Reference
configureServerSideSIWE Props
configureServerSideSIWE({session: {cookieName: string, // defaults to "connectkit-next-siwe"password: string, // defaults to `process.env.SESSION_SECRET`cookieOptions: {secure: boolean, // defaults to true if `process.env.NODE_ENV === 'production'`// see https://www.npmjs.com/package/cookie for other options},},options: {afterLogout: Promise<void>,afterNonce: Promise<void>,afterSession: Promise<void>,afterVerify: Promise<void>,},});
configureClientSIWE Props
configureClientSIWE({apiRoutePrefix: string,statement: string, // defaults to "Sign In With Ethereum."});
configureClientSIWE.Provider Props
import { ConnectKitProvider, SIWESession} from "connectkit";import { siweClient } from "@/utils/siweClient";<siweClient.Provider// Optional parametersenabled={true} // defaults truenonceRefetchInterval={300000} // in milliseconds, defaults to 5 minutessessionRefetchInterval={300000}// in milliseconds, defaults to 5 minutessignOutOnDisconnect={true} // defaults truesignOutOnAccountChange={true} // defaults truesignOutOnNetworkChange={true} // defaults trueonSignIn={(session?: SIWESession) => void}onSignOut={() => void}/>