Powered by

A design system from Family

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 directory
statement: "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.ts
export 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 parameters
enabled={true} // defaults true
nonceRefetchInterval={300000} // in milliseconds, defaults to 5 minutes
sessionRefetchInterval={300000}// in milliseconds, defaults to 5 minutes
signOutOnDisconnect={true} // defaults true
signOutOnAccountChange={true} // defaults true
signOutOnNetworkChange={true} // defaults true
onSignIn={(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

config
object
config properties
chains
Chains[]

The chains to use for verifying the SIWE signature.

transports
Record<number, Transport>

The transports to use for verifying the SIWE signature.

session
object
session properties
cookieName
string
Default is "connectkit-next-siwe"
password
string
Default is process.env.SESSION_SECRET
cookieOptions
cookieOptions properties
secure
boolean
Default is true

Defaults to false if process.env.NODE_ENV != 'production'.
See npmjs.com/package/cookie for other options.

options
object
options properties
afterLogout
Promise<void>
afterNonce
Promise<void>
afterSession
Promise<void>
afterVerify
Promise<void>
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

apiRoutePrefix
string
Required
statement
string
Default is "Sign In With Ethereum."

Human-readable ASCII assertion that the user will sign, and it must not contain `\n`.

configureClientSIWE({
apiRoutePrefix: string,
statement: string, // defaults to "Sign In With Ethereum."
});

configureClientSIWE.Provider Props

enabled
boolean
Default is true

Whether or not to enable SIWE.

nonceRefetchInterval
number
Default is 300000

How often to refetch the nonce, in milliseconds.

sessionRefetchInterval
number
Default is 300000

How often to refetch the session, in milliseconds.

signOutOnDisconnect
boolean
Default is true

Whether or not to sign out when the user disconnects their wallet.

signOutOnAccountChange
boolean
Default is true

Keeps SIWE session matching connected account

signOutOnNetworkChange
boolean
Default is true

Keeps the SIWE session and the connected account/network in sync

onSignIn
function

Callback when user signs in

onSignOut
function

Callback when user signs out

import { ConnectKitProvider, SIWESession} from "connectkit";
import { siweClient } from "@/utils/siweClient";
<siweClient.Provider
// Optional parameters
enabled={true} // defaults true
nonceRefetchInterval={300000} // in milliseconds, defaults to 5 minutes
sessionRefetchInterval={300000}// in milliseconds, defaults to 5 minutes
signOutOnDisconnect={true} // defaults true
signOutOnAccountChange={true} // defaults true
signOutOnNetworkChange={true} // defaults true
onSignIn={(session?: SIWESession) => void}
onSignOut={() => void}
/>