CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rainbow-me--rainbowkit

A comprehensive React library that simplifies wallet connection functionality for decentralized applications with support for 66 wallets and extensive customization options

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

authentication.mddocs/

Authentication

SIWE (Sign-In with Ethereum) authentication integration for secure user authentication in RainbowKit applications.

Capabilities

RainbowKitAuthenticationProvider

Provider component that wraps your application to enable authentication functionality.

/**
 * Authentication provider component for SIWE integration
 * @param props - Authentication provider configuration
 * @returns React provider component for authentication
 */
function RainbowKitAuthenticationProvider<T>(
  props: RainbowKitAuthenticationProviderProps<T>
): JSX.Element;

interface RainbowKitAuthenticationProviderProps<T> extends AuthenticationConfig<T> {
  /** Whether authentication is enabled */
  enabled?: boolean;
  /** React children to wrap */
  children: ReactNode;
}

interface AuthenticationConfig<T> {
  /** Authentication adapter implementation */
  adapter: AuthenticationAdapter<T>;
  /** Current authentication status */
  status: AuthenticationStatus;
}

type AuthenticationStatus = 'loading' | 'unauthenticated' | 'authenticated';

Usage Examples:

import { 
  RainbowKitAuthenticationProvider,
  createAuthenticationAdapter,
  AuthenticationStatus,
} from '@rainbow-me/rainbowkit';
import { useState } from 'react';

function App() {
  const [authenticationStatus, setAuthenticationStatus] = useState<AuthenticationStatus>('loading');

  const authenticationAdapter = createAuthenticationAdapter({
    getNonce: async () => {
      const response = await fetch('/api/nonce');
      return await response.text();
    },

    createMessage: ({ nonce, address, chainId }) => {
      return `Sign this message to authenticate: ${nonce}`;
    },

    verify: async ({ message, signature }) => {
      const response = await fetch('/api/verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message, signature }),
      });
      
      const verified = await response.json();
      setAuthenticationStatus(verified ? 'authenticated' : 'unauthenticated');
      return verified;
    },

    signOut: async () => {
      await fetch('/api/logout', { method: 'POST' });
      setAuthenticationStatus('unauthenticated');
    },
  });

  return (
    <RainbowKitAuthenticationProvider
      adapter={authenticationAdapter}
      status={authenticationStatus}
    >
      {/* Your app */}
    </RainbowKitAuthenticationProvider>
  );
}

createAuthenticationAdapter

Factory function for creating authentication adapters with SIWE integration.

/**
 * Creates an authentication adapter for SIWE integration
 * @param adapter - Authentication adapter implementation
 * @returns Configured authentication adapter
 */
function createAuthenticationAdapter<T>(
  adapter: AuthenticationAdapter<T>
): AuthenticationAdapter<T>;

interface AuthenticationAdapter<T> {
  /** Fetches a nonce from your authentication server */
  getNonce: () => Promise<string>;
  /** Creates a message to be signed by the user */
  createMessage: (args: CreateMessageArgs) => T;
  /** Verifies the signed message on your server */
  verify: (args: VerifyArgs<T>) => Promise<boolean>;
  /** Signs out the user */
  signOut: () => Promise<void>;
}

interface CreateMessageArgs {
  /** Random nonce from server */
  nonce: string;
  /** User's wallet address */
  address: string;
  /** Current chain ID */
  chainId: number;
}

interface VerifyArgs<T> {
  /** The message that was signed */
  message: T;
  /** The signature from the user's wallet */
  signature: string;
}

Usage Examples:

import { createAuthenticationAdapter } from '@rainbow-me/rainbowkit';
import { SiweMessage } from 'siwe';

// SIWE (Sign-In with Ethereum) adapter
const siweAuthenticationAdapter = createAuthenticationAdapter({
  getNonce: async () => {
    const response = await fetch('/api/nonce');
    return await response.text();
  },

  createMessage: ({ nonce, address, chainId }) => {
    return new SiweMessage({
      domain: window.location.host,
      address,
      statement: 'Sign in with Ethereum to the app.',
      uri: window.location.origin,
      version: '1',
      chainId,
      nonce,
    });
  },

  verify: async ({ message, signature }) => {
    const verifyRes = await fetch('/api/verify', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ message, signature }),
    });

    return Boolean(verifyRes.ok);
  },

  signOut: async () => {
    await fetch('/api/logout', { method: 'POST' });
  },
});

// Simple message adapter
const simpleAuthenticationAdapter = createAuthenticationAdapter({
  getNonce: async () => {
    return Math.random().toString(36).substring(2);
  },

  createMessage: ({ nonce, address }) => {
    return `Please sign this message to authenticate: ${nonce}\nAddress: ${address}`;
  },

  verify: async ({ message, signature }) => {
    // Implement your verification logic
    return true;
  },

  signOut: async () => {
    console.log('Signed out');
  },
});

useAddRecentTransaction

Hook for adding transactions to the recent transactions list (useful for authenticated flows).

/**
 * Hook for adding transactions to recent transactions list
 * @returns Function to add transactions
 */
function useAddRecentTransaction(): (transaction: NewTransaction) => void;

interface NewTransaction {
  /** Transaction hash */
  hash: string;
  /** Human-readable description */
  description: string;
  /** Number of confirmations (optional) */
  confirmations?: number;
}

