Skip to Content
Dial v1 live 🎉
SdkAuthentication

Authentication

Dial uses industry-standard wallet signature authentication (SIWE/SIWS), eliminating the need for traditional usernames and passwords.

Universal vs User-Bound Client

The Dial SDK supports two modes:

Universal Client (Default)

No authentication required for public/registry features:

import { DialClient } from '@dial/sdk'; const dial = new DialClient({ apiKey: process.env.DIAL_API_KEY }); // Use public features without auth const rooms = await dial.registry.listPublicRooms(); const tokenInfo = await dial.registry.getTokenInfo('0x...');

User-Bound Client (Authenticated)

For user-specific features like calls, messages, etc:

import { SiweMessage } from 'siwe'; // Create SIWE message const siweMessage = new SiweMessage({ domain: 'dial.wtf', address: walletAddress, statement: 'Sign in to Dial', uri: 'https://dial.wtf', version: '1', chainId: 1, nonce: await dial.auth.getNonce(), // Get nonce from server }); const message = siweMessage.prepareMessage(); const signature = await wallet.signMessage(message); // Create authenticated instance const userDialer = await dial.asUser({ siwe: { message, signature } }); // Now all operations use this user's context await userDialer.calls.start({ to: '0x...', type: 'audio' });

Authentication Standards

Dial uses cryptographically-secure authentication standards:

  • SIWE (Sign-In with Ethereum, EIP-4361) for Ethereum and EVM chains
  • SIWS (Sign-In with Solana) for Solana wallets

These standards provide:

  • âś… Replay attack protection via nonces
  • âś… Session expiration
  • âś… Domain binding for security
  • âś… Human-readable signing messages
  • âś… Industry-wide interoperability

How It Works

  1. Get Nonce: Request a unique nonce from Dial servers
  2. Create Message: Generate a SIWE/SIWS compliant message
  3. Sign Message: User signs with their wallet
  4. Verify & Authenticate: Dial verifies the signature and creates a session

Authentication Flow

Step 1: Create Universal Client

import { DialClient } from '@dial/sdk'; const dial = new DialClient({ apiKey: process.env.DIAL_API_KEY });

Step 2: Get Server Nonce

// Request a unique nonce for this authentication attempt const nonce = await dial.auth.getNonce();

Step 3: Create and Sign SIWE Message

Using ethers.js:

import { ethers } from 'ethers'; import { SiweMessage } from 'siwe'; const provider = new ethers.BrowserProvider(window.ethereum); const signer = await provider.getSigner(); const address = await signer.getAddress(); // Create SIWE message const siweMessage = new SiweMessage({ domain: 'dial.wtf', address, statement: 'Sign in to Dial', uri: 'https://dial.wtf', version: '1', chainId: await provider.getNetwork().then(n => n.chainId), nonce, issuedAt: new Date().toISOString(), }); const message = siweMessage.prepareMessage(); const signature = await signer.signMessage(message);

Using wagmi (React):

import { useSignMessage, useAccount, useChainId } from 'wagmi'; import { SiweMessage } from 'siwe'; const { address } = useAccount(); const { signMessageAsync } = useSignMessage(); const chainId = useChainId(); // Get nonce const nonce = await dial.auth.getNonce(); // Create SIWE message const siweMessage = new SiweMessage({ domain: 'dial.wtf', address, statement: 'Sign in to Dial', uri: 'https://dial.wtf', version: '1', chainId, nonce, issuedAt: new Date().toISOString(), }); const message = siweMessage.prepareMessage(); const signature = await signMessageAsync({ message });

Step 4: Create User-Bound Instance

const userDialer = await dial.asUser({ siwe: { message, signature } }); console.log('Authenticated as:', userDialer.walletAddress);

Full Examples

With ethers.js (SIWE)

