Skip to main content

Installation

npm install @crossmint/client-sdk-auth

Overview

The Auth SDK provides a headless authentication client for building custom authentication experiences without pre-built UI components. It supports multiple authentication methods and handles token refresh automatically.

Quick Start

import { createCrossmint, CrossmintAuth } from '@crossmint/client-sdk-auth';

// Initialize Crossmint
const crossmint = createCrossmint({ apiKey: 'YOUR_API_KEY' });

// Create auth client
const auth = CrossmintAuth.from(crossmint, {
  callbacks: {
    onTokenRefresh: (authMaterial) => {
      console.log('Token refreshed:', authMaterial);
    },
    onLogout: () => {
      console.log('User logged out');
    },
  },
});

// Get current user
const user = await auth.getUser();

Configuration

CrossmintAuthClient.from()

Create an authentication client instance:
const auth = CrossmintAuth.from(crossmint, {
  callbacks: {
    onTokenRefresh: (authMaterial) => {
      // Called when JWT is refreshed
      console.log('New JWT:', authMaterial.jwt);
    },
    onLogout: () => {
      // Called when user logs out
      console.log('User logged out');
    },
  },
  logoutRoute: '/api/auth/logout', // Optional custom logout endpoint
  refreshRoute: '/api/auth/refresh', // Optional custom refresh endpoint
  storageProvider: customStorage, // Optional custom storage provider
});

Config Options

callbacks
object
Lifecycle callbacks:
  • onTokenRefresh(authMaterial) - Called when JWT is refreshed
  • onLogout() - Called when user logs out
logoutRoute
string
Custom logout endpoint URL. If not provided, uses default Crossmint logout.
refreshRoute
string
Custom token refresh endpoint URL. If not provided, uses default Crossmint refresh.
storageProvider
StorageProvider
Custom storage provider for tokens. Defaults to browser localStorage.
interface StorageProvider {
  get(key: string): Promise<string | null>;
  set(key: string, value: string, expiresAt: string): Promise<void>;
  remove(key: string): Promise<void>;
}

Authentication Methods

Email OTP

Authenticate users with email one-time passwords:
// 1. Send OTP to user's email
const { emailId } = await auth.sendEmailOtp('[email protected]');

// 2. User enters OTP from email
const otpCode = '123456'; // From user input

// 3. Confirm OTP and get one-time secret
const oneTimeSecret = await auth.confirmEmailOtp(
  '[email protected]',
  emailId,
  otpCode
);

// 4. Exchange secret for auth material
const authMaterial = await auth.handleOneTimeSecret(oneTimeSecret);
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:197-243

OAuth Providers

Authenticate with OAuth providers (Google, Twitter, Farcaster):
// 1. Get OAuth URL for provider
const oauthUrl = await auth.getOAuthUrl('google');

// 2. Redirect user to OAuth URL
window.location.href = oauthUrl;

// 3. Handle callback after OAuth completes
// The callback URL will contain a one-time secret
const urlParams = new URLSearchParams(window.location.search);
const oneTimeSecret = urlParams.get('oneTimeSecret');

// 4. Exchange secret for auth material
if (oneTimeSecret) {
  const authMaterial = await auth.handleOneTimeSecret(oneTimeSecret);
}

Supported Providers

provider
OAuthProvider
OAuth provider identifier:
  • 'google' - Google OAuth
  • 'twitter' - Twitter OAuth
  • 'farcaster' - Farcaster authentication
options
object
Optional OAuth configuration:
{
  appSchema: 'myapp://' // For mobile deep linking
}
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:158-195

Farcaster

Authenticate with Farcaster Sign In With Farcaster:
import type { UseSignInData } from '@farcaster/auth-kit';

// 1. Get Farcaster sign-in data from @farcaster/auth-kit
const farcasterData: UseSignInData = {
  message: 'Sign in with Farcaster',
  signature: '0x...',
  fid: 12345,
  signatureParams: {
    domain: 'example.com',
    // ... other params
  },
};

// 2. Sign in with Farcaster
const oneTimeSecret = await auth.signInWithFarcaster(farcasterData);

// 3. Exchange secret for auth material
const authMaterial = await auth.handleOneTimeSecret(oneTimeSecret);
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:245-273

Smart Wallet

Authenticate with EVM or Solana smart wallets:
// 1. Initiate smart wallet sign in
const { message } = await auth.signInWithSmartWallet(
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  'evm' // or 'solana'
);

// 2. Sign the message with the wallet
const signature = await wallet.signMessage(message);

// 3. Authenticate with signature
const { oneTimeSecret } = await auth.authenticateSmartWallet(
  '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  'evm',
  signature
);

// 4. Exchange secret for auth material
const authMaterial = await auth.handleOneTimeSecret(oneTimeSecret);
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:275-320

User Management

Get Current User

const user = await auth.getUser();

console.log(user);
// {
//   userId: 'usr_...',
//   email: '[email protected]',
//   linkedAccounts: [
//     { type: 'email', identifier: '[email protected]' },
//     { type: 'google', identifier: '[email protected]' },
//   ]
// }
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:65-81

Logout

await auth.logout();
// Clears stored tokens and triggers onLogout callback
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:99-120

Token Management

Store Auth Material

Manually store authentication material:
const authMaterial = {
  jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
  refreshToken: {
    secret: 'rt_...',
    expiresAt: '2024-12-31T23:59:59Z',
  },
  user: {
    userId: 'usr_...',
    email: '[email protected]',
  },
};

await auth.storeAuthMaterial(authMaterial);
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:83-97

