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

@drmhse/sso-sdk

v0.3.14

Published

Zero-dependency TypeScript SDK for AuthOS, the multi-tenant authentication platform

Readme

AuthOS SDK

npm version License: MIT

A zero-dependency, strongly-typed TypeScript SDK for AuthOS, the multi-tenant authentication platform.

View Full Documentation →

Features

  • Zero Dependencies - Built on native fetch API
  • Strongly Typed - Complete TypeScript definitions
  • Framework Agnostic - Works in any JavaScript environment
  • Automatic Session Management - Invisible token persistence and auto-refresh
  • Smart Token Handling - Auto-inject tokens and handle 401 errors transparently
  • OAuth 2.0 Flows - Support for GitHub, Google, Microsoft
  • Password Authentication - Native email/password auth with MFA
  • Device Flow - RFC 8628 for CLIs and headless apps
  • Multi-Factor Authentication - TOTP-based 2FA with backup codes
  • Organization Management - Multi-tenant with RBAC
  • Analytics & Audit Logs - Track authentication and administrative actions
  • SAML 2.0 - Act as Identity Provider

Installation

npm install @drmhse/sso-sdk

Quick Start

import { SsoClient } from '@drmhse/sso-sdk';

// Initialize the client - automatically loads tokens from localStorage
const sso = new SsoClient({
  baseURL: 'https://sso.example.com'
});

// Login - session is automatically saved
await sso.auth.login({
  email: '[email protected]',
  password: 'SecurePass123!'
});

// Make authenticated requests - tokens auto-injected and auto-refreshed
const profile = await sso.user.getProfile();
console.log(profile.email);

const orgs = await sso.organizations.list();
console.log(orgs);

Understanding Context Modes

AuthOS supports two initialization modes that determine how authentication is handled:

Platform-Level Mode

For platform owners and administrators managing AuthOS itself:

const sso = new SsoClient({
  baseURL: 'https://sso.example.com'
});

// Platform-level login (email/password only)
await sso.auth.login({
  email: '[email protected]',
  password: 'SecurePass123!'
});
// JWT contains: { is_platform_owner: true, ... }

When to use: Admin dashboards, platform management tools, internal tooling.

Multi-Tenant Mode

For end-user applications that integrate with AuthOS:

// Initialize with org and service context
const sso = new SsoClient({
  baseURL: 'https://sso.example.com'
});

// OAuth redirects require org + service to determine:
// 1. Which tenant's OAuth credentials (BYOO) to use
// 2. Which service the identity is attributed to
const loginUrl = sso.auth.getLoginUrl('github', {
  org: 'acme-corp',      // Organization slug
  service: 'main-app',   // Service within that org
  redirect_uri: 'https://app.acme.com/callback'
});

// Password login can also include org context
await sso.auth.login({
  email: '[email protected]',
  password: 'SecurePass123!',
  org_slug: 'acme-corp'  // Optional: scopes JWT to this org
});
// JWT contains: { org: 'acme-corp', ... }

When to use: Customer-facing apps, SaaS products, multi-tenant applications.

Why Context Matters

| Context | JWT Claims | OAuth Credentials Used | |---------|-----------|----------------------| | Platform-level | is_platform_owner: true | Platform's PLATFORM_* credentials | | Multi-tenant | org: 'slug', service: 'slug' | Tenant's BYOO credentials (or platform fallback) |

The org and service parameters ensure:

  • Identity isolation: User identities are scoped to specific services
  • Credential isolation: Each tenant can use their own OAuth app credentials
  • Proper attribution: Login events are tracked per organization/service

New to the SDK? Start with the Getting Started Guide → for a step-by-step tutorial showing how to authenticate users from scratch, handle token refresh, and implement logout.

Essential Guides:

Authentication Examples

OAuth Login

// Redirect to OAuth provider
const loginUrl = sso.auth.getLoginUrl('github', {
  org: 'acme-corp',
  service: 'main-app',
  redirect_uri: 'https://app.acme.com/callback'
});
window.location.href = loginUrl;

// Handle callback - tokens are returned in URL fragment (#) for security
// (prevents tokens from being logged in server access logs)
const hashParams = new URLSearchParams(window.location.hash.substring(1));
const accessToken = hashParams.get('access_token');

