@zaplink/web
v0.2.3
Published
Browser SDK for Zaplink WhatsApp Authentication
Maintainers
Readme
@zaplink/web
Browser SDK for Zaplink WhatsApp Authentication. Provides PKCE-secured authentication flows, session management, and WhatsApp deep link helpers for client-side applications.
Installation
npm install @zaplink/web
# or
pnpm add @zaplink/web
# or
yarn add @zaplink/webFeatures
- PKCE Flow - Secure OAuth 2.0 PKCE implementation using Web Crypto API
- Session Management - Automatic token storage and refresh
- WhatsApp Integration - Deep link helpers for seamless WhatsApp handoff
- Type-safe - Full TypeScript support
- Browser-only - Uses Web APIs, no Node.js dependencies
- Auto-refresh - Optional automatic token refresh
- Storage Options - localStorage or sessionStorage
Quick Start
import { ZaplinkWeb } from '@zaplink/web';
// Initialize the client
const zaplink = new ZaplinkWeb({
publicKey: import.meta.env.VITE_ZAPLINK_PUBLIC_KEY,
baseUrl: import.meta.env.VITE_ZAPLINK_API_URL,
version: '2025-10',
autoRefresh: true, // Enable automatic token refresh
});
// Start login flow
const { attemptId } = await zaplink.startLogin({
phone: '+5215555555555',
locale: 'es-MX',
});
// Open WhatsApp to receive OTP
zaplink.openWhatsApp({
phone: '+5215555555555',
});
// After user receives OTP, verify it
const session = await zaplink.verifyOtp({
attemptId,
otp: '123456',
});
console.log('Logged in!', session.accessToken);
// Check authentication status
if (zaplink.isAuthenticated()) {
console.log('User is logged in');
}
// Get current access token
const token = zaplink.getAccessToken();
// Refresh session
await zaplink.refreshSession();
// Logout
await zaplink.revokeSession();Complete Authentication Flow
import { ZaplinkWeb } from '@zaplink/web';
// 1. Initialize client
const zaplink = new ZaplinkWeb({
publicKey: 'pk_xxx',
autoRefresh: true,
});
// 2. Start login
async function login(phoneNumber: string) {
try {
// Start the login flow
const { attemptId } = await zaplink.startLogin({
phone: phoneNumber,
});
// Open WhatsApp so user can receive OTP
zaplink.openWhatsApp({ phone: phoneNumber });
// Store attemptId for OTP verification
return attemptId;
} catch (error) {
console.error('Login failed:', error);
throw error;
}
}
// 3. Verify OTP
async function verifyOTP(attemptId: string, otp: string) {
try {
const session = await zaplink.verifyOtp({
attemptId,
otp,
});
console.log('Login successful!');
return session;
} catch (error) {
console.error('OTP verification failed:', error);
throw error;
}
}
// 4. Check if user is logged in
function isLoggedIn(): boolean {
return zaplink.isAuthenticated();
}
// 5. Logout
async function logout() {
await zaplink.revokeSession();
console.log('Logged out');
}
// Example usage
const attemptId = await login('+5215555555555');
// User receives OTP via WhatsApp
const session = await verifyOTP(attemptId, '123456');Session Management
Automatic Session Storage
Sessions are automatically saved to browser storage (localStorage by default):
const zaplink = new ZaplinkWeb({
publicKey: 'pk_xxx',
storage: {
prefix: 'myapp', // Custom storage prefix
useSessionStorage: false, // Use localStorage (default)
},
});
// Session is automatically saved after verifyOtp
await zaplink.verifyOtp({ attemptId, otp });
// Session is automatically loaded on page refresh
if (zaplink.isAuthenticated()) {
console.log('User is still logged in');
}Manual Session Access
// Get current session
const session = zaplink.getSession();
if (session) {
console.log('Access token:', session.accessToken);
console.log('Expires at:', new Date(session.expiresAt));
}
// Get access token only
const token = zaplink.getAccessToken();
// Access storage directly
const storage = zaplink.getStorage();
storage.clearAll(); // Clear all dataAutomatic Token Refresh
const zaplink = new ZaplinkWeb({
publicKey: 'pk_xxx',
autoRefresh: true, // Enable automatic refresh
});
// Token will be automatically refreshed when it's about to expire
// (less than 5 minutes remaining)Manual Token Refresh
// Manually refresh the session
try {
const newSession = await zaplink.refreshSession();
console.log('Token refreshed:', newSession.accessToken);
} catch (error) {
console.error('Refresh failed:', error);
// Session expired, need to login again
}WhatsApp Integration
Opening WhatsApp
// Simple deep link (auto-detects platform)
zaplink.openWhatsApp({
phone: '+5215555555555',
});
// With platform specification
zaplink.openWhatsApp({
phone: '+5215555555555',
platform: 'web', // 'web' | 'mobile' | 'ios' | 'android'
});
// Using utility functions directly
import { openWhatsApp, createWhatsAppDeepLink, detectPlatform } from '@zaplink/web';
// Detect platform
const platform = detectPlatform();
console.log('Platform:', platform); // 'web' | 'mobile' | 'ios' | 'android'
// Create deep link
const link = createWhatsAppDeepLink({
phone: '+5215555555555',
platform: 'web',
});
// Open WhatsApp
openWhatsApp({ phone: '+5215555555555' });Storage Options
Using sessionStorage
const zaplink = new ZaplinkWeb({
publicKey: 'pk_xxx',
storage: {
useSessionStorage: true, // Session cleared when tab closes
},
});Custom Storage Prefix
const zaplink = new ZaplinkWeb({
publicKey: 'pk_xxx',
storage: {
prefix: 'myapp', // Keys will be: myapp:session, myapp:pkce, etc.
},
});Custom Storage Implementation
import { createClientStorage } from '@zaplink/web';
const customStorage = createClientStorage({
prefix: 'custom',
useSessionStorage: false,
});
const zaplink = new ZaplinkWeb({
publicKey: 'pk_xxx',
customStorage,
});Error Handling
import { ZaplinkWeb, type Problem } from '@zaplink/web';
import { isHttpError } from '@zaplink/core';
try {
await zaplink.verifyOtp({ attemptId, otp: '000000' });
} catch (error) {
if (isHttpError(error)) {
// Handle API errors
console.error(`Error ${error.code}:`, error.message);
switch (error.code) {
case 'otp_invalid':
console.error('Invalid OTP code');
break;
case 'otp_expired':
console.error('OTP has expired');
break;
case 'rate_limit_exceeded':
console.error('Too many attempts');
break;
default:
console.error('Unknown error:', error.detail);
}
} else {
// Network or other errors
console.error('Unexpected error:', error);
}
}PKCE Utilities
The SDK uses PKCE (Proof Key for Code Exchange) for secure authentication:
import { generatePKCE, createPKCEParams } from '@zaplink/web';
// Generate PKCE parameters
const pkce = await generatePKCE();
console.log('Verifier:', pkce.verifier);
console.log('Challenge:', pkce.challenge);
// Generate complete PKCE params including state
const params = await createPKCEParams();
console.log('Verifier:', params.verifier);
console.log('Challenge:', params.challenge);
console.log('State:', params.state);Phone Utilities
import { normalizePhone, isValidE164 } from '@zaplink/web';
// Validate phone numbers
if (isValidE164('+5215555555555')) {
console.log('Valid E.164 format');
}
// Normalize phone numbers
const normalized = normalizePhone('(521) 555-555-5555');
console.log(normalized); // '+5215555555555'React Integration
For React applications, use @zaplink/react which provides hooks and components built on top of this SDK.
npm install @zaplink/reactConfiguration
interface WebClientOptions {
// Required: Public key for browser authentication
publicKey: string;
// Optional: API base URL (default: https://api.zaplink.so)
baseUrl?: string;
// Optional: API version header
version?: string;
// Optional: Storage configuration
storage?: {
prefix?: string;
useSessionStorage?: boolean;
};
// Optional: Enable automatic token refresh
autoRefresh?: boolean;
// Optional: Custom storage implementation
customStorage?: ClientStorage;
// Optional: Additional headers
headers?: Record<string, string>;
// Optional: Request timeout (default: 30000ms)
timeout?: number;
// Optional: Enable debug logging
debug?: boolean;
}Browser Support
- Modern browsers with ES2020 support
- Web Crypto API (for PKCE)
- Fetch API
- localStorage or sessionStorage
Security Notes
- Never use API secret keys in the browser - Use
publicKeyonly - PKCE is used to secure the authentication flow
- Tokens are stored in browser storage - consider the security implications
- Use HTTPS in production
- Implement proper CORS configuration on your backend
License
MIT
