@safepassage/sdk
Version:
SafePassage SDK - Lightweight redirect-based age verification
371 lines (281 loc) • 9.96 kB
Markdown
# 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 /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)