import { DialClient } from '@dial/sdk'; import { ethers } from 'ethers'; import { SiweMessage } from 'siwe'; async function authenticateWithDial() { // Initialize universal client const dial = new DialClient({ apiKey: process.env.DIAL_API_KEY }); // Connect wallet const provider = new ethers.BrowserProvider(window.ethereum); const signer = await provider.getSigner(); const address = await signer.getAddress(); const chainId = await provider.getNetwork().then(n => Number(n.chainId)); // Get nonce from Dial const nonce = await dial.auth.getNonce(); // Create SIWE message const siweMessage = new SiweMessage({ domain: 'dial.wtf', address, statement: 'Sign in to Dial', uri: 'https://dial.wtf', version: '1', chainId, nonce, issuedAt: new Date().toISOString(), }); // Sign message const message = siweMessage.prepareMessage(); const signature = await signer.signMessage(message); // Create user-bound client const userDialer = await dial.asUser({ siwe: { message, signature } }); return userDialer; }

With wagmi (React)

import { DialClient } from '@dial/sdk'; import { useAccount, useSignMessage, useChainId } from 'wagmi'; import { SiweMessage } from 'siwe'; import { useState } from 'react'; export function useDialAuth() { const { address } = useAccount(); const { signMessageAsync } = useSignMessage(); const chainId = useChainId(); const [userDialer, setUserDialer] = useState(null); const authenticate = async () => { if (!address) throw new Error('No wallet connected'); const dial = new DialClient({ apiKey: process.env.NEXT_PUBLIC_DIAL_API_KEY }); // Get nonce from Dial const nonce = await dial.auth.getNonce(); // Create SIWE message const siweMessage = new SiweMessage({ domain: 'dial.wtf', address, statement: 'Sign in to Dial', uri: 'https://dial.wtf', version: '1', chainId, nonce, issuedAt: new Date().toISOString(), }); // Sign message const message = siweMessage.prepareMessage(); const signature = await signMessageAsync({ message }); // Create user-bound client const userDialer = await dial.asUser({ siwe: { message, signature } }); setUserDialer(userDialer); return userDialer; }; return { userDialer, authenticate }; }

With Solana (SIWS)

import { DialClient } from '@dial/sdk'; import { useWallet } from '@solana/wallet-adapter-react'; import { SigninMessage } from '@solana/wallet-standard-util'; import bs58 from 'bs58'; export function useSolanaDialAuth() { const { publicKey, signMessage } = useWallet(); const [userDialer, setUserDialer] = useState(null); const authenticate = async () => { if (!publicKey || !signMessage) throw new Error('Wallet not connected'); const dial = new DialClient({ apiKey: process.env.NEXT_PUBLIC_DIAL_API_KEY }); // Get nonce from Dial const nonce = await dial.auth.getNonce(); // Create SIWS message const siwsMessage = new SigninMessage({ domain: 'dial.wtf', address: publicKey.toBase58(), statement: 'Sign in to Dial', uri: 'https://dial.wtf', version: '1', chainId: 'mainnet', nonce, issuedAt: new Date().toISOString(), }); // Sign message const messageText = siwsMessage.prepare(); const messageBytes = new TextEncoder().encode(messageText); const signatureBytes = await signMessage(messageBytes); const signature = bs58.encode(signatureBytes); // Create user-bound client const userDialer = await dial.asUser({ siws: { message: messageText, signature } }); setUserDialer(userDialer); return userDialer; }; return { userDialer, authenticate }; }

Helper Utilities

Simple SIWE Helper

For quick integration, Dial provides a helper that handles SIWE message creation:

import { DialClient } from '@dial/sdk'; const dial = new DialClient({ apiKey: '...' }); // Helper that creates SIWE message internally const userDialer = await dial.authenticateWithWallet({ wallet: walletProvider, // ethers signer or wagmi connector chainId: 1, });

Simple SIWS Helper

For Solana wallets:

import { DialClient } from '@dial/sdk'; const dial = new DialClient({ apiKey: '...' }); // Helper for Solana const userDialer = await dial.authenticateWithSolana({ wallet: solanaWallet, // Solana wallet adapter });

Session Management

Storing Sessions

// Export session data for later use const sessionData = userDialer.exportSession(); localStorage.setItem('dial_session', JSON.stringify(sessionData)); // Restore session const dial = new DialClient({ apiKey: '...' }); const userDialer = await dial.restoreSession( JSON.parse(localStorage.getItem('dial_session')) );

Checking Session Validity

const isValid = await userDialer.isSessionValid(); if (!isValid) { // Re-authenticate const newUserDialer = await dial.asUser({ ... }); }

Logging Out

await userDialer.logout(); localStorage.removeItem('dial_session');

Required Dependencies

Install the appropriate authentication library for your chain:

# For Ethereum/EVM chains pnpm add siwe # For Solana pnpm add @solana/wallet-standard-util bs58 # Optional: ethers or wagmi for wallet integration pnpm add ethers # or pnpm add wagmi viem

Security Best Practices

  1. Use SIWE/SIWS - Industry-standard authentication with built-in replay protection
  2. Never expose your API key - Use environment variables
  3. Verify message content - Always show users what they’re signing
  4. Nonce validation - Each nonce can only be used once
  5. Session expiry - SIWE/SIWS messages include expiration timestamps
  6. Domain binding - Messages are bound to your domain (dial.wtf)
  7. Use HTTPS - Always use secure connections
  8. Validate signatures server-side - Dial verifies all signatures on the backend

Supported Wallets

Ethereum/EVM

  • MetaMask
  • WalletConnect
  • Coinbase Wallet
  • Rainbow
  • Trust Wallet
  • Any EIP-1193 compatible wallet

Solana

  • Phantom
  • Solflare
  • Backpack
  • Any Solana wallet adapter compatible wallet

Next Steps

Last updated on