interface Transaction extends NewTransaction {
  /** Transaction status */
  status: 'pending' | 'confirmed' | 'failed';
}

Usage Examples:

import { useAddRecentTransaction } from '@rainbow-me/rainbowkit';
import { useContractWrite, useWaitForTransaction } from 'wagmi';

function TokenTransfer() {
  const addRecentTransaction = useAddRecentTransaction();

  const { data, write } = useContractWrite({
    // ... contract configuration
    onSuccess(data) {
      addRecentTransaction({
        hash: data.hash,
        description: 'Transfer tokens',
      });
    },
  });

  return (
    <button onClick={() => write?.()}>
      Transfer Tokens
    </button>
  );
}

Advanced Authentication Patterns

Full SIWE Implementation

import {
  RainbowKitAuthenticationProvider,
  createAuthenticationAdapter,
  AuthenticationStatus,
} from '@rainbow-me/rainbowkit';
import { SiweMessage } from 'siwe';
import { useState, useEffect } from 'react';
import { useAccount } from 'wagmi';

function AuthenticationWrapper({ children }) {
  const [authenticationStatus, setAuthenticationStatus] = useState<AuthenticationStatus>('loading');
  const { isConnected } = useAccount();

  useEffect(() => {
    const fetchStatus = async () => {
      if (!isConnected) {
        setAuthenticationStatus('unauthenticated');
        return;
      }

      try {
        const response = await fetch('/api/me');
        setAuthenticationStatus(response.ok ? 'authenticated' : 'unauthenticated');
      } catch {
        setAuthenticationStatus('unauthenticated');
      }
    };

    fetchStatus();
  }, [isConnected]);

  const authenticationAdapter = createAuthenticationAdapter({
    getNonce: async () => {
      const response = await fetch('/api/nonce');
      return await response.text();
    },

    createMessage: ({ nonce, address, chainId }) => {
      return new SiweMessage({
        domain: window.location.host,
        address,
        statement: 'Sign in with Ethereum to access protected features.',
        uri: window.location.origin,
        version: '1',
        chainId,
        nonce,
        expirationTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 24 hours
      });
    },

    verify: async ({ message, signature }) => {
      const response = await fetch('/api/verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          message: message.prepareMessage(),
          signature,
        }),
      });

      const success = response.ok;
      setAuthenticationStatus(success ? 'authenticated' : 'unauthenticated');
      return success;
    },

    signOut: async () => {
      await fetch('/api/logout', { method: 'POST' });
      setAuthenticationStatus('unauthenticated');
    },
  });

  return (
    <RainbowKitAuthenticationProvider
      adapter={authenticationAdapter}
      status={authenticationStatus}
    >
      {children}
    </RainbowKitAuthenticationProvider>
  );
}

Server-Side SIWE Verification

// API route: /api/verify
import { SiweMessage } from 'siwe';
import { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { method } = req;

  switch (method) {
    case 'POST':
      try {
        const { message, signature } = req.body;
        const siweMessage = new SiweMessage(message);
        
        const fields = await siweMessage.verify({ signature });

        if (fields.success) {
          // Store authentication state (session, JWT, etc.)
          req.session.siwe = fields.data;
          req.session.save();
          res.json({ success: true });
        } else {
          res.status(422).json({ message: 'Invalid signature.' });
        }
      } catch (error) {
        res.status(400).json({ message: error.message });
      }
      break;
    default:
      res.setHeader('Allow', ['POST']);
      res.status(405).end(`Method ${method} Not Allowed`);
  }
}

Conditional Rendering Based on Authentication

import { useAccount } from 'wagmi';
import { ConnectButton } from '@rainbow-me/rainbowkit';

function ProtectedContent() {
  const { isConnected } = useAccount();

  // This would typically use a custom hook to check authentication status
  // For example: const { isAuthenticated } = useAuthentication();
  
  if (!isConnected) {
    return (
      <div>
        <h2>Connect Your Wallet</h2>
        <p>Please connect your wallet to access this content.</p>
        <ConnectButton />
      </div>
    );
  }

  // In a real app, you'd check authentication status here
  return (
    <div>
      <h2>Protected Content</h2>
      <p>This content is only visible to authenticated users.</p>
      {/* Protected content here */}
    </div>
  );
}

Custom Authentication Status Hook

import { useState, useEffect } from 'react';
import { useAccount } from 'wagmi';

export function useAuthenticationStatus() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const { isConnected, address } = useAccount();

  useEffect(() => {
    const checkAuthStatus = async () => {
      if (!isConnected || !address) {
        setIsAuthenticated(false);
        setIsLoading(false);
        return;
      }

      try {
        const response = await fetch('/api/me');
        setIsAuthenticated(response.ok);
      } catch {
        setIsAuthenticated(false);
      } finally {
        setIsLoading(false);
      }
    };

    checkAuthStatus();
  }, [isConnected, address]);

  const signOut = async () => {
    try {
      await fetch('/api/logout', { method: 'POST' });
      setIsAuthenticated(false);
    } catch (error) {
      console.error('Sign out failed:', error);
    }
  };

  return {
    isAuthenticated,
    isLoading,
    signOut,
  };
}

Install with Tessl CLI

npx tessl i tessl/npm-rainbow-me--rainbowkit

docs

authentication.md

configuration.md

core-components.md

index.md

modal-controls.md

theming.md

wallet-connectors.md

tile.json