Better Auth Foundation
Sigma Auth is built on top of Better Auth, a modern authentication framework that provides:
- Type-Safe: Full TypeScript support with type inference
- Framework Agnostic: Works with any web framework or platform
- Plugin Architecture: Extensible through custom plugins
- OAuth 2.0 Compatible: Standard OAuth flows with custom providers
Architecture Overview
Sigma Auth implements a custom Better Auth plugin called sigma that adds Bitcoin authentication support. The architecture consists of:
- Client Plugin (
sigma-client-plugin.ts) - Handles OAuth flow initiation - Server Plugin (in auth server) - Manages Bitcoin signature verification
- OAuth Bridge - Standard OAuth 2.0 authorization code flow
Authentication Flow
Here's how the Bitcoin authentication flow works with Better Auth:
Client Configuration
Install the official Sigma Auth plugin for Better Auth:
bun add @sigma-auth/better-auth-plugin better-authConfigure the auth client:
import { createAuthClient } from 'better-auth/client';
import { sigmaClient } from '@sigma-auth/better-auth-plugin/client';
export const authClient = createAuthClient({
baseURL: 'https://auth.sigmaidentity.com',
plugins: [sigmaClient()],
});
// Export signIn and other utilities
export const { signIn } = authClient;Server-Side Token Exchange (Next.js)
Create a callback route to handle the OAuth code exchange:
import { createCallbackHandler } from "@sigma-auth/better-auth-plugin/next";
export const runtime = "nodejs";
export const POST = createCallbackHandler();The callback handler:
- Exchanges the authorization code for tokens
- Signs the request with your member key
- Returns user profile with BAP identity data
Usage in Components
Sign In
import { signIn } from '@/lib/auth-client';
export function SignInButton() {
const handleSignIn = () => {
signIn.sigma({
clientId: process.env.NEXT_PUBLIC_SIGMA_CLIENT_ID!,
callbackURL: `${window.location.origin}/callback`,
});
};
return <button onClick={handleSignIn}>Sign In with Sigma</button>;
}The plugin handles PKCE automatically, storing the code verifier in sessionStorage.
Session Management
import { useSession } from '@/lib/auth-client';
export function UserProfile() {
const { data: session, isPending } = useSession();
if (isPending) return <div>Loading...</div>;
if (!session) return <div>Not signed in</div>;
return (
<div>
<p>Welcome, {session.user.profile?.name || session.user.pubkey}</p>
<p>Bitcoin Address: {session.user.address}</p>
</div>
);
}OAuth Callback Page
Handle the OAuth callback client-side:
'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import { Suspense, useEffect, useState } from 'react';
function CallbackContent() {
const router = useRouter();
const searchParams = useSearchParams();
const [error, setError] = useState('');
useEffect(() => {
const handleCallback = async () => {
const code = searchParams.get('code');
const codeVerifier = sessionStorage.getItem('sigma_code_verifier');
if (!code) {
setError('No authorization code');
return;
}
try {
const response = await fetch('/api/auth/callback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, code_verifier: codeVerifier }),
});
if (!response.ok) {
const data = await response.json();
throw new Error(data.error || 'Token exchange failed');
}
const { user, access_token } = await response.json();
// Store session (use your preferred method)
localStorage.setItem('auth_session', JSON.stringify({ user, access_token }));
sessionStorage.removeItem('sigma_code_verifier');
router.push('/dashboard');
} catch (err) {
setError(err instanceof Error ? err.message : 'Authentication failed');
}
};
handleCallback();
}, [searchParams, router]);
if (error) {
return <div>Error: {error}</div>;
}
return <div>Processing authentication...</div>;
}
export default function CallbackPage() {
return (
<Suspense fallback={<div>Loading...</div>}>
<CallbackContent />
</Suspense>
);
}Benefits of Better Auth
Type Safety
- Full TypeScript support with automatic type inference
- Type-safe plugin development
- No manual type definitions needed
Modern Architecture
- Plugin-based extensibility
- React Query integration
- Optimistic updates
Developer Experience
- Simple API with powerful features
- Excellent documentation
- Active community support
Environment Variables
# Required
NEXT_PUBLIC_SIGMA_CLIENT_ID=your-app
SIGMA_MEMBER_PRIVATE_KEY=your-member-wif
# Optional (defaults shown)
NEXT_PUBLIC_SIGMA_AUTH_URL=https://auth.sigmaidentity.comThe SIGMA_MEMBER_PRIVATE_KEY is used to sign token exchange requests. You receive this when you register your OAuth client at auth.sigmaidentity.com/account.
Learn More
For Bitcoin-specific features and implementation details, see our Bitcoin Authentication Guide.