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
- Get Nonce: Request a unique nonce from Dial servers
- Create Message: Generate a SIWE/SIWS compliant message
- Sign Message: User signs with their wallet
- 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 viemSecurity Best Practices
- Use SIWE/SIWS - Industry-standard authentication with built-in replay protection
- Never expose your API key - Use environment variables
- Verify message content - Always show users what they’re signing
- Nonce validation - Each nonce can only be used once
- Session expiry - SIWE/SIWS messages include expiration timestamps
- Domain binding - Messages are bound to your domain (dial.wtf)
- Use HTTPS - Always use secure connections
- 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