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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@drmhse/sso-sdk

v0.2.9

Published

Zero-dependency TypeScript SDK for the multi-tenant SSO Platform API

Readme

SSO Platform SDK

npm version License: MIT

A zero-dependency, strongly-typed TypeScript SDK for the multi-tenant SSO Platform API.

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);

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 - initialize client with token from OAuth callback
const params = new URLSearchParams(window.location.search);
const accessToken = params.get('access_token');

if (accessToken) {
  // 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
await sso.auth.register({
  email: '[email protected]',
  password: 'SecurePass123!',
  org_slug: 'acme-corp'
});

// Login with password - session automatically saved
await sso.auth.login({
  email: '[email protected]',
  password: 'SecurePass123!'
});
// Tokens are now automatically stored and injected on all requests

// 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'
});

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 the entire SSO system:

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

The SSO platform 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