Handle Token Refresh

Manually trigger token refresh:
const authMaterial = await auth.handleRefreshAuthMaterial();

if (authMaterial) {
  console.log('Token refreshed:', authMaterial.jwt);
} else {
  console.log('Session expired, user logged out');
}
Token refresh happens automatically before expiration. Manual refresh is rarely needed.
See implementation at /home/daytona/workspace/source/packages/client/auth/src/CrossmintAuthClient.ts:122-156

Custom Storage Provider

Implement a custom storage provider for tokens:
import type { StorageProvider } from '@crossmint/client-sdk-auth';

class CustomStorage implements StorageProvider {
  async get(key: string): Promise<string | null> {
    // Implement custom get logic
    return await AsyncStorage.getItem(key);
  }

  async set(key: string, value: string, expiresAt: string): Promise<void> {
    // Implement custom set logic
    await AsyncStorage.setItem(key, value);
    await AsyncStorage.setItem(`${key}_expires`, expiresAt);
  }

  async remove(key: string): Promise<void> {
    // Implement custom remove logic
    await AsyncStorage.removeItem(key);
    await AsyncStorage.removeItem(`${key}_expires`);
  }
}

const auth = CrossmintAuth.from(crossmint, {
  storageProvider: new CustomStorage(),
});

Server-Side Integration

Custom Refresh Endpoint

Use a custom server endpoint for token refresh:
// Client-side
const auth = CrossmintAuth.from(crossmint, {
  refreshRoute: '/api/auth/refresh',
});
// Server-side (Next.js API route example)
import { NextApiRequest, NextApiResponse } from 'next';
import { CrossmintAuth } from '@crossmint/common-sdk-auth';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const refreshToken = req.cookies.refreshToken;
  
  if (!refreshToken) {
    return res.status(401).json({ error: 'No refresh token' });
  }

  try {
    const crossmint = createCrossmint({ apiKey: process.env.CROSSMINT_API_KEY });
    const auth = CrossmintAuth.from(crossmint);
    
    const authMaterial = await auth.refreshAuthMaterial(refreshToken);
    
    // Store new tokens in cookies
    res.setHeader('Set-Cookie', [
      `jwt=${authMaterial.jwt}; HttpOnly; Secure; SameSite=Strict`,
      `refreshToken=${authMaterial.refreshToken.secret}; HttpOnly; Secure; SameSite=Strict`,
    ]);
    
    res.json({ success: true });
  } catch (error) {
    res.status(401).json({ error: 'Refresh failed' });
  }
}

Custom Logout Endpoint

// Client-side
const auth = CrossmintAuth.from(crossmint, {
  logoutRoute: '/api/auth/logout',
});
// Server-side (Next.js API route example)
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // Clear cookies
  res.setHeader('Set-Cookie', [
    'jwt=; HttpOnly; Secure; SameSite=Strict; Max-Age=0',
    'refreshToken=; HttpOnly; Secure; SameSite=Strict; Max-Age=0',
  ]);
  
  res.json({ success: true });
}

Type Definitions

import type {
  // Client types
  CrossmintAuthClient,
  CrossmintAuthClientConfig,
  
  // Auth material
  AuthMaterialWithUser,
  
  // OAuth types
  OAuthProvider,
  
  // Storage types
  StorageProvider,
  
  // Utility functions
  getJWTExpiration,
} from '@crossmint/client-sdk-auth';

Error Handling

import { CrossmintAuthenticationError } from '@crossmint/common-sdk-auth';

try {
  await auth.sendEmailOtp('invalid-email');
} catch (error) {
  if (error instanceof CrossmintAuthenticationError) {
    console.error('Auth error:', error.message);
  }
}

Best Practices

Use HttpOnly cookies for production apps to prevent XSS attacks:
const auth = CrossmintAuth.from(crossmint, {
  refreshRoute: '/api/auth/refresh',
  logoutRoute: '/api/auth/logout',
});
This delegates token storage to your server where cookies can be properly secured.
The SDK handles token refresh automatically. Listen to the callback:
const auth = CrossmintAuth.from(crossmint, {
  callbacks: {
    onTokenRefresh: (authMaterial) => {
      // Update UI or trigger re-fetch of protected data
      console.log('Token refreshed');
    },
  },
});
Handle authentication errors gracefully:
try {
  const user = await auth.getUser();
} catch (error) {
  if (error instanceof CrossmintAuthenticationError) {
    // Redirect to login
    window.location.href = '/login';
  }
}

Framework Examples

// lib/auth.ts
import { createCrossmint, CrossmintAuth } from '@crossmint/client-sdk-auth';

const crossmint = createCrossmint({ 
  apiKey: process.env.NEXT_PUBLIC_CROSSMINT_API_KEY! 
});

export const auth = CrossmintAuth.from(crossmint, {
  refreshRoute: '/api/auth/refresh',
  logoutRoute: '/api/auth/logout',
});
// pages/login.tsx
import { auth } from '@/lib/auth';

export default function LoginPage() {
  const handleLogin = async (email: string, otp: string) => {
    const { emailId } = await auth.sendEmailOtp(email);
    const secret = await auth.confirmEmailOtp(email, emailId, otp);
    await auth.handleOneTimeSecret(secret);
    router.push('/dashboard');
  };

  return <LoginForm onSubmit={handleLogin} />;
}

Next Steps

React UI SDK

Use pre-built auth components

React Native SDK

Build mobile auth flows

API Reference

Explore authentication APIs

Security Guide

Learn about security best practices

Build docs developers (and LLMs) love