npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

postex-auth-sdk-stage

v1.0.4

Published

PostEx Auth SDK for authentication flows: OTP, magic links, passkeys, and token management

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-stage

Quick 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 address
  • webauthn: Whether WebAuthn is available
  • challenge, 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 requests
  • refresh_token: Token for refreshing access
  • id_token: User identity token
  • expires_in: Token expiration time in seconds
  • verified, 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 from initiateAuth()
  • rp: Relying party information
  • credentialIds: 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 passkeys
  • credentialId: 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 token
  • id_token: New ID token
  • expires_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(): boolean

isConditionalUISupported()

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): ArrayBuffer

Passkey 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 token
  • auth_sdk_refresh_token: Refresh token
  • auth_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.status and response.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