Sigma Auth
Customization

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:

  1. Client Plugin (sigma-client-plugin.ts) - Handles OAuth flow initiation
  2. Server Plugin (in auth server) - Manages Bitcoin signature verification
  3. 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-auth

Configure the auth client:

lib/auth-client.ts
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:

app/api/auth/callback/route.ts
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

components/auth-button.tsx
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

components/user-profile.tsx
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:

app/callback/page.tsx
'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

.env.local
# 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.com

The 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.

On this page