postex-auth-sdk-stage
v1.0.4
Published
PostEx Auth SDK for authentication flows: OTP, magic links, passkeys, and token management
Maintainers
Readme
PostEx Authentication SDK
Developer Documentation
Overview
The PostEx Authentication SDK provides a comprehensive solution for implementing secure authentication in web applications. It supports multiple authentication methods including:
- WebAuthn/Passkeys for passwordless authentication
- One-Time Password (OTP) verification
- Magic link authentication
- DPoP (Demonstration of Proof-of-Possession) for enhanced security
Key Features
- Multi-factor authentication: Support for passkeys, OTP, and magic links
- DPoP security: RFC 9449 compliant token binding using ECDSA P-256
- Token management: Automatic token storage, refresh, and cleanup
- Trusted device support: Cookie-based device recognition
- Browser compatibility: Built-in feature detection and fallbacks
Installation
Install the SDK via npm:
npm install postex-auth-sdk-stageQuick Start
Initialize the SDK
import { AuthSDK } from 'postex-auth-sdk-stage';
const auth = new AuthSDK({ apiKey: 'your-api-key' });Basic Authentication Flow
// 1. Check authentication status
const status = await auth.getStatus('[email protected]');
// 2. Initiate authentication
const result = await auth.initiateAuth('[email protected]');
// 3. Handle authentication based on status
if (result.status === 'webauthn_challenge') {
// Use passkey authentication
const authResponse = await auth.authenticateWithPasskey({
challenge: result.challenge,
rp: result.rp,
credentialIds: result.credentialIds
});
} else if (result.status === 'otp_sent') {
// Verify OTP code
const response = await auth.verifyOTP(otpCode);
}API Reference
AuthSDK Class
The main class for interacting with the authentication service.
Constructor
new AuthSDK(config: AuthSDKConfig)Parameters:
| Parameter | Description |
|-----------|-------------|
| config.apiKey | (Optional) Your API key for authentication |
Core Authentication Methods
getStatus()
Check if the client has a trusted device session and what authentication method is available.
async getStatus(email: string): Promise<AuthStatusResponse>Returns:
status:'no_session'|'session_found'|'webauthn_ready'email: User's email addresswebauthn: Whether WebAuthn is availablechallenge,credentialIds,rp: WebAuthn challenge data if available
initiateAuth()
Start the authentication process. Returns either a WebAuthn challenge or confirms OTP was sent.
async initiateAuth(email: string): Promise<InitiateAuthResponse>Returns:
status:'webauthn_challenge'|'otp_sent'challenge,credentialIds,rp: WebAuthn data if status is'webauthn_challenge'
verifyOTP()
Verify the OTP code entered by the user. Automatically stores tokens on success.
async verifyOTP(otp: string): Promise<OTPVerifyResponse>Returns:
access_token: Bearer token for API requestsrefresh_token: Token for refreshing accessid_token: User identity tokenexpires_in: Token expiration time in secondsverified,email: Verification status and user email
WebAuthn/Passkey Methods
registerPasskey()
Register a new passkey for the user. This enables passwordless authentication.
async registerPasskey(email: string): Promise<PasskeyRegisterResponse>Process:
- Initiates passkey registration challenge
- Triggers browser's passkey creation flow
- Automatically prevents duplicate registration
- Returns registration confirmation
authenticateWithPasskey()
Authenticate using a registered passkey. Automatically stores tokens on success.
async authenticateWithPasskey({
challenge: string,
rp: { name: string; host: string },
credentialIds: string[]
}): Promise<AuthResponse>Parameters:
challenge: WebAuthn challenge frominitiateAuth()rp: Relying party informationcredentialIds: List of allowed credential IDs
getPasskeyStatus()
Check if a user has registered passkeys.
async getPasskeyStatus(username: string): Promise<PasskeyStatusResponse>Returns:
hasCredentials: Boolean indicating if user has passkeyscredentialId: The credential ID if available
removePasskey()
Remove a user's registered passkey.
async removePasskey(username: string): Promise<void>Magic Link Methods
verifyMagicLink()
Verify a magic link token. This sets the authentication session cookie.
async verifyMagicLink(token: string, email: string): Promise<void>completeMagicLink()
Complete magic link authentication. Must be called after verifyMagicLink(). Automatically stores tokens.
async completeMagicLink(): Promise<OTPVerifyResponse>Token Management Methods
refreshToken()
Refresh the access token using the server-stored refresh token. Requires a trusted device cookie. Rate limited to 10 requests per minute.
async refreshToken(): Promise<RefreshTokenResponse>Returns:
access_token: New access tokenid_token: New ID tokenexpires_in: Token expiration time
getAccessToken()
Retrieve the stored access token.
async getAccessToken(): Promise<string | null>logout()
Log out from the current device. Revokes the current token and trusted device, then clears local tokens and DPoP keys.
async logout(): Promise<void>DPoP (Proof-of-Possession) Methods
DPoP provides enhanced security by binding tokens to cryptographic keys. The SDK implements RFC 9449 using ECDSA P-256.
getRequestAuthHeaders()
Get authentication headers (Authorization + DPoP) for a request. Use this to attach auth to your own HTTP client.
async getRequestAuthHeaders(
method: string,
url: string
): Promise<Record<string, string>>Example:
const headers = await auth.getRequestAuthHeaders('GET', '/api/user');
// headers = { Authorization: 'Bearer ...', DPoP: '...' }authenticatedFetch()
Drop-in replacement for fetch() that automatically includes DPoP authentication headers.
async authenticatedFetch(
input: RequestInfo | URL,
init?: RequestInit
): Promise<Response>Example:
const response = await auth.authenticatedFetch('/api/protected', {
method: 'POST',
body: JSON.stringify({ data: 'value' })
});WebAuthn Utility Functions
The webauthn module provides utility functions for WebAuthn support detection, data encoding, and passkey email management.
Browser Support Detection
isWebAuthnSupported()
Check if the browser supports WebAuthn.
function isWebAuthnSupported(): booleanisConditionalUISupported()
Check if conditional UI (autofill passkeys) is supported.
async function isConditionalUISupported(): Promise<boolean>Encoding Functions
Functions for converting between ArrayBuffer and base64 formats.
// Convert ArrayBuffer to base64
function arrayBufferToBase64(buffer: ArrayBuffer): string
// Convert base64 to ArrayBuffer
function base64ToArrayBuffer(base64: string): ArrayBuffer
// Convert ArrayBuffer to base64url (URL-safe)
function arrayBufferToBase64url(buffer: ArrayBuffer): string
// Convert string to ArrayBuffer
function stringToArrayBuffer(str: string): ArrayBufferPasskey Email Management
Functions for storing and retrieving the passkey email in IndexedDB.
// Store passkey email
async function setPasskeyEmail(email: string): Promise<void>
// Retrieve passkey email
async function getPasskeyEmail(): Promise<string | null>
// Clear stored passkey email
async function clearPasskeyEmail(): Promise<void>DPoP Utility Functions
Low-level DPoP functions. Most developers won't need these as they're handled automatically by the SDK.
// Generate DPoP key pair
async function generateDPoPKeyPair(): Promise<{
publicKey: MinimalECPublicKeyJWK;
thumbprint: string;
}>
// Generate DPoP proof JWT
async function generateDPoPProof(
httpMethod: string,
httpUri: string,
accessToken?: string
): Promise<string | null>
// Check if DPoP is enabled
async function isDPoPEnabled(): Promise<boolean>
// Clear DPoP keys
async function clearDPoPKey(): Promise<void>Complete Examples
Passkey Registration Flow
import { AuthSDK, isWebAuthnSupported } from 'postex-auth-sdk-stage';
async function registerUserPasskey(email: string) {
// Check browser support
if (!isWebAuthnSupported()) {
alert('Passkeys not supported in this browser');
return;
}
const auth = new AuthSDK({ apiKey: 'your-api-key' });
try {
// Register passkey
const result = await auth.registerPasskey(email);
if (result.registered) {
console.log('Passkey registered successfully');
}
} catch (error) {
console.error('Passkey registration failed:', error);
}
}OTP Authentication Flow
import { AuthSDK } from 'postex-auth-sdk-stage';
async function loginWithOTP(email: string, otpCode: string) {
const auth = new AuthSDK({ apiKey: 'your-api-key' });
try {
// Initiate authentication
const initResult = await auth.initiateAuth(email);
if (initResult.status !== 'otp_sent') {
throw new Error('Expected OTP to be sent');
}
// Verify OTP
const verifyResult = await auth.verifyOTP(otpCode);
if (verifyResult.verified) {
console.log('Authentication successful');
console.log('Access token:', verifyResult.access_token);
}
} catch (error) {
console.error('Authentication failed:', error);
}
}Magic Link Authentication Flow
import { AuthSDK } from 'postex-auth-sdk-stage';
async function handleMagicLink(token: string, email: string) {
const auth = new AuthSDK({ apiKey: 'your-api-key' });
try {
// Step 1: Verify the magic link token
await auth.verifyMagicLink(token, email);
// Step 2: Complete authentication
const result = await auth.completeMagicLink();
console.log('Magic link authentication successful');
console.log('Access token:', result.access_token);
} catch (error) {
console.error('Magic link authentication failed:', error);
}
}Making Authenticated API Requests
import { AuthSDK } from 'postex-auth-sdk-stage';
const auth = new AuthSDK({ apiKey: 'your-api-key' });
// Option 1: Use authenticatedFetch (recommended)
const response1 = await auth.authenticatedFetch('/api/user/profile');
const userData = await response1.json();
// Option 2: Get headers for your own HTTP client
const headers = await auth.getRequestAuthHeaders(
'POST',
'https://api.example.com/data'
);
const response2 = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: 'value' })
});Error Handling
The SDK throws AuthSDKFetchError for HTTP errors. This error includes the status code and response data.
import { AuthSDK, AuthSDKFetchError } from 'postex-auth-sdk-stage';
try {
await auth.verifyOTP(otpCode);
} catch (error) {
if (error instanceof AuthSDKFetchError) {
console.error('Status:', error.response.status);
console.error('Data:', error.response.data);
if (error.response.status === 401) {
// Token expired - attempt refresh
try {
await auth.refreshToken();
} catch (refreshError) {
// Refresh failed - redirect to login
window.location.href = '/login';
}
}
}
}Security Considerations
DPoP Token Binding
The SDK automatically implements DPoP (RFC 9449) to bind tokens to cryptographic keys. This prevents token theft and replay attacks. DPoP keys are:
- Generated using ECDSA P-256 (ES256)
- Stored securely in IndexedDB
- Non-extractable (private key cannot be exported)
- Automatically included in all authenticated requests
Token Storage
Tokens are stored in localStorage with the following keys:
postex-auth-token: Access tokenauth_sdk_refresh_token: Refresh tokenauth_sdk_id_token: ID token
Trusted Device Cookies
The backend sets a secure, HTTP-only cookie (td) to identify trusted devices. This enables:
- Token refresh without re-authentication
- Session persistence across page reloads
- Device-specific security policies
Best Practices
- Always use HTTPS: The SDK requires secure contexts for WebAuthn and cryptographic operations
- Handle token expiration: Implement automatic token refresh before expiration
- Clear tokens on logout: Always call
auth.logout()when users sign out - Validate on backend: Never trust client-side authentication alone
- Monitor 401 errors: The SDK automatically clears tokens on 401 responses
Browser Compatibility
WebAuthn Support
WebAuthn/Passkeys are supported in:
- Chrome/Edge 67+
- Firefox 60+
- Safari 13+
- Opera 54+
Fallback Authentication
For browsers without WebAuthn support, the SDK automatically falls back to OTP authentication. Always check browser support before attempting passkey operations:
import { isWebAuthnSupported } from 'postex-auth-sdk-stage';
if (isWebAuthnSupported()) {
// Offer passkey registration/authentication
} else {
// Use OTP or magic link
}TypeScript Support
The SDK is written in TypeScript and includes full type definitions. All interfaces are exported for your convenience:
import {
AuthSDK,
AuthSDKConfig,
AuthStatusResponse,
InitiateAuthResponse,
OTPVerifyResponse,
AuthResponse,
PasskeyRegisterResponse,
PasskeyStatusResponse,
RefreshTokenResponse,
AuthSDKFetchError
} from 'postex-auth-sdk-stage';Support and Resources
- API Documentation: https://auth-stage.postexglobal.com/docs
- GitHub Repository: (Add your repository link)
- Issue Tracker: (Add your issue tracker link)
- Email Support: (Add your support email)
API overview
- AuthSDK – Main client:
getStatus,initiateAuth,verifyOTP,verifyMagicLink,completeMagicLink,authenticateWithPasskey,registerPasskey,getPasskeyStatus,removePasskey,getRequestAuthHeaders,authenticatedFetch,refreshToken,logout,getAccessToken,storeTokens,clearTokens - WebAuthn – Helpers:
isWebAuthnSupported,isConditionalUISupported, base64/ArrayBuffer conversion, DPoP key and proof helpers, passkey email storage - AuthSDKFetchError – Error class with
response.statusandresponse.data
Versioning
This package follows semantic versioning. CDN URLs support:
@1– latest 1.x.x@1.0– latest 1.0.x@1.0.0– exact version
License
MIT
