@sparkvault/sdk
v1.1.6
Published
SparkVault JavaScript SDK - Identity, Sparks, Vaults, and RNG
Maintainers
Readme
SparkVault JavaScript SDK
The official SparkVault JavaScript SDK for browser applications. Add passwordless authentication to your app in minutes with zero-config integration or full programmatic control.
Table of Contents
- Installation
- Zero-Config Integration
- Manual Initialization
- Identity Verification
- Backend Token Verification
- React Integration
- Error Handling
- TypeScript Support
- Browser Support
- Development
- License
Installation
CDN (Recommended)
<script src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"></script>npm / yarn
npm install @sparkvault/sdk
# or
yarn add @sparkvault/sdkBundle Formats:
sparkvault.js— UMD bundle for browsers (recommended)sparkvault.esm.js— ES modules for modern bundlerssparkvault.cjs.js— CommonJS for Node.js
Zero-Config Integration
Add SparkVault authentication to your site with a single script tag — no JavaScript required!
<!-- Add this single script tag -->
<script
async
src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"
data-account-id="acc_YOUR_ACCOUNT_ID"
data-attach-selector=".login-btn"
data-success-url="/auth/verify-token"
></script>
<!-- Any element matching the selector becomes a login button -->
<button class="login-btn">Sign In</button>
<a href="#" class="login-btn">Create Account</a>That's it! The SDK automatically:
- Initializes with your account ID
- Attaches click handlers to all matching elements
- Opens the verification modal when clicked
- POSTs the result to your success URL
- Watches for dynamically added elements (MutationObserver)
Data Attributes
| Attribute | Required | Description |
|-----------|----------|-------------|
| data-account-id | Yes | Your SparkVault account ID (required for auto-init) |
| data-attach-selector | No | CSS selector for elements to attach click handlers |
| data-success-url | No | URL to POST { token, identity, identityType } on success |
| data-success-function | No | Global function name to call on success (e.g., "MyApp.auth.onSuccess") |
| data-error-url | No | URL to redirect on error (appends ?error=message) |
| data-error-function | No | Global function name to call on error |
| data-preload-config | No | Set to "false" to defer config loading. Default: true |
| data-timeout | No | Request timeout in milliseconds. Default: 30000 |
| data-debug | No | Set to "true" for verbose console logging |
Complete Example
<script
async
src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"
data-account-id="acc_YOUR_ACCOUNT_ID"
data-attach-selector=".js-sparkvault-auth"
data-success-url="https://example.com/auth/verify-token"
data-success-function="onSparkVaultSuccess"
data-error-function="onSparkVaultError"
data-debug="true"
></script>
<script>
// Optional: Define callback functions
function onSparkVaultSuccess(result) {
console.log('Verified:', result.identity);
console.log('Token:', result.token);
// The SDK will also POST to data-success-url
}
function onSparkVaultError(error) {
console.error('Error:', error.message);
alert('Authentication failed. Please try again.');
}
</script>
<!-- Multiple elements can use the same selector -->
<button class="js-sparkvault-auth">Sign In</button>
<a href="#" class="js-sparkvault-auth">Login with SparkVault</a>Success URL Response
When you specify data-success-url, the SDK POSTs the verification result:
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"identity": "[email protected]",
"identityType": "email"
}Your backend can respond with redirect instructions:
// Option 1: Redirect to a URL
{ "redirectUrl": "/dashboard" }
// or
{ "redirect_url": "/dashboard" }
// Option 2: Reload the current page
{ "reload": true }
// Option 3: Return data (no automatic action)
{ "success": true, "user": { "id": 123, "email": "[email protected]" } }Manual Trigger from JavaScript
You can also trigger authentication programmatically using the SparkVault global:
// Trigger authentication from anywhere in your code
SparkVault.identity.pop();
// This uses your configured success/error handlers<button onclick="SparkVault.identity.pop()">
Sign In with SparkVault
</button>Manual Initialization
For full control over the SDK, initialize it manually in JavaScript:
import SparkVault from '@sparkvault/sdk';
const sparkvault = SparkVault.init({
accountId: 'acc_YOUR_ACCOUNT_ID' // Required
});
// The SDK is now ready to use
const result = await sparkvault.identity.pop();Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| accountId | string | required | Your SparkVault account ID (starts with acc_) |
| timeout | number | 30000 | HTTP request timeout in milliseconds |
| preloadConfig | boolean | true | Preload Identity config for instant modal |
CDN Usage
<script src="https://cdn.sparkvault.com/sdk/v1/sparkvault.js"></script>
<script>
const sparkvault = SparkVault.init({
accountId: 'acc_YOUR_ACCOUNT_ID'
});
document.getElementById('login-btn').addEventListener('click', async () => {
const result = await sparkvault.identity.pop();
console.log('Verified:', result.identity);
});
</script>Identity Verification
The Identity module provides passwordless authentication through a beautiful, customizable modal. Users can verify via passkeys, email codes, SMS codes, magic links, or social providers.
Basic Usage
// Open the verification modal
const result = await sparkvault.identity.pop();
// User verified!
console.log(result.identity); // "[email protected]" or "+14155551234"
console.log(result.identityType); // "email" or "phone"
console.log(result.token); // Signed JWT token
// Send the token to your backend for verification
await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: result.token })
});pop() Options
| Option | Type | Description |
|--------|------|-------------|
| email | string | Pre-fill email address (mutually exclusive with phone) |
| phone | string | Pre-fill phone in E.164 format, e.g. "+14155551234" |
| backdropBlur | boolean | Override backdrop blur setting from app config |
| onCancel | function | Callback when user closes modal without verifying |
| onSuccess | function | Callback on success (before promise resolves) |
| onError | function | Callback on error (before promise rejects) |
pop() Result
| Field | Type | Description |
|-------|------|-------------|
| token | string | Signed JWT token (Ed25519). Verify this on your backend. |
| identity | string | The verified email address or phone number |
| identityType | "email" \| "phone" | Type of identity that was verified |
Example with Options
try {
const result = await sparkvault.identity.pop({
// Pre-fill the email field
email: '[email protected]',
// Handle cancellation
onCancel: () => {
console.log('User cancelled');
}
});
// Success! Send token to your backend
await loginWithToken(result.token);
} catch (error) {
if (error.code === 'user_cancelled') {
// User closed the modal
} else {
// Handle other errors
console.error('Verification failed:', error.message);
}
}Authentication Methods
Authentication methods are configured in your Identity App settings. The SDK automatically shows only the methods you have enabled.
| Method | Description | Identity Type |
|--------|-------------|---------------|
| passkey | WebAuthn/FIDO2 biometric or security key | Email |
| totp_email | 6-digit code sent via email | Email |
| totp_sms | 6-digit code sent via SMS | Phone |
| totp_voice | 6-digit code sent via voice call | Phone |
| magic_link / sparklink | One-click magic link via email | Email |
| google | Sign in with Google | Email |
| apple | Sign in with Apple | Email |
| microsoft | Sign in with Microsoft | Email |
| github | Sign in with GitHub | Email |
| facebook | Sign in with Facebook | Email |
| linkedin | Sign in with LinkedIn | Email |
Backend Token Verification
Security Critical: Always verify tokens on your backend. Never trust the frontend result alone. Tokens are signed with Ed25519 — use the JWKS endpoint to verify the signature.
Node.js
import * as jose from 'jose';
const ACCOUNT_ID = 'acc_YOUR_ACCOUNT_ID';
const IDENTITY_URL = 'https://api.sparkvault.com/v1/apps/identity';
app.post('/api/auth/login', async (req, res) => {
const { token } = req.body;
try {
// Fetch public keys and verify signature
const jwksUrl = `${IDENTITY_URL}/${ACCOUNT_ID}/.well-known/jwks.json`;
const JWKS = jose.createRemoteJWKSet(new URL(jwksUrl));
const { payload } = await jose.jwtVerify(token, JWKS, {
issuer: `${IDENTITY_URL}/${ACCOUNT_ID}`,
audience: ACCOUNT_ID
});
// Token is valid! Create user session
const user = await findOrCreateUser(payload.identity, payload.identity_type);
req.session.userId = user.id;
res.json({ success: true, user });
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
});Python
import jwt
from jwt import PyJWKClient
ACCOUNT_ID = 'acc_YOUR_ACCOUNT_ID'
IDENTITY_URL = 'https://api.sparkvault.com/v1/apps/identity'
def verify_identity_token(token: str):
"""Verify a SparkVault Identity token and return the claims."""
jwks_url = f"{IDENTITY_URL}/{ACCOUNT_ID}/.well-known/jwks.json"
jwks_client = PyJWKClient(jwks_url)
# Get the signing key
signing_key = jwks_client.get_signing_key_from_jwt(token)
# Verify and decode
claims = jwt.decode(
token,
signing_key.key,
algorithms=["EdDSA"],
issuer=f"{IDENTITY_URL}/{ACCOUNT_ID}",
audience=ACCOUNT_ID
)
return claims
# Usage in Flask
@app.route('/api/auth/login', methods=['POST'])
def login():
token = request.json.get('token')
try:
claims = verify_identity_token(token)
# Create session
user = find_or_create_user(claims['identity'], claims['identity_type'])
session['user_id'] = user.id
return jsonify({'success': True, 'user': user.to_dict()})
except jwt.InvalidTokenError as e:
return jsonify({'error': 'Invalid token'}), 401Go
package main
import (
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
)
const (
AccountID = "acc_YOUR_ACCOUNT_ID"
IdentityURL = "https://api.sparkvault.com/v1/apps/identity"
)
func verifyToken(tokenString string) (jwt.Token, error) {
jwksURL := fmt.Sprintf("%s/%s/.well-known/jwks.json", IdentityURL, AccountID)
// Fetch JWKS
keySet, err := jwk.Fetch(context.Background(), jwksURL)
if err != nil {
return nil, err
}
// Parse and verify token
token, err := jwt.Parse(
[]byte(tokenString),
jwt.WithKeySet(keySet),
jwt.WithIssuer(fmt.Sprintf("%s/%s", IdentityURL, AccountID)),
jwt.WithAudience(AccountID),
)
if err != nil {
return nil, err
}
return token, nil
}
// Usage in HTTP handler
func loginHandler(w http.ResponseWriter, r *http.Request) {
var body struct {
Token string `json:"token"`
}
json.NewDecoder(r.Body).Decode(&body)
token, err := verifyToken(body.Token)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
identity, _ := token.Get("identity")
// Create session with identity...
}Ruby
require 'jwt'
require 'net/http'
require 'json'
ACCOUNT_ID = 'acc_YOUR_ACCOUNT_ID'
IDENTITY_URL = 'https://api.sparkvault.com/v1/apps/identity'
def verify_identity_token(token)
jwks_url = "#{IDENTITY_URL}/#{ACCOUNT_ID}/.well-known/jwks.json"
# Fetch JWKS
uri = URI(jwks_url)
response = Net::HTTP.get(uri)
jwks = JSON.parse(response)
# Decode and verify
JWT.decode(
token,
nil,
true,
{
algorithms: ['EdDSA'],
iss: "#{IDENTITY_URL}/#{ACCOUNT_ID}",
aud: ACCOUNT_ID,
verify_iss: true,
verify_aud: true,
jwks: jwks
}
).first
end
# Usage in Rails controller
class AuthController < ApplicationController
def login
claims = verify_identity_token(params[:token])
user = User.find_or_create_by(email: claims['identity'])
session[:user_id] = user.id
render json: { success: true, user: user }
rescue JWT::DecodeError => e
render json: { error: 'Invalid token' }, status: :unauthorized
end
endToken Claims
When decoded, the JWT token contains these claims:
{
"iss": "https://api.sparkvault.com/v1/apps/identity/acc_xxx",
"sub": "identity:a1b2c3d4e5f67890",
"aud": "acc_xxx",
"identity": "[email protected]",
"identity_type": "email",
"method": "totp_email",
"verified_at": 1703977195,
"iat": 1703977200,
"exp": 1703980800,
"jti": "tok_unique_id"
}| Claim | Type | Description |
|-------|------|-------------|
| iss | string | Issuer URL — always verify this matches expected value |
| sub | string | Subject — hashed identity for consistent user identification |
| aud | string | Audience — your account ID |
| identity | string | The verified email address or phone number |
| identity_type | string | "email" or "phone" |
| method | string | Authentication method used (e.g., "totp_email", "passkey") |
| verified_at | number | Unix timestamp when identity was verified |
| iat | number | Issued at timestamp |
| exp | number | Expiration timestamp |
| jti | string | Unique token ID for replay protection |
React Integration
import { useState, useCallback } from 'react';
import SparkVault from '@sparkvault/sdk';
// Initialize once, outside component
const sparkvault = SparkVault.init({
accountId: 'acc_YOUR_ACCOUNT_ID'
});
function useSparkVaultAuth() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const login = useCallback(async (options = {}) => {
setLoading(true);
setError(null);
try {
// Open verification modal
const result = await sparkvault.identity.pop(options);
// Send token to backend
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: result.token })
});
if (!response.ok) throw new Error('Login failed');
const data = await response.json();
setUser(data.user);
return data.user;
} catch (err) {
if (err.code !== 'user_cancelled') {
setError(err.message);
}
throw err;
} finally {
setLoading(false);
}
}, []);
const logout = useCallback(async () => {
await fetch('/api/auth/logout', { method: 'POST' });
setUser(null);
}, []);
return { user, loading, error, login, logout };
}
// Usage in component
function LoginPage() {
const { user, loading, error, login, logout } = useSparkVaultAuth();
if (user) {
return (
<div>
<p>Welcome, {user.email}!</p>
<button onClick={logout}>Sign out</button>
</div>
);
}
return (
<div>
<button onClick={() => login()} disabled={loading}>
{loading ? 'Verifying...' : 'Sign in'}
</button>
{error && <p className="error">{error}</p>}
</div>
);
}Error Handling
The SDK throws typed errors that you can catch and handle appropriately.
import SparkVault, {
UserCancelledError,
ValidationError,
NetworkError,
TimeoutError
} from '@sparkvault/sdk';
try {
const result = await sparkvault.identity.pop();
} catch (error) {
switch (error.code) {
case 'user_cancelled':
// User closed the modal — not necessarily an error
console.log('User cancelled verification');
break;
case 'validation_error':
// Invalid input (e.g., malformed email)
console.error('Validation error:', error.message);
break;
case 'network_error':
// Network connectivity issue
console.error('Network error — please check your connection');
break;
case 'timeout_error':
// Request timed out
console.error('Request timed out — please try again');
break;
case 'popup_blocked':
// Browser blocked the popup
console.error('Please allow popups for this site');
break;
default:
console.error('Unexpected error:', error.message);
}
}Error Types
| Error Class | Code | Description |
|-------------|------|-------------|
| UserCancelledError | user_cancelled | User closed the modal without completing verification |
| ValidationError | validation_error | Invalid input or configuration |
| NetworkError | network_error | Network connectivity failure |
| TimeoutError | timeout_error | Request exceeded timeout limit |
| PopupBlockedError | popup_blocked | Browser blocked the popup window |
| AuthenticationError | authentication_error | Authentication failed (e.g., expired token) |
TypeScript Support
The SDK is written in TypeScript and includes full type definitions.
import SparkVault, {
// Configuration
SparkVaultConfig,
// Identity types
VerifyOptions,
VerifyResult,
TokenClaims,
// Error types
SparkVaultError,
UserCancelledError,
ValidationError,
NetworkError,
TimeoutError,
PopupBlockedError,
} from '@sparkvault/sdk';
// Fully typed configuration
const config: SparkVaultConfig = {
accountId: 'acc_YOUR_ACCOUNT_ID',
timeout: 30000,
};
const sparkvault = SparkVault.init(config);
// Fully typed options and result
const options: VerifyOptions = {
email: '[email protected]',
onCancel: () => console.log('Cancelled'),
};
const result: VerifyResult = await sparkvault.identity.pop(options);
// result.token: string
// result.identity: string
// result.identityType: 'email' | 'phone'Best Practices
Initialize Once — Create the SparkVault instance once when your app loads, not on every login attempt. The SDK preloads configuration for instant modal opening.
Always Verify Server-Side — Never trust client-side token validation in production. Always verify the JWT signature on your backend using the JWKS endpoint before creating a session.
Handle Cancellation Gracefully — Users may close the modal without completing verification. This throws a
UserCancelledError— handle it gracefully rather than showing an error message.Cache JWKS Keys — The JWKS endpoint returns public keys with caching headers. Use a library like
josethat handles caching automatically, or cache keys for 5-10 minutes.
Browser Support
The SDK supports all modern browsers:
- Chrome 80+
- Firefox 75+
- Safari 13.1+
- Edge 80+
Note: Passkey authentication requires WebAuthn support. On unsupported browsers, passkey will not appear as an option — other methods will still work.
Development
Setup
git clone https://github.com/Spark-Vault/sdk-js.git
cd sdk-js
npm installScripts
npm run build # Build all bundles
npm run build:watch # Build with watch mode
npm run typecheck # Run TypeScript type checking
npm run lint # Run ESLint
npm run test # Run tests
npm run test:watch # Run tests in watch modeProject Structure
sdk-js/
├── src/
│ ├── index.ts # Main entry point
│ ├── config.ts # Configuration management
│ ├── http.ts # HTTP client
│ ├── errors.ts # Error types
│ └── identity/ # Identity module
├── dist/ # Built bundles
├── tests/ # Test files
├── .github/workflows/ # CI/CD workflows
├── rollup.config.js # Rollup bundler config
├── tsconfig.json # TypeScript config
└── package.jsonLicense
MIT License - see LICENSE for details.