if (accessToken) {
  // Clear hash from URL for security
  window.history.replaceState(null, '', window.location.pathname);

  // Initialize SDK with OAuth token - automatically stored
  const sso = new SsoClient({
    baseURL: 'https://sso.example.com',
    token: accessToken
  });
  // Token is now automatically stored and managed
  window.location.href = '/dashboard';
}

Password Authentication

// Register new user (tenant-first: always include org + service for proper attribution)
await sso.auth.register({
  email: '[email protected]',
  password: 'SecurePass123!',
  org_slug: 'acme-corp',     // Your organization
  service_slug: 'main-app'   // Your application
});
// User identity is now scoped to your org and service

// Login with password (service-scoped: include org + service)
await sso.auth.login({
  email: '[email protected]',
  password: 'SecurePass123!',
  org_slug: 'acme-corp',     // Your organization
  service_slug: 'main-app'   // Your application
});
// JWT is now scoped to org + service

// Enable MFA
const mfaSetup = await sso.user.mfa.setup();
console.log(mfaSetup.qr_code_svg); // Display QR code to user

// Verify and enable - automatically saves backup codes
const result = await sso.user.mfa.verify('123456'); // TOTP code from authenticator app
console.log('Backup codes:', result.backup_codes); // Save these securely!

Device Flow (for CLIs)

// In your CLI application
const deviceAuth = await sso.auth.deviceCode.request({
  client_id: 'cli-client',
  org: 'acme-corp',
  service: 'cli-tool'
});

console.log(`Visit: ${deviceAuth.verification_uri}`);
console.log(`Enter code: ${deviceAuth.user_code}`);

// Poll for token
const interval = setInterval(async () => {
  try {
    const tokens = await sso.auth.deviceCode.exchangeToken({
      device_code: deviceAuth.device_code,
      client_id: 'cli-client',
      grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
    });

    // Initialize SDK with the token - automatically stored
    const authenticatedSso = new SsoClient({
      baseURL: 'https://sso.example.com',
      token: tokens.access_token
    });

    clearInterval(interval);
    console.log('Authentication successful!');
  } catch (error) {
    // Continue polling...
  }
}, deviceAuth.interval * 1000);

Token Refresh

Automatic Token Refresh - The SDK automatically refreshes expired tokens when it detects a 401 error:

// No manual refresh needed! Just make your API calls
try {
  // If the access token is expired, the SDK will:
  // 1. Detect the 401 error
  // 2. Use the refresh token to get new tokens
  // 3. Retry the request automatically
  // 4. Return the result - you never see the 401!
  const profile = await sso.user.getProfile();
  console.log(profile);
} catch (error) {
  // You'll only see errors if refresh fails (e.g., refresh token expired)
  console.error('Session expired - please log in again');
  // Redirect to login
}

// Optional: Manually refresh if needed (advanced use case)
const currentRefreshToken = await sso.getToken();
if (currentRefreshToken) {
  const tokens = await sso.auth.refreshToken(currentRefreshToken);
  // Tokens automatically updated
}

Organization Management

// Create organization
const org = await sso.organizations.createPublic({
  name: 'Acme Corp',
  slug: 'acme-corp'
});

// Configure custom OAuth (BYOO - Bring Your Own OAuth)
await sso.organizations.oauthCredentials.set('acme-corp', 'github', {
  client_id: 'your-github-client-id',
  client_secret: 'your-github-client-secret'
});

// Invite team members
await sso.organizations.invitations.create('acme-corp', {
  email: '[email protected]',
  role: 'admin'
});

// Configure SMTP for transactional emails
await sso.organizations.setSmtp('acme-corp', {
  host: 'smtp.gmail.com',
  port: 587,
  username: '[email protected]',
  password: 'app-password',
  from_email: '[email protected]',
  from_name: 'Acme Corp'
});

Subscription & Billing

The SDK provides provider-agnostic billing integration that works with both Stripe and Polar.

// Check billing status
const billingInfo = await sso.organizations.billing.getInfo('acme-corp');
console.log(billingInfo.has_billing_account); // true/false
console.log(billingInfo.provider); // "stripe" or "polar"

// Open billing portal for subscription management
const portal = await sso.organizations.billing.createPortalSession('acme-corp', {
  return_url: 'https://app.acme.com/settings/billing'
});
// Redirect user to manage their subscription
window.location.href = portal.url;

BYOP - Bring Your Own Payment

Organizations can configure their own billing provider credentials to charge their end-users:

