Skip to content

Authentication

Simple guide to LazorKit's passkey-based wallet authentication.

Quick Start

LazorKit uses WebAuthn passkeys instead of seed phrases - more secure and user-friendly.

import { useWallet } from '@lazorkit/wallet';
 
function WalletConnect() {
  const { connect, account, isConnecting } = useWallet();
 
  return (
    <button onClick={connect} disabled={isConnecting}>
      {account ? `Connected: ${account.smartWallet.slice(0, 8)}...` : 'Connect Wallet'}
    </button>
  );
}

How It Works

Passkeys = Device biometrics (Face ID, fingerprint) + secure hardware storage

  1. First time: Create passkey → Deploy smart wallet
  2. Return visits: Authenticate → Reconnect automatically
  3. Transactions: Biometric approval → Sign & send

Browser Support: Chrome 88+, Safari 15+, Firefox 90+, Edge 88+

Connection Patterns

Standard Connection (Recommended)

const { connect, account, isConnecting, error } = useWallet();
 
const handleConnect = async () => {
  try {
    const account = await connect(); // Auto-handles new/existing users
    console.log('Connected:', account.smartWallet);
  } catch (error) {
    console.error(error.code); // 'NO_STORED_CREDENTIALS' | 'USER_CANCELLED'
  }
};

Auto-Connect on App Load

function App() {
  const { connect, isConnected } = useWallet();
 
  useEffect(() => {
    if (!isConnected) {
      connect().catch(error => {
        if (error.code !== 'NO_STORED_CREDENTIALS') {
          console.error('Auto-connect failed:', error);
        }
      });
    }
  }, []);
 
  return <YourApp />;
}

Error Handling

Simple error patterns for common scenarios:

const handleAuthError = (error) => {
  switch (error.code) {
    case 'NO_STORED_CREDENTIALS':
      return 'Welcome! Ready to create your first wallet?';
    case 'USER_CANCELLED':
      return 'Authentication cancelled. Try again when ready.';
    case 'PASSKEY_CREATION_FAILED':
      return 'Failed to create passkey. Check browser settings.';
    default:
      return 'Authentication failed. Please try again.';
  }
};
 
// Usage
try {
  await connect();
} catch (error) {
  setMessage(handleAuthError(error));
}

Advanced Patterns

Custom Onboarding

function OnboardingFlow() {
  const { createPasskeyOnly, createSmartWalletOnly } = useWallet();
  const [step, setStep] = useState<'passkey' | 'wallet' | 'complete'>('passkey');
  const [passkeyData, setPasskeyData] = useState(null);
 
  const steps = {
    passkey: async () => {
      const data = await createPasskeyOnly();
      setPasskeyData(data);
      setStep('wallet');
    },
    wallet: async () => {
      await createSmartWalletOnly(passkeyData);
      setStep('complete');
    }
  };
 
  return (
    <div>
      {step === 'passkey' && <button onClick={steps.passkey}>Create Passkey</button>}
      {step === 'wallet' && <button onClick={steps.wallet}>Deploy Wallet</button>}
      {step === 'complete' && <div>✅ Wallet Ready!</div>}
    </div>
  );
}

External Payer Pattern

function ExternalPayerTransaction() {
  const { buildSmartWalletTransaction, smartWalletPubkey } = useWallet();
 
  const sendWithExternalPayer = async () => {
    const instruction = SystemProgram.transfer({
      fromPubkey: smartWalletPubkey,
      toPubkey: new PublicKey(process.env.REACT_APP_RECIPIENT_ADDRESS!),
      lamports: 1000000, // 0.001 SOL
    });
 
    const { createSessionTx, executeSessionTx } = await buildSmartWalletTransaction(
      new PublicKey(process.env.REACT_APP_EXTERNAL_PAYER_ADDRESS!),
      instruction
    );
 
    // Send to backend for signing
    await fetch('/api/external-payer/sign', {
      method: 'POST',
      body: JSON.stringify({
        sessionTx: Buffer.from(createSessionTx.serialize()).toString('base64'),
        executeTx: Buffer.from(executeSessionTx.serialize()).toString('base64'),
      }),
    });
  };
 
  return <button onClick={sendWithExternalPayer}>Send (External Payer)</button>;
}

Security & Compatibility

What's Secure

  • Private keys: Stored in device secure hardware (never in browser)
  • Biometric auth: Required for every transaction
  • Cross-origin protection: Passkeys bound to your domain
  • No seed phrases: Nothing to lose or leak

Browser Compatibility

// Feature detection (optional)
const supportsPasskeys = !!(navigator.credentials && navigator.credentials.create);
 
function CompatibilityCheck() {
  if (!supportsPasskeys) {
    return <div>Please use Chrome 88+, Safari 15+, or Firefox 90+</div>;
  }
  return <WalletConnection />;
}

Platform Notes

  • iOS: Requires iOS 16+ for full passkey support
  • Android: Works with Android 9+ and device screen lock
  • Desktop: Hardware security keys supported
  • Private browsing: Limited functionality

Best Practices

  1. Always HTTPS - Required for WebAuthn
  2. User-initiated - Don't auto-trigger authentication
  3. Clear errors - Use the error codes to guide users
  4. Test thoroughly - Behavior varies across devices
  5. Graceful fallback - Handle unsupported browsers