@sp-uvb/client
v0.1.0
Published
Universal Verification Broker (UVB) TypeScript/JavaScript SDK
Maintainers
Readme
@sp-uvb/client
Official TypeScript/JavaScript SDK for Universal Verification Broker (UVB).
Features:
- ✅ Full TypeScript support with complete type definitions
- ✅ Browser & Node.js compatible (ES modules + CommonJS)
- ✅ Multiple storage options - Memory, localStorage, sessionStorage, cookies
- ✅ Comprehensive error handling - Typed errors with detailed messages
- ✅ Zero dependencies - Lightweight and fast
- ✅ Tree-shakeable - Only bundle what you use
- ✅ 100% test coverage - Reliable and production-ready
Installation
# npm
npm install @sp-uvb/client
# yarn
yarn add @sp-uvb/client
# pnpm
pnpm add @sp-uvb/clientQuick Start
Basic Usage
import { UvbClient } from '@sp-uvb/client';
// Create client
const client = new UvbClient({
baseUrl: 'https://uvb.example.com',
});
// Start verification
const { transaction_id, challenges } = await client.startVerification(
{
user_id: 'user_123',
tenant_id: 'tenant_a',
email: '[email protected]',
},
{
tenant_id: 'tenant_a',
application_id: 'app_1',
intent: 'login',
}
);
// Continue with TOTP
const response = await client.continueVerification({
transaction_id,
challenge_id: challenges[0].challenge_id,
factor_id: 'totp',
response_payload: { otp: '123456' },
});
if (response.status === 'Succeeded' && response.assertion) {
// Store JWT token
await client.setToken(response.assertion);
console.log('Authentication successful!');
}With Token Storage
import { UvbClient, LocalStorageTokenStorage } from '@sp-uvb/client';
const client = new UvbClient({
baseUrl: 'https://uvb.example.com',
tokenStorage: new LocalStorageTokenStorage(),
});
// After successful authentication, token is automatically stored
// and persists across browser sessionsAPI Reference
UvbClient
Constructor
new UvbClient(config: UvbClientConfig)Options:
baseUrl(required): Base URL of UVB APItimeout(optional): Request timeout in milliseconds (default: 30000)headers(optional): Custom headers to include in all requestsfetch(optional): Custom fetch implementationtokenStorage(optional): Token storage implementation (default: MemoryTokenStorage)
Example:
const client = new UvbClient({
baseUrl: 'https://uvb.example.com',
timeout: 10000,
headers: {
'X-Custom-Header': 'value',
},
});Methods
startVerification(subject, context)
Start a verification transaction.
const result = await client.startVerification(
{
user_id: 'user_123',
tenant_id: 'tenant_a',
email: '[email protected]',
},
{
tenant_id: 'tenant_a',
application_id: 'app_1',
intent: 'login',
ip_address: '192.168.1.1',
}
);
console.log('Transaction ID:', result.transaction_id);
console.log('Available challenges:', result.challenges);continueVerification(request)
Continue a verification with a factor response.
const result = await client.continueVerification({
transaction_id: 'txn_123',
challenge_id: 'chal_456',
factor_id: 'totp',
response_payload: { otp: '123456' },
});
if (result.status === 'Succeeded') {
console.log('JWT token:', result.assertion);
}enrollFactor(request)
Enroll a user in an authentication factor.
const result = await client.enrollFactor({
subject: {
user_id: 'user_123',
tenant_id: 'tenant_a',
},
factor_id: 'totp',
});
console.log('TOTP secret:', result.enrollment_data.secret);
console.log('QR code:', result.enrollment_data.qr_code_url);listEnrollments(request)
List all enrollments for a user.
const { enrollments } = await client.listEnrollments({
user_id: 'user_123',
tenant_id: 'tenant_a',
});
enrollments.forEach((enrollment) => {
console.log('Factor:', enrollment.factor_id);
console.log('Status:', enrollment.status);
});deleteEnrollment(request)
Delete an enrollment.
await client.deleteEnrollment({
enrollment_id: 'enr_123',
user_id: 'user_123',
tenant_id: 'tenant_a',
});introspectToken(request)
Verify and extract claims from a JWT token.
const result = await client.introspectToken({ token: jwtToken });
if (result.active) {
console.log('User:', result.sub);
console.log('Factors:', result.factors_used);
console.log('Assurance level:', result.assurance_level);
}Token Management
// Store token
await client.setToken('eyJhbGc...');
// Get token
const token = await client.getToken();
// Remove token
await client.removeToken();
// Check if token exists
const hasToken = await client.hasToken();
// Verify current token
const claims = await client.verifyCurrentToken();
if (claims && claims.active) {
console.log('Token is valid');
}Token Storage
Available Storage Implementations
MemoryTokenStorage (Default)
Stores tokens in memory (not persistent).
import { MemoryTokenStorage } from '@sp-uvb/client';
const storage = new MemoryTokenStorage();Use when:
- Testing
- Server-side applications
- Single-page apps that don't need persistence
LocalStorageTokenStorage
Stores tokens in browser localStorage (persistent across sessions).
import { LocalStorageTokenStorage } from '@sp-uvb/client';
const storage = new LocalStorageTokenStorage('my_token_key');Use when:
- Browser apps needing persistence
- Simple token storage requirements
Warning: Accessible by JavaScript, so only use for non-sensitive tokens.
SessionStorageTokenStorage
Stores tokens in browser sessionStorage (cleared when tab closes).
import { SessionStorageTokenStorage } from '@sp-uvb/client';
const storage = new SessionStorageTokenStorage();Use when:
- Browser apps
- Tokens should not persist across sessions
CookieTokenStorage
Stores tokens in cookies.
import { CookieTokenStorage } from '@sp-uvb/client';
const storage = new CookieTokenStorage({
cookieName: 'uvb_token',
secure: true,
sameSite: 'strict',
maxAge: 3600,
});Use when:
- Need cookie-based authentication
- Working with server-side rendering
Custom Storage
Implement your own storage:
import type { TokenStorage } from '@sp-uvb/client';
class CustomStorage implements TokenStorage {
async setToken(token: string): Promise<void> {
// Your implementation
}
async getToken(): Promise<string | null> {
// Your implementation
}
async removeToken(): Promise<void> {
// Your implementation
}
}
const client = new UvbClient({
baseUrl: 'https://uvb.example.com',
tokenStorage: new CustomStorage(),
});Error Handling
The SDK provides typed error classes for better error handling:
import {
NetworkError,
ApiError,
RateLimitError,
AuthenticationError,
ValidationError,
} from '@sp-uvb/client';
try {
await client.startVerification(subject, context);
} catch (error) {
if (error instanceof RateLimitError) {
console.log('Rate limited. Retry after:', error.retryAfter);
} else if (error instanceof AuthenticationError) {
console.log('Authentication failed');
} else if (error instanceof NetworkError) {
console.log('Network error:', error.message);
} else if (error instanceof ApiError) {
console.log('API error:', error.statusCode, error.errorCode);
}
}Error Types
UvbError- Base error classNetworkError- Connection failures, timeoutsApiError- HTTP errors (4xx, 5xx)AuthenticationError- 401 errorsRateLimitError- 429 errors with retry-afterValidationError- Invalid inputConfigurationError- Invalid client configuration
Examples
Complete Authentication Flow
import { UvbClient, LocalStorageTokenStorage } from '@sp-uvb/client';
const client = new UvbClient({
baseUrl: 'https://uvb.example.com',
tokenStorage: new LocalStorageTokenStorage(),
});
async function login(userId: string, tenantId: string) {
try {
// 1. Start verification
const { transaction_id, challenges } = await client.startVerification(
{ user_id: userId, tenant_id: tenantId },
{
tenant_id: tenantId,
application_id: 'my_app',
intent: 'login',
}
);
// 2. Show available factors to user
console.log(
'Available factors:',
challenges.map((c) => c.display_name)
);
// 3. User selects TOTP and enters code
const otp = await promptUserForOTP(); // Your UI logic
// 4. Continue verification
const result = await client.continueVerification({
transaction_id,
challenge_id: challenges[0].challenge_id,
factor_id: 'totp',
response_payload: { otp },
});
// 5. Handle result
if (result.status === 'Succeeded' && result.assertion) {
await client.setToken(result.assertion);
console.log('Login successful!');
return true;
} else {
console.log('Login failed:', result.last_factor_result?.error_message);
return false;
}
} catch (error) {
console.error('Login error:', error);
return false;
}
}TOTP Enrollment
async function enrollTOTP(userId: string, tenantId: string) {
const { enrollment_data } = await client.enrollFactor({
subject: { user_id: userId, tenant_id: tenantId },
factor_id: 'totp',
});
// Display QR code to user
showQRCode(enrollment_data.qr_code_url);
// Or show secret for manual entry
console.log('Secret:', enrollment_data.secret);
return enrollment_data;
}Protected API Requests
async function makeAuthenticatedRequest() {
// Check if token is valid
const claims = await client.verifyCurrentToken();
if (!claims || !claims.active) {
console.log('Not authenticated, redirecting to login...');
return;
}
// Token is valid, proceed with request
const token = await client.getToken();
const response = await fetch('https://api.example.com/protected', {
headers: {
Authorization: `Bearer ${token}`,
},
});
return response.json();
}React Hook Example
import { useState, useEffect } from 'react';
import { UvbClient } from '@sp-uvb/client';
function useAuth(client: UvbClient) {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
checkAuth();
}, []);
const checkAuth = async () => {
const claims = await client.verifyCurrentToken();
setIsAuthenticated(claims?.active ?? false);
setIsLoading(false);
};
const login = async (userId: string, tenantId: string, otp: string) => {
// ... login logic
await checkAuth();
};
const logout = async () => {
await client.removeToken();
setIsAuthenticated(false);
};
return { isAuthenticated, isLoading, login, logout };
}TypeScript Support
The SDK is written in TypeScript and provides full type definitions:
import type {
Subject,
Context,
Challenge,
VerificationStatus,
StartVerificationResponse,
ContinueVerificationResponse,
EnrollFactorRequest,
IntrospectTokenResponse,
} from '@sp-uvb/client';
// All types are exported and fully documentedBrowser Support
- ✅ Chrome/Edge (latest)
- ✅ Firefox (latest)
- ✅ Safari (latest)
- ✅ Node.js 16+
Contributing
Contributions are welcome! See CONTRIBUTING.md for details.
License
MIT License - see LICENSE for details.
Support
- Documentation: UVB Docs
- Issues: GitHub Issues
- Discussions: GitHub Discussions
