Skip to content

GaloisField2718/wallet-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bitcoin Wallet Authentication System

A complete, self-contained Bitcoin wallet authentication system for Next.js applications. This system provides secure, cryptographic authentication using Bitcoin wallet signatures with address whitelisting.

Features

  • 🔐 Cryptographic Authentication: Users sign challenge messages with their Bitcoin wallet
  • 🛡️ Address Whitelisting: Only authorized Bitcoin addresses can access your application
  • 🔒 Secure Sessions: Server-side session management with HttpOnly cookies
  • 🚪 Route Protection: Automatic middleware-based route protection
  • 💼 Multi-Wallet Support: Compatible with UniSat, Xverse, and OKX wallets
  • 📊 Supabase Integration: Database-backed whitelist management

System Architecture

Authentication Flow

1. User visits protected page
2. Middleware redirects to /login
3. User connects Bitcoin wallet (UniSat, Xverse, OKX)
4. System generates unique challenge message
5. User signs challenge with wallet private key
6. Server verifies signature and checks whitelist
7. If valid, creates secure session and redirects to app

Security Features

  • Challenge-Response Authentication: Unique challenge messages prevent replay attacks
  • Server-Side Verification: All authentication logic runs on the server
  • Secure Cookies: HttpOnly cookies prevent client-side manipulation
  • Row Level Security: Database policies prevent unauthorized access
  • Session Expiration: Sessions expire after 24 hours

Installation & Setup

See SETUP.md for detailed installation instructions.

File Structure

auth/
├── README.md                          # This file
├── SETUP.md                          # Installation guide
├── components/
│   ├── auth-guard.tsx                # Client-side auth guard component
│   └── wallet-connector.tsx          # Wallet connection UI component
├── lib/
│   ├── auth.ts                       # Server-side auth functions
│   ├── auth-utils.ts                 # Client-side auth utilities
│   └── supabase/
│       ├── client.ts                 # Supabase client setup
│       └── server.ts                 # Supabase server setup
├── api/
│   └── auth/
│       ├── challenge/route.ts        # Generate challenge message
│       ├── verify/route.ts           # Verify signature and create session
│       ├── session/route.ts          # Check session status
│       └── logout/route.ts           # Clear session
├── pages/
│   └── login/page.tsx                # Login page UI
├── middleware.ts                     # Route protection middleware
└── scripts/
    ├── 002_create_whitelist_table.sql    # Database schema
    └── 003_add_whitelist_addresses.sql   # Add authorized addresses

API Endpoints

Authentication APIs

  • GET /api/auth/challenge - Generate a unique challenge message for signing
  • POST /api/auth/verify - Verify wallet signature and create authentication session
  • GET /api/auth/session - Check current session status
  • POST /api/auth/logout - Clear session and logout user

Request/Response Examples

Get Challenge

// Request
GET /api/auth/challenge

// Response
{
  "success": true,
  "challenge": "Bitcoin PSBT Builder Authentication\nTimestamp: 1234567890\nNonce: abc123..."
}

Verify Signature

// Request
POST /api/auth/verify
{
  "address": "bc1q...",
  "message": "Bitcoin PSBT Builder Authentication...",
  "signature": "base64_signature..."
}

// Response
{
  "success": true,
  "message": "Authentication successful",
  "address": "bc1q..."
}

Usage Examples

Protecting a Page

// app/dashboard/page.tsx
import { AuthGuard } from "@/auth/components/auth-guard"

export default function DashboardPage() {
  return (
    <AuthGuard>
      <div>Your protected content here</div>
    </AuthGuard>
  )
}

Using the Wallet Connector

// app/login/page.tsx
import { WalletConnector } from "@/auth/components/wallet-connector"

export default function LoginPage() {
  const handleConnect = (address: string) => {
    console.log("Connected:", address)
    // Handle wallet connection
  }

  return (
    <WalletConnector
      onConnect={handleConnect}
      onDisconnect={() => console.log("Disconnected")}
    />
  )
}

Checking Auth Status (Client-Side)

import { useAuth, logout } from "@/auth/lib/auth-utils"

function MyComponent() {
  const { isAuthenticated, address, isLoading } = useAuth()

  if (isLoading) return <div>Loading...</div>

  return (
    <div>
      {isAuthenticated ? (
        <div>
          <p>Connected: {address}</p>
          <button onClick={logout}>Logout</button>
        </div>
      ) : (
        <p>Not authenticated</p>
      )}
    </div>
  )
}

Server-Side Session Verification

// app/api/protected-endpoint/route.ts
import { verifyAuthSession } from "@/auth/lib/auth"

export async function GET() {
  const session = await verifyAuthSession()
  
  if (!session) {
    return Response.json({ error: "Unauthorized" }, { status: 401 })
  }

  return Response.json({
    message: "Access granted",
    address: session.address
  })
}

Environment Variables

Required environment variables:

# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key

# Optional
NODE_ENV=development # or production

Database Schema

The authentication system requires a whitelist table in Supabase:

CREATE TABLE whitelist (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  bitcoin_address TEXT NOT NULL UNIQUE,
  label TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  is_active BOOLEAN DEFAULT true
);

See scripts/002_create_whitelist_table.sql for the complete schema setup.

Managing Authorized Addresses

Add Address via SQL

INSERT INTO whitelist (bitcoin_address, label) VALUES
  ('bc1qyour_address_here', 'User Description');

Remove Access

-- Soft delete (recommended)
UPDATE whitelist 
SET is_active = false 
WHERE bitcoin_address = 'bc1q...';

-- Hard delete
DELETE FROM whitelist 
WHERE bitcoin_address = 'bc1q...';

List All Authorized Addresses

SELECT bitcoin_address, label, created_at, is_active 
FROM whitelist 
WHERE is_active = true 
ORDER BY created_at DESC;

Security Considerations

What This System Prevents

✅ Unauthorized access (only whitelisted addresses can authenticate)
✅ Session hijacking (HttpOnly cookies prevent client-side access)
✅ Replay attacks (unique challenge messages with timestamps)
✅ Client-side bypass (all verification happens server-side)
✅ Database tampering (Row Level Security policies)

Important Security Notes

⚠️ Private Key Security: Users must keep their Bitcoin private keys secure
⚠️ Wallet Compatibility: Requires compatible Bitcoin wallet browser extension
⚠️ HTTPS in Production: Always use HTTPS in production environments
⚠️ Session Duration: Sessions expire after 24 hours by default
⚠️ Address Management: Remove addresses from whitelist to immediately revoke access

Customization

Adjusting Session Duration

Edit lib/auth.ts:

export async function createAuthSession(address: string): Promise<string> {
  const session: AuthSession = {
    address,
    isAuthenticated: true,
    expiresAt: Date.now() + 24 * 60 * 60 * 1000, // Change this value
  }
  // ...
}

Customizing Protected Routes

Edit middleware.ts:

const PUBLIC_ROUTES = ["/login", "/api/auth", "/public-page"]

Custom Challenge Message

Edit lib/auth.ts:

export function generateChallengeMessage(): string {
  const timestamp = Date.now()
  const nonce = Math.random().toString(36).substring(2, 15)
  
  return `Your Custom Message\nTimestamp: ${timestamp}\nNonce: ${nonce}`
}

Troubleshooting

"Address not authorized"

  • Check if address is in whitelist table
  • Verify is_active = true for the address
  • Check for typos in the Bitcoin address

"Invalid signature"

  • Ensure wallet is properly connected
  • Try signing the message again
  • Verify wallet compatibility (UniSat works best)

"Session expired"

  • Sessions expire after 24 hours
  • Re-authenticate by visiting /login
  • Check server time synchronization

Middleware redirect loops

  • Verify public routes are properly excluded in middleware.ts
  • Check that /login is in the PUBLIC_ROUTES array
  • Clear browser cookies and cache

Wallet not detected

  • Install the wallet extension (UniSat, Xverse, or OKX)
  • Refresh the browser after installation
  • Check browser console for connection errors

Production Deployment Checklist

  • Update whitelist with real authorized addresses
  • Configure all environment variables in production
  • Enable HTTPS for secure cookie transmission
  • Review and test Row Level Security policies
  • Consider shorter session timeouts for high-security needs
  • Set up monitoring and logging
  • Test with all supported wallet types
  • Document authorized users and their addresses

Dependencies

Required npm packages:

{
  "@supabase/ssr": "^0.x.x",
  "@supabase/supabase-js": "^2.x.x",
  "next": "^14.x.x",
  "react": "^18.x.x"
}

License

This authentication system is designed to be integrated into your Next.js application. Modify and use as needed.

Support

For issues or questions:

  1. Check the troubleshooting section above
  2. Review the SETUP.md file for installation steps
  3. Verify your environment variables are correctly configured
  4. Check the Supabase dashboard for database connectivity

Changelog

Version 1.0.0

  • Initial release
  • Bitcoin wallet authentication
  • Multi-wallet support (UniSat, Xverse, OKX)
  • Supabase integration
  • Route protection middleware
  • Session management

About

Wallet signing authentication

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors