Privy handles the initial authentication flow, managing user sessions and wallet connections. You can also implement additional authentication layers for enhanced security or custom requirements.The code snippets in this guide are based on the following example project:
For additional security or custom authentication requirements, you can implement backend verification using Sign-In with Ethereum (SIWE)
with the Coinbase Wallet SDK.
Development Only: This backend implementation is not production-ready. The nonce management system needs proper persistence and security enhancements for production use.
import { NextResponse } from 'next/server';import crypto from 'crypto';import { nonceStore } from '@/lib/nonce-store';export async function GET() { try { const nonce = crypto.randomBytes(16).toString('hex'); nonceStore.add(nonce); return NextResponse.json({ nonce }); } catch (error) { return NextResponse.json( { error: 'Failed to generate nonce' }, { status: 500 } ); }}
import { NextRequest, NextResponse } from 'next/server';import { createPublicClient, http } from 'viem';import { base } from 'viem/chains';import { nonceStore } from '@/lib/nonce-store';const client = createPublicClient({ chain: base, transport: http() });export async function POST(request: NextRequest) { try { const { address, message, signature } = await request.json(); // Extract nonce from SIWE message const nonce = message.match(/Nonce: (\w+)/)?.[1]; if (!nonce || !nonceStore.consume(nonce)) { return NextResponse.json( { error: 'Invalid or reused nonce' }, { status: 400 } ); } // Verify signature using viem const valid = await client.verifyMessage({ address: address as `0x${string}`, message, signature: signature as `0x${string}` }); if (!valid) { return NextResponse.json( { error: 'Invalid signature' }, { status: 401 } ); } return NextResponse.json({ success: true, address, timestamp: new Date().toISOString() }); } catch (error) { return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); }}
// Simple in-memory nonce store// In production, use Redis or a databaseclass NonceStore { private nonces = new Set<string>(); add(nonce: string): void { this.nonces.add(nonce); } consume(nonce: string): boolean { return this.nonces.delete(nonce); }}export const nonceStore = new NonceStore();