UNPKG

@safepassage/sdk

Version:

SafePassage SDK - Lightweight redirect-based age verification

371 lines (281 loc) 9.96 kB
# SafePassage SDK v3.5.2 A lightweight SDK for integrating SafePassage age verification into your website or application. ## Features - **Ultra-lightweight**: ~18KB minified - **Simple integration**: 5 lines of code to get started - **Two modes**: Same-tab redirect or new-tab popup - **TypeScript support**: Full type definitions included - **Auto-environment detection**: Works seamlessly across environments - **Secure**: HMAC-signed state parameters, automatic session management - **Compliant**: Enforces minimum age of 25 ## Changelog ### 3.5.2 - SafePassage version alignment release to match PrivateAV at `3.5.2` - No SDK API changes from `3.5.0` ### 3.5.0 - Added `language` option for UI localization (`en`, `de`, `es`, `fr`, `pt`) - Per-verification `language` override via `verify({ language: 'es' })` ### 3.4.11 - Fixed CDN documentation URLs (files are at package root, not `/dist/`) ### 3.4.9 - Prevents `onCancel` from firing after a successful new-tab verification when the popup closes. ## Installation ```bash npm install @safepassage/sdk ``` Or load directly from jsDelivr CDN (no bundler required): ```html <script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/safepassage.min.js"></script> ``` ## Quick Start ### With npm/bundler ```javascript import { SafePassage } from '@safepassage/sdk'; const sp = new SafePassage({ apiKey: 'pk_...', // Your public key from the dashboard returnUrl: window.location.origin + '/verified' }); // Start verification - redirects user to SafePassage await sp.verify(); ``` ### With CDN (no bundler) ```html <script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/safepassage.min.js"></script> <script> const sp = new SafePassage({ apiKey: 'pk_...', returnUrl: window.location.origin + '/verified' }); document.getElementById('verify-btn').onclick = () => sp.verify(); </script> ``` That's it! The SDK handles session creation automatically. ## Configuration | Option | Type | Required | Description | |--------|------|----------|-------------| | apiKey | string | Yes | Your public API key (`pk_...`) | | returnUrl | string | Yes | URL to redirect after verification | | cancelUrl | string | No | URL to redirect to if user closes the verification window (new-tab mode) | | environment | string | No | `'production'` or `'staging'` (auto-detected) | | mode | string | No | `'redirect'` (default) or `'new-tab'` | | defaultChallengeAge | number | No | Default minimum age (25 or higher) | | defaultVerificationMode | string | No | `'L1'` or `'L2'` | | onComplete | function | No | Callback for new-tab mode | | onCancel | function | No | Called when user closes popup (new-tab mode). Return `false` to suppress automatic `cancelUrl` redirect. | | language | string | No | Default UI language (`'en'`, `'de'`, `'es'`, `'fr'`, `'pt'`). Can be overridden per verification. | | onError | function | No | Error handler | ## Verification Options Override settings per-verification: ```javascript await sp.verify({ challengeAge: 30, // Override minimum age for this session verificationMode: 'L2', // Force ID verification for this session externalUserId: 'user-123', // Your user ID (returned in webhooks) skipIntro: true, // Skip intro screen autoReturn: true, // Auto-redirect after success language: 'es' // Override UI language for this session }); ``` ### Verification Modes - **L1**: Age estimation using computer vision (faster, less friction) - **L2**: Full ID document verification (more thorough) ## Integration Modes ### Same-Tab Redirect (Default) User is redirected to SafePassage, then back to your `returnUrl`: ```javascript const sp = new SafePassage({ apiKey: 'pk_...', returnUrl: '/age-verified' }); await sp.verify(); // User is redirected to SafePassage... // After verification, user returns to /age-verified?sessionId=xxx&status=verified ``` ### New-Tab Mode Verification opens in a popup window: ```javascript const sp = new SafePassage({ apiKey: 'pk_...', returnUrl: '/age-verified', cancelUrl: '/age-cancelled', mode: 'new-tab', onComplete: (result) => { console.log('Verification complete:', result.sessionId, result.status); // Validate on your server, then update UI }, onCancel: () => { console.log('User closed the verification window'); // Return false if you want to handle navigation manually // return false; }, onError: (error) => { console.error('Verification error:', error.message); } }); await sp.verify(); ``` If `cancelUrl` is provided, the SDK will redirect the opener to `cancelUrl` with `status=cancelled` and `sessionId` when the user closes the verification window. Return `false` from `onCancel` to suppress the automatic redirect. ## Server-Side Validation (Required!) After verification completes, **always validate the result on your server** before granting access: ```javascript // Node.js / Express example app.get('/age-verified', async (req, res) => { const { sessionId } = req.query; // Validate with your SECRET key (sk_...) const response = await fetch( `https://api.safepassageapp.com/api/v1/sessions/${sessionId}`, { headers: { 'Authorization': `Bearer ${process.env.SAFEPASSAGE_SECRET_KEY}` } } ); const session = await response.json(); if (session.status === 'VERIFIED') { // Grant access req.session.ageVerified = true; res.redirect('/content'); } else { res.redirect('/age-verification-failed'); } }); ``` > **Security Note**: Never trust client-side verification status alone. Always validate server-side using your secret key. ## Webhooks (Recommended) For reliable verification tracking, configure webhooks in your dashboard: ```javascript // Webhook payload example { "event": "verification.completed", "sessionId": "abc-123", "verified": true, "externalUserId": "your-user-id", // If provided during verify() "timestamp": "2025-01-15T10:30:00Z" } ``` ## Complete Example ### HTML + CDN ```html <!DOCTYPE html> <html> <head> <title>Age Verification</title> <script src="https://cdn.jsdelivr.net/npm/@safepassage/sdk@latest/safepassage.min.js"></script> </head> <body> <button id="verify-btn">Verify Your Age</button> <script> const sp = new SafePassage({ apiKey: 'pk_...', returnUrl: window.location.origin + '/verified' }); document.getElementById('verify-btn').onclick = () => sp.verify(); </script> </body> </html> ``` ### React Component ```jsx import { useState } from 'react'; import { SafePassage } from '@safepassage/sdk'; function AgeGate() { const [verifying, setVerifying] = useState(false); const sp = new SafePassage({ apiKey: process.env.NEXT_PUBLIC_SAFEPASSAGE_KEY, returnUrl: window.location.origin + '/verified' }); const handleVerify = async () => { setVerifying(true); try { await sp.verify(); } catch (error) { console.error('Failed to start verification:', error); setVerifying(false); } }; return ( <button onClick={handleVerify} disabled={verifying}> {verifying ? 'Redirecting...' : 'Verify Your Age'} </button> ); } ``` ## TypeScript Full TypeScript support included: ```typescript import { SafePassage, SafePassageConfig, VerificationResult } from '@safepassage/sdk'; const config: SafePassageConfig = { apiKey: process.env.NEXT_PUBLIC_SAFEPASSAGE_KEY!, returnUrl: '/verified', mode: 'new-tab', onComplete: (result: VerificationResult) => { console.log(`Session ${result.sessionId}: ${result.status}`); } }; const sp = new SafePassage(config); ``` ## Skip Parameters For streamlined embedded flows: ```javascript // Skip intro screen (go directly to camera) await sp.verify({ skipIntro: true }); // Auto-redirect after success (no success screen) await sp.verify({ autoReturn: true }); // Both - minimal user interaction await sp.verify({ skipIntro: true, autoReturn: true }); ``` ## Error Handling ```javascript const sp = new SafePassage({ apiKey: 'pk_...', returnUrl: '/verified', onError: (error) => { if (error.message.includes('popup')) { alert('Please allow popups for age verification'); } else if (error.message.includes('rate limit')) { alert('Too many attempts. Please wait a moment.'); } else { console.error('Verification error:', error); } } }); ``` ## API Keys SafePassage uses two types of API keys: | Key Type | Prefix | Use Case | |----------|--------|----------| | Public Key | `pk_` | Client-side SDK (this package) | | Secret Key | `sk_` | Server-side validation only | > **Important**: This SDK only works with public keys (`pk_`). For server-side integrations using secret keys, use the [Direct API](https://docs.safepassageapp.com/api) instead. ## Browser Support - Chrome 60+ - Firefox 60+ - Safari 12+ - Edge 79+ - Mobile browsers (iOS Safari, Chrome for Android) ## Security Best Practices 1. **Use public keys client-side** - Never expose secret keys in browser code 2. **Validate server-side** - Always verify results using your secret key 3. **Configure webhooks** - For reliable, tamper-proof verification notifications 4. **Register callback URLs** - Pre-register your `returnUrl` in the dashboard 5. **Use HTTPS** - SDK enforces HTTPS in production ## Cleanup When done with the SDK (e.g., in SPA route changes): ```javascript sp.destroy(); ``` ## Migration from v3.0.x If you were using merchant-generated session IDs: ```javascript // Old (v3.0.x) sp.verify({ sessionId: crypto.randomUUID() }); // New (v3.4+) - sessionId is auto-generated await sp.verify(); ``` The SDK now creates sessions automatically via the API when using public keys. ## Support - [Documentation](https://docs.safepassageapp.com) - [API Reference](https://docs.safepassageapp.com/api) - [Dashboard](https://portal.safepassageapp.com)