https://github.com/random-bits-studio/use-siwe
The easiest way to add Sign-in with Ethereum to your app.
https://github.com/random-bits-studio/use-siwe
auth authentication ethereum express expressjs ironsession next nextjs react session siwe wagmi web3
Last synced: about 1 month ago
JSON representation
The easiest way to add Sign-in with Ethereum to your app.
- Host: GitHub
- URL: https://github.com/random-bits-studio/use-siwe
- Owner: random-bits-studio
- License: mit
- Created: 2022-09-19T15:05:04.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2023-10-28T07:23:00.000Z (over 1 year ago)
- Last Synced: 2025-02-17T17:17:18.769Z (about 2 months ago)
- Topics: auth, authentication, ethereum, express, expressjs, ironsession, next, nextjs, react, session, siwe, wagmi, web3
- Language: TypeScript
- Homepage:
- Size: 365 KB
- Stars: 25
- Watchers: 2
- Forks: 3
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
- awesome-nextjs - UseSIWE - React hooks and Next.js API routes that make it super easy to add Sign-In with Ethereum to your app. (Extensions)
- fucking-awesome-nextjs - UseSIWE - React hooks and Next.js API routes that make it super easy to add Sign-In with Ethereum to your app. (Extensions)
README
# UseSIWE
UseSIWE is a library that provides react hooks and API endpoints that make it
dead simple to add Sign-In with Ethereum functionality to your react
application.### 🌈 Works with RainbowKit
The easiest way to use this library is with RainbowKit!
Check out the RainbowKit authentication adapter for UseSiwe here:
https://github.com/random-bits-studio/rainbowkit-use-siwe-auth# Table of Contents
- [Installation](#installation)
- [Getting Started](#getting-started)
- [Configure settings for iron-session](#configure-settings-for-iron-session)
- [Setting up the API routes](#setting-up-the-api-routes)
- [Next.js](#nextjs)
- [Express.js](#expressjs)
- [Wrapping your application with SiweProvider](#wrapping-your-application-with-siweprovider)
- [Using the hooks](#using-the-hooks)
- [Checking if a user is authenticated](#checking-if-a-user-is-authenticated)
- [Signing In](#signing-in)
- [Signing Out](#signing-out)
- [API](#api)
- [Types](#types)
- [UseSiweOptions](#usesiweoptions)
- [Components](#components)
- [SiweProvider](#siweprovider)
- [Hooks](#hooks)
- [useSession](#usesession)
- [useSignIn](#usesignin)
- [useSignOut](#usesignout)
- [useOptions](#useoptions)
- [Routes](#routes)
- [Next.js: SiweApi](#nextjs-siweapi)
- [Express.js: SiweApi](#expressjs-siweapi)
- [Functions](#functions)
- [getSession](#getsession)
- [createMessage](#createmessage)
- [getMessageBody](#getmessagebody)
- [verify](#verify)
- [signOut](#signout)# Installation
To install UseSIWE and it's dependencies run the following command:
```
npm install @randombits/use-siwe wagmi ethers iron-session
```# Getting Started
## Configure settings for `iron-session`
Copy and paste the following code into a new file in your project:
```ts
// lib/ironOptions.tsimport { IronSessionOptions } from 'iron-session';
if (!process.env.IRON_SESSION_PASSWORD)
throw new Error('IRON_SESSION_PASSWORD must be set');const ironOptions: IronSessionOptions = {
password: process.env.IRON_SESSION_PASSWORD,
cookieName: 'session',
cookieOptions: {
secure: process.env.NODE_ENV === "production",
},
};declare module "iron-session" {
interface IronSessionData {
address?: string | undefined;
nonce?: string | undefined;
}
}export default ironOptions;
```**Remember to set IRON_SESSION_PASSWORD** in your `.env.local` file for
development, and in your production environment through your hosting
provider settings. The password must be at least 32 characters long. You can
use https://1password.com/password-generator/ to generate strong passwords.For full reference of possible options see:
https://github.com/vvo/iron-session#ironoptions**Typing session data**
The type definition of `IronSessionData` in the example above provides a type
definition to the data passed to api functions in `req.session`. `address` and
`nonce` are used and set by UseSIWE; if you plan on storing other data in the
session, feel free to add additional types here.For more information see:
https://github.com/vvo/iron-session#typing-session-data-with-typescript## Setting up the API routes
### Next.js
Copy and past the following code into `pages/api/auth/[[...route]].ts`:
```ts
import { withIronSessionApiRoute } from "iron-session/next";
import ironOptions from "lib/ironOptions";
import { siweApi } from "@randombits/use-siwe/next"export default withIronSessionApiRoute(siweApi(), ironOptions);
```### Express.js
To add auth routes to your existing express API, add the following:
```ts
import express from "express";
import { ironSession } from "iron-session/express";
import ironOptions from "./ironOptions.js";
import { authRouter } from "@randombits/use-siwe/express";const app = express();
// Add iron session middleware before all routes that will use session data
app.use(ironSession(ironOptions));// Your existing api routes here...
// Add UseSIWE auth routes
app.use('/auth', authRouter());app.listen(3001);
```## Wrapping your application with `SiweProvider`
Any component that uses the any of the UseSIWE hooks must be wrapped with the
`SiweProvider` component. For a Next.js application we recommend doing so in
`pages/_app.tsx` like in the example below:```ts
// pages/_app.tsximport type { AppProps } from 'next/app';
import { configureChains, mainnet } from 'wagmi';
import { publicProvider } from 'wagmi/providers/public';
import { SiweProvider } from '@randombits/use-siwe';const { chains, provider, webSocketProvider } = configureChains(
[mainnet],
[publicProvider()],
);const client = createClient({
autoConnect: true,
provider,
webSocketProvider,
});export default function MyApp({ Component, pageProps }: AppProps) {
return (
);
}
```**Important:** The `SiweProvider` must be inside a `WagmiConfig` component.
## Using the hooks
### Checking if a user is authenticated
#### Client-side
Check to see is a user is authenticated with the `useSession` hook like in the
example below:```ts
import { useSession } from "@randombits/use-siwe";export const AuthCheck = () => {
const { isLoading, authenticated, address } = useSession();if (isLoading) return
Loading...
;
if (!authenticated) returnNot authenticated
;
return{address} is Authenticated
;
};
```#### 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`.```ts
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);
```### Signing In
Login the user by calling the `signIn` function returned by the `useSignIn`
hook:```ts
import { useSignIn } from "@randombits/use-siwe";const SignInButton = () => {
const { signIn, isLoading } = useSignIn();
return signIn()} disabled={isLoading}>Sign In with Ethereum;
};
```### Signing Out
Logout the user by calling the `signOut` function returned by the `useSignOut`
hook:```ts
import { useSignOut } from "@randombits/use-siwe";const SignOutButton = () => {
const { signOut, isLoading } = useSignOut();
return signOut()} disabled={isLoading}>Sign Out;
};
```# API
## Types
### UseSiweOptions
UseSIWE accepts an object of options. Currently this consists of one optional
setting:#### Usage
```ts
const options: UseSiweOptions = {
baseUrl: "/v2/api/auth",
};
```#### Options
- `baseUrl`, optional: The base url for the auth API endpoints that is
prepended to all requests. Defaults to: `/api/auth`## Components
### SiweProvider
Context provider component that must wrap all components that use `useSession`,
`useSignIn`, `useSignOut`, or `useOptions` hooks.#### Usage
```ts
import type { AppProps } from 'next/app';
import { SiweProvider } from '@randombits/use-siwe';export default function MyApp({ Component, pageProps }: AppProps) {
return
;
}
```#### Props
- `options`, Optional: A `UseSiweOptions` object.
## Hooks
### useSession
A hook that returns the the current state of the users session.
#### Usage
```ts
import { useSession } from "@randombits/use-siwe";export const Component = () => {
const { isLoading, authenticated, address } = useSession();if (isLoading) return
Loading...;
if (!authenticated) returnNot Signed In;
returnHello, {address}!;
};
```#### Return Value
Returns a `UseQueryResult` ([ref](https://tanstack.com/query/latest/docs/react/reference/useQuery))
augmented with the following:```ts
{
authenticated: boolean;
address?: string;
nonce?: string;
} & UseQueryResult
```### useSignIn
A hook that returns a `signIn` function that will initiate a SIWE flow, as well
as the status of that signIn process.#### Usage
```ts
import { useSignIn } from "@randombits/use-siwe";const SignInButton = () => {
const { signIn, isLoading } = useSignIn();
return signIn()} disabled={isLoading}>Sign In with Ethereum;
};
```#### Options
```ts
{
onSuccess: () => void,
onError: () => void,
}
```#### Return Value
Returns a `UseMutationResult` ([ref](https://tanstack.com/query/latest/docs/react/reference/useMutation))
augmented with the following:```ts
{
signIn: () => void,
SignInAsync: () => Promise,
} & UseMutationResult
```### useSignOut
A hook that returns a `signOut` function that when called will sign out the
current user and disconnect their wallet.#### Usage
```ts
import { useSignOut } from "@randombits/use-siwe";const SignOutButton = () => {
const { signOut, isLoading } = useSignOut();
return signOut()} disabled={isLoading}>Sign Out;
};
```#### Options
```ts
{
onSuccess: () => void,
onError: () => void,
}
```#### Return Value
Returns a `UseMutationResult` ([ref](https://tanstack.com/query/latest/docs/react/reference/useMutation))
augmented with the following:```ts
{
signOut: () => void,
SignOutAsync: () => Promise,
} & UseMutationResult
```### useOptions
A hook that simply returns the options that have been set by in the
`SiweProvider` component.#### Usage
```ts
import { useOptions, verify } from "@randombits/use-siwe";const verifyButton = (props) => {
const options = useOptions();
const handleClick = () => verify({
message: props.message,
signature: props.signature,
}, options);return handleClick()}>Verify Signature;
};
```#### Return Value
```ts
useSiweOptions
```## Routes
### Next.js: SiweApi
A function that returns a `NextApiHandler` that will handle all auth API
routes.#### Usage
```ts
import { withIronSessionApiRoute } from "iron-session/next";
import ironOptions from "lib/ironOptions";
import { siweApi } from "@randombits/use-siwe/next"export default withIronSessionApiRoute(siweApi(), ironOptions);
```#### Return Value
```ts
NextApiHandler
```### Express.js: SiweApi
A function that returns an express `Router` that will handle all auth API
routes.#### Usage
```ts
import express from "express";
import { ironSession } from "iron-session/express";
import ironOptions from "./ironOptions.js";
import { authRouter } from "@randombits/use-siwe/express";const app = express();
app.use(ironSession(ironOptions));
app.use('/auth', authRouter());app.listen(3001);
```#### Return Value
```ts
Router
```## Functions
### getSession
A function to retrieve the session data where using a hook doesn't make sense.
#### Usage
```ts
import { getSession } from "@randombits/use-siwe";const addressOrNull = async () => {
const { address } = await getSession();
if (!address) return null;
return address;
};
```#### Args
- `options?: UseSiweOptions`
#### Return Value
```ts
{
authenticated: boolean;
address?: string;
nonce?: string;
}
```### createMessage
Returns a `SiweMessage` for the given address, chainId, and nonce.
#### Usage
```ts
import { createMessage, getMessageBody } from "@randombits/use-siwe";const debugMessage = (address, chainId, nonce) => {
const message = createMessage({ address, chainId, nonce });
const messageBody = getMessageBody({ message });
console.log({ message, messageBody });
};
```#### Args
- `args: MessageArgs`
```ts
type MessageArgs = {
address: string,
chainId: number,
nonce: string,
};
```#### Return Value
```ts
SiweMessage
```### getMessageBody
Returns a message ready to be signed according with the type defined in the
SiweMessage object.#### Usage
```ts
import { createMessage, getMessageBody } from "@randombits/use-siwe";const debugMessage = (address, chainId, nonce) => {
const message = createMessage({ address, chainId, nonce });
const messageBody = getMessageBody({ message });
console.log({ message, messageBody });
};
```#### Args
- `args: { message: SiweMessage }`
#### Return Value
```ts
string
```### verify
Takes a message and a signature as arguments and attempts to verify them using
the auth API. A successful verification will create a session for the user.#### Usage
```ts
import { verify } from "@randombits/use-siwe";const verifyButton = (props) => {
const handleClick = () => {
const success = verify({
message: props.message,
signature: props.signature,
});if (!success) return console.error("VERIFICATION FAILED");
console.log("SIGNATURE VERIFIED");
};return handleClick()}>Verify Signature;
};
```#### Args
- `args: VerifyArgs`
- `options?: UseSiweOptions````ts
type VerifyArgs = {
message: SiweMessage,
signature: string,
};
```#### Return Value
```ts
boolean
```### signOut
A function to sign out the user where using a hook doesn't make sense.
#### Usage
```ts
import { signOut } from "@randombits/use-siwe";// Logout a user after 1 hour
setTimeout(async () => {
await signOut();
window.location.href = "/session-expired";
}, 60 * 60 * 1000);
```#### Args
- `options?: UseSiweOptions`
#### Return Value
```ts
Promise
```