// Configure organization's own Stripe credentials
await sso.organizations.billingCredentials.set('acme-corp', 'stripe', {
  api_key: 'sk_live_...',
  webhook_secret: 'whsec_...',
  mode: 'live' // or 'test'
});

// Check credential status
const status = await sso.organizations.billingCredentials.get('acme-corp', 'stripe');
console.log(status.configured); // true
console.log(status.mode); // "live"

// Remove credentials
await sso.organizations.billingCredentials.delete('acme-corp', 'stripe');

Services & API Keys

// Create a service
const service = await sso.services.create('acme-corp', {
  name: 'Main Application',
  slug: 'main-app',
  redirect_uris: ['https://app.acme.com/callback']
});

// Create API key for service-to-service auth
const apiKey = await sso.services.apiKeys.create('acme-corp', 'main-app', {
  name: 'Production Backend',
  expires_at: '2026-01-01T00:00:00Z'
});

console.log('API Key (save this):', apiKey.key);

// Use API key for backend authentication
const backendClient = new SsoClient({
  baseURL: 'https://sso.example.com',
  apiKey: apiKey.key
});

Analytics

// Get login trends
const trends = await sso.analytics.getLoginTrends('acme-corp', {
  start_date: '2025-01-01',
  end_date: '2025-01-31'
});

// Get provider distribution
const byProvider = await sso.analytics.getLoginsByProvider('acme-corp');
console.log(byProvider); // [{ provider: 'github', count: 1523 }, ...]

// Get recent activity
const recentLogins = await sso.analytics.getRecentLogins('acme-corp', {
  limit: 20
});

Error Handling

import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';

try {
  await sso.user.getProfile();
} catch (error) {
  if (error instanceof SsoApiError) {
    console.error(`API Error: ${error.message}`);
    console.error(`Status: ${error.status}`);
    console.error(`Code: ${error.errorCode}`);

    if (error.status === 401) {
      // Session expired (refresh token also expired)
      // Automatic refresh already tried and failed
      // Redirect to login
      window.location.href = '/login';
    } else if (error.status === 403) {
      // Forbidden - insufficient permissions
      console.error('You do not have permission to access this resource');
    }
  } else {
    console.error('Unexpected error:', error);
  }
}

// React to authentication state changes
sso.onAuthStateChange((isAuthenticated) => {
  if (!isAuthenticated) {
    // User logged out or session expired
    window.location.href = '/login';
  }
});

Platform Administration

For platform owners managing AuthOS:

// Approve pending organization
await sso.platform.organizations.approve('org-id', {
  tier: 'professional',
  reason: 'Verified enterprise customer'
});

// Promote user to platform owner
await sso.platform.promoteOwner({
  email: '[email protected]'
});

// Get platform analytics
const overview = await sso.platform.analytics.getOverview();
console.log(overview); // { total_users, total_orgs, total_logins, ... }

// Search users across all organizations
const users = await sso.platform.users.search('[email protected]');

TypeScript Support

The SDK is written in TypeScript and includes complete type definitions:

import type {
  User,
  Organization,
  Service,
  LoginTrendPoint,
  RefreshTokenResponse,
  CreateServicePayload,
  UpdateServicePayload,
  LoginPayload,
  RegisterPayload,
  SsoApiError
} from '@drmhse/sso-sdk';

// Example using types
const createService = async (payload: CreateServicePayload): Promise<Service> => {
  return await sso.services.create('org-slug', payload);
};

const login = async (credentials: LoginPayload): Promise<RefreshTokenResponse> => {
  return await sso.auth.login(credentials);
};

Validating JWTs in Your Backend

AuthOS uses RS256 (asymmetric) JWT signing. Your backend can validate tokens without sharing secrets:

// Fetch JWKS from the SSO platform
const jwksUrl = 'https://sso.example.com/.well-known/jwks.json';
const response = await fetch(jwksUrl);
const jwks = await response.json();

// Use a JWT library to verify tokens
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const client = jwksClient({ jwksUri: jwksUrl });
const key = await client.getSigningKey(header.kid);
const publicKey = key.getPublicKey();

const decoded = jwt.verify(token, publicKey, {
  algorithms: ['RS256']
});

See Backend Validation Guide →

Documentation

Complete documentation is available at drmhse.com/docs/sso

Key Documentation Pages

Requirements

  • Node.js: 18+ (for native fetch support)
  • Browsers: All modern browsers with fetch support
  • TypeScript: 4.5+ (optional, but recommended)

License

MIT © DRM HSE

Support