Better Auth – Worldcoin Minikit (SIWE)

Authenticate users via Worldcoin Minikit using Better Auth. This plugin provides a Sign-In With Ethereum (SIWE) flow adapted for Worldcoin Minikit applications.

Features

Installation

npm i better-auth-minikit

Server Setup

Add the minikit plugin to your Better Auth configuration. You need to provide implementations for nonce generation and message verification.

import { betterAuth } from "better-auth";
import { minikit } from "better-auth-minikit";
import { generateRandomString } from "better-auth/crypto";
import { parseSiweMessage, validateSiweMessage } from "viem/siwe"; // Example using viem

export const auth = betterAuth({
  plugins: [
    minikit({
      // 1. Domain expected in the SIWE message
      domain: "app.example.com",

      // 2. Nonce generation function
      getNonce: async () => {
        return generateRandomString(32);
      },

      // 3. Message verification function
      verifyMessage: async ({ message, signature, address, chainId }) => {
        try {
            const valid = await validateSiweMessage({
                message,
                signature,
                domain: "app.example.com",
                nonce: (await parseSiweMessage(message)).nonce,
            });
            return valid;
        } catch (e) {
            return false;
        }
      },

      // (Optional) Custom email domain for generated user emails
      // emailDomainName: "minikit-user.com",
      
      // (Optional) Allow anonymous users (default: true)
      // anonymous: true,
    })
  ]
});

Client Setup

Add the client plugin to your Better Auth client instance.

import { createAuthClient } from "better-auth/react";
import { minikitClient } from "better-auth-minikit/client";

const client = createAuthClient({
  plugins: [minikitClient()],
  fetchOptions: {
    credentials: "include", // Required for session cookies in MiniApps
  },
});

export const authClient = client;

Usage

The authentication flow consists of three steps: getting a nonce, signing the message, and verifying the signature.

1. Get Nonce

Request a nonce from the server for the user's wallet address.

const walletAddress = "0x..."; // User's wallet address
const chainId = 1; // Current chain ID

const { data, error } = await authClient.minikit.getNonce({
  walletAddress,
  chainId
});

const nonce = data.nonce;

2. Sign Message (Client Side)

Use the Worldcoin Minikit SDK (or any wallet provider) to sign the SIWE message.

import { MiniKit } from '@worldcoin/minikit-js';

// Construct SIWE message (example)
const message = createSiweMessage({
    domain: window.location.host,
    address: walletAddress,
    statement: "Sign in to My App",
    uri: window.location.origin,
    version: "1",
    chainId: chainId,
    nonce: nonce,
});

// Request signature from Minikit
const { commandPayload: signature } = await MiniKit.commands.signMessage({
    message: message
});

3. Verify and Sign In

Send the signature and message to the server to complete the sign-in.

const { data, error } = await authClient.minikit.signInWithMinikit({
  message,
  signature,
  walletAddress,
  chainId,
  user: {
      username: "user-handle", // Optional
      profilePictureUrl: "https://..." // Optional
  }
});

if (data?.success) {
  console.log("Signed in user:", data.user);
}

Database Schema

This plugin adds a walletAddress table and extends the user and account tables.

walletAddress

Field Type Notes
id string Primary Key
userId string Foreign Key to User
address string Wallet Address
chainId number Chain ID
isPrimary boolean Whether this is the primary wallet
createdAt date Creation timestamp

User & Account Extensions

Migration

Run the migration command to update your database schema:

npx @better-auth/cli migrate