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

@passflow/passflow-js-sdk

v0.1.45

Published

Passflow JS SDK

Readme

Passflow JavaScript SDK Documentation

Changelog

Version 0.1.45

  • Fixed bug related to null/undefined checks in the codebase

Table of Contents

Quick Start Examples

Basic Initialization

import { Passflow } from "passflow-js";

// Minimal initialization with just the required appId
const passflow = new Passflow({
  appId: "your-app-id",
});

// Simple session setup
passflow.session({
  createSession: () => console.log("User is authenticated"),
  expiredSession: () => console.log("User session expired"),
});

Simple Authentication

// Basic sign in with just email and password
const simpleSignIn = async () => {
  try {
    await passflow.signIn({
      email: "[email protected]",
      password: "password123",
    });
    console.log("Signed in successfully");
  } catch (error) {
    console.error("Sign in failed", error);
  }
};

// Basic sign up with minimal user info
const simpleSignUp = async () => {
  try {
    await passflow.signUp({
      user: {
        email: "[email protected]",
        password: "password123",
      },
    });
    console.log("Registered successfully");
  } catch (error) {
    console.error("Registration failed", error);
  }
};

// Simple logout
const simpleSignOut = async () => {
  await passflow.logOut();
};

Quick Passwordless Authentication

// Start passwordless flow with just email
const simplePasswordless = async () => {
  try {
    const response = await passflow.passwordlessSignIn({
      email: "[email protected]",
      challenge_type: "otp",
      redirect_url: window.location.origin,
    });
    console.log("Check your email for the code");
    return response.challenge_id;
  } catch (error) {
    console.error("Failed to start passwordless flow", error);
  }
};

// Complete with just the challenge ID and OTP
const completeSimplePasswordless = async (challengeId, otp) => {
  try {
    await passflow.passwordlessSignInComplete({
      challenge_id: challengeId,
      otp: otp,
    });
    console.log("Authentication successful");
  } catch (error) {
    console.error("Failed to complete passwordless flow", error);
  }
};

Basic Passkey Usage

// Register a passkey with minimal options
const simplePasskeyRegister = async () => {
  try {
    await passflow.passkeyRegister({
      relying_party_id: window.location.hostname,
      redirect_url: window.location.origin,
      scopes: ["id", "offline"],
    });
    console.log("Passkey registered");
  } catch (error) {
    console.error("Passkey registration failed", error);
  }
};

// Authenticate with minimal options
const simplePasskeyAuthenticate = async () => {
  try {
    await passflow.passkeyAuthenticate({
      relying_party_id: window.location.hostname,
    });
    console.log("Authenticated with passkey");
  } catch (error) {
    console.error("Passkey authentication failed", error);
  }
};

Quick Password Reset

// Send reset email with just the email address
const simplePasswordReset = async () => {
  try {
    await passflow.sendPasswordResetEmail({
      email: "[email protected]",
    });
    console.log("Password reset email sent");
  } catch (error) {
    console.error("Failed to send reset email", error);
  }
};

Basic Tenant Creation

// Create a tenant with just a name
const simpleCreateTenant = async () => {
  try {
    await passflow.createTenant("My Organization");
    console.log("Tenant created");
  } catch (error) {
    console.error("Failed to create tenant", error);
  }
};

Simple Event Subscription

// Subscribe to authentication events with minimal setup
passflow.subscribe({
  onAuthChange: (eventType) => {
    console.log(`Auth event occurred: ${eventType}`);
  },
});

Introduction

Passflow JavaScript SDK is a client library for interacting with the Passflow authentication service. It provides a comprehensive set of features for user authentication, token management, tenant management, and more. The SDK supports various authentication methods including email/password, passwordless, passkeys (WebAuthn), and federated identity providers.

Installation

npm install passflow-js
# or
yarn add passflow-js

Getting Started

Initialization

Import and initialize the Passflow client with your configuration:

import { Passflow } from "passflow-js";

const passflow = new Passflow({
  url: "https://auth.passflow.cloud", // or your custom URL
  appId: "your-app-id",
  scopes: [
    "id",
    "offline",
    "tenant",
    "email",
    "oidc",
    "openid",
    "access:tenant:all",
  ], // optional, these are the defaults
  createTenantForNewUser: false, // optional
  parseQueryParams: true, // optional, will parse tokens from URL query params
  keyStoragePrefix: "myapp", // optional, prefix for localStorage keys
});

Session Management

Setup session management to handle authentication state:

passflow.session({
  createSession: (tokens) => {
    console.log("Session created", tokens);
    // Set your app's authenticated state
  },
  expiredSession: () => {
    console.log("Session expired");
    // Clear your app's authenticated state
  },
  doRefresh: true, // automatically refresh tokens when expired
});

Authentication

Sign In

// Sign in with email and password
const signIn = async () => {
  try {
    const response = await passflow.signIn({
      email: "[email protected]",
      password: "password123",
      scopes: ["id", "offline", "tenant", "email"], // optional
    });
    console.log("Signed in successfully", response);
  } catch (error) {
    console.error("Sign in failed", error);
  }
};

Sign Up

// Register a new user
const signUp = async () => {
  try {
    const response = await passflow.signUp({
      user: {
        email: "[email protected]",
        password: "password123",
        given_name: "John",
        family_name: "Doe",
        // Additional optional user fields
      },
      scopes: ["id", "offline", "tenant", "email"], // optional
      create_tenant: true, // optional
    });
    console.log("Registered successfully", response);
  } catch (error) {
    console.error("Registration failed", error);
  }
};

Sign Out

// Log out the current user
const signOut = async () => {
  try {
    await passflow.logOut();
    console.log("Signed out successfully");
  } catch (error) {
    console.error("Sign out failed", error);
  }
};

Passwordless Authentication

// Start passwordless authentication flow
const startPasswordless = async () => {
  try {
    const response = await passflow.passwordlessSignIn({
      email: "[email protected]",
      challenge_type: "otp", // or 'magic_link'
      redirect_url: "https://yourapp.com/auth/callback",
    });
    console.log("Passwordless authentication started", response);
    // Store the challenge_id for the next step
  } catch (error) {
    console.error("Passwordless authentication failed", error);
  }
};

// Complete passwordless authentication flow
const completePasswordless = async (challengeId, otp) => {
  try {
    const response = await passflow.passwordlessSignInComplete({
      challenge_id: challengeId,
      otp: otp,
    });
    console.log("Passwordless authentication completed", response);
  } catch (error) {
    console.error("Passwordless authentication completion failed", error);
  }
};

Federated Authentication

// Sign in with a provider using popup
passflow.federatedAuthWithPopup(
  "google", // or 'facebook'
  "https://yourapp.com/auth/callback",
  ["id", "offline", "tenant", "email"] // optional scopes
);

// Sign in with a provider using redirect
passflow.federatedAuthWithRedirect(
  "google", // or 'facebook'
  "https://yourapp.com/auth/callback",
  ["id", "offline", "tenant", "email"] // optional scopes
);

Passkey Authentication

// Register a new passkey
const registerPasskey = async () => {
  try {
    const response = await passflow.passkeyRegister({
      passkey_display_name: "My Passkey",
      passkey_username: "[email protected]",
      relying_party_id: window.location.hostname,
      redirect_url: "https://yourapp.com/auth/callback",
      scopes: ["id", "offline", "tenant", "email"], // optional
    });
    console.log("Passkey registered successfully", response);
  } catch (error) {
    console.error("Passkey registration failed", error);
  }
};

// Authenticate with a passkey
const authenticateWithPasskey = async () => {
  try {
    const response = await passflow.passkeyAuthenticate({
      relying_party_id: window.location.hostname,
      scopes: ["id", "offline", "tenant", "email"], // optional
    });
    console.log("Passkey authentication successful", response);
  } catch (error) {
    console.error("Passkey authentication failed", error);
  }
};

// Add additional passkey to user account
const addPasskey = async () => {
  try {
    await passflow.addUserPasskey({
      relyingPartyId: window.location.hostname,
      passkeyUsername: "[email protected]",
      passkeyDisplayName: "My Secondary Passkey",
    });
    console.log("Passkey added successfully");
  } catch (error) {
    console.error("Adding passkey failed", error);
  }
};

// Manage user passkeys
const managePasskeys = async () => {
  try {
    // Get all passkeys
    const passkeys = await passflow.getUserPasskeys();
    console.log("User passkeys", passkeys);

    // Rename a passkey
    await passflow.renameUserPasskey("New Name", "passkey-id");

    // Delete a passkey
    await passflow.deleteUserPasskey("passkey-id");
  } catch (error) {
    console.error("Passkey management failed", error);
  }
};

Password Reset

// Send password reset email
const sendResetEmail = async () => {
  try {
    await passflow.sendPasswordResetEmail({
      email: "[email protected]",
      reset_page_url: "https://yourapp.com/reset-password",
    });
    console.log("Password reset email sent");
  } catch (error) {
    console.error("Sending password reset email failed", error);
  }
};

// Reset password (after receiving reset token)
const resetPassword = async (newPassword) => {
  try {
    // The token should be in the URL query parameters
    const response = await passflow.resetPassword(newPassword);
    console.log("Password reset successful", response);
  } catch (error) {
    console.error("Password reset failed", error);
  }
};

Token Management

// Check if user is authenticated
const isAuthenticated = passflow.isAuthenticated();

// Get current tokens
const tokens = passflow.getTokensCache();

// Get parsed tokens (decoded JWT payload)
const parsedTokens = passflow.getParsedTokenCache();

// Manually refresh token
const refreshToken = async () => {
  try {
    const response = await passflow.refreshToken();
    console.log("Token refreshed", response);
  } catch (error) {
    console.error("Token refresh failed", error);
  }
};

// Set tokens manually
const setTokens = async (tokens) => {
  try {
    await passflow.setTokens({
      access_token: tokens.access_token,
      refresh_token: tokens.refresh_token,
      id_token: tokens.id_token,
      scopes: tokens.scopes,
    });
  } catch (error) {
    console.error("Setting tokens failed", error);
  }
};

// Handle tokens from redirect
const handleRedirect = () => {
  const tokens = passflow.handleTokensRedirect();
  if (tokens) {
    console.log("Tokens received from redirect", tokens);
  }
};

Tenant Management

// Create a new tenant
const createTenant = async () => {
  try {
    const response = await passflow.createTenant("My Organization", true);
    console.log("Tenant created", response);
  } catch (error) {
    console.error("Tenant creation failed", error);
  }
};

// Join a tenant via invitation
const joinTenant = async (invitationToken) => {
  try {
    const response = await passflow.joinInvitation(invitationToken);
    console.log("Joined tenant", response);
  } catch (error) {
    console.error("Joining tenant failed", error);
  }
};

Invitation Management

// Request an invitation link
const requestInvite = async () => {
  try {
    const response = await passflow.requestInviteLink({
      email: "[email protected]",
      tenant: "tenant-id", // optional
      group: "group-id", // optional
      role: "role-name", // optional
      callback: "https://yourapp.com/onboarding", // optional
      send_to_email: true, // optional
    });
    console.log("Invitation link created", response);
  } catch (error) {
    console.error("Creating invitation link failed", error);
  }
};

// Get all active invitations
const getInvitations = async () => {
  try {
    const invitations = await passflow.getInvitations({
      tenant_id: "tenant-id", // optional
      group_id: "group-id", // optional
      skip: 0, // optional
      limit: 10, // optional
    });
    console.log("Active invitations", invitations);
  } catch (error) {
    console.error("Getting invitations failed", error);
  }
};

// Delete an invitation
const deleteInvitation = async (token) => {
  try {
    await passflow.deleteInvitation(token);
    console.log("Invitation deleted");
  } catch (error) {
    console.error("Deleting invitation failed", error);
  }
};

Events and Subscriptions

// Define a subscriber
const subscriber = {
  onAuthChange: (eventType, source) => {
    console.log(`Auth event: ${eventType}`, source);

    // Handle different event types
    switch (eventType) {
      case "signin":
        // Handle sign in
        break;
      case "signout":
        // Handle sign out
        break;
      case "register":
        // Handle registration
        break;
      case "error":
        // Handle error
        break;
      case "refresh":
        // Handle token refresh
        break;
    }
  },
};

// Subscribe to all events
passflow.subscribe(subscriber);

// Subscribe to specific events
passflow.subscribe(subscriber, ["signin", "signout"]);

// Unsubscribe from all events
passflow.unsubscribe(subscriber);

// Unsubscribe from specific events
passflow.unsubscribe(subscriber, ["error"]);

Error Handling

Errors thrown by the SDK are typically instances of PassflowError which include details about the error:

try {
  await passflow.signIn({
    email: "[email protected]",
    password: "wrong-password",
  });
} catch (error) {
  if (error instanceof PassflowError) {
    console.error(`Error ID: ${error.id}`);
    console.error(`Error Message: ${error.message}`);
    console.error(`Status Code: ${error.status}`);
    console.error(`Location: ${error.location}`);
    console.error(`Time: ${error.time}`);
  } else {
    console.error("Unknown error:", error);
  }
}

API Reference

Passflow Class

The main class that provides access to all functionality of the SDK.

Constructor

constructor(config: PassflowConfig)

Configuration options:

  • url: The URL of the Passflow service (default: 'https://auth.passflow.cloud')
  • appId: Your application ID
  • scopes: Token scopes to request (default: ['id', 'offline', 'tenant', 'email', 'oidc', 'openid', 'access:tenant:all'])
  • createTenantForNewUser: Whether to create a tenant for new users (default: false)
  • parseQueryParams: Whether to parse tokens from URL query parameters (default: false)
  • keyStoragePrefix: Prefix for localStorage keys

Methods

Authentication Methods

| Method | Description | | ---------------------------------------------------------- | -------------------------------------- | | session({ createSession, expiredSession, doRefresh }) | Set up session management | | signIn(payload) | Sign in with email/password | | signUp(payload) | Register a new user | | logOut() | Sign out the current user | | passwordlessSignIn(payload) | Start passwordless authentication | | passwordlessSignInComplete(payload) | Complete passwordless authentication | | federatedAuthWithPopup(provider, redirectUrl, scopes) | Sign in with a provider using popup | | federatedAuthWithRedirect(provider, redirectUrl, scopes) | Sign in with a provider using redirect | | passkeyRegister(payload) | Register a new passkey | | passkeyAuthenticate(payload) | Authenticate with a passkey | | sendPasswordResetEmail(payload) | Send password reset email | | resetPassword(newPassword, scopes) | Reset password |

Token Methods

| Method | Description | | -------------------------- | ------------------------------ | | isAuthenticated() | Check if user is authenticated | | getTokensCache() | Get current tokens | | getParsedTokenCache() | Get parsed tokens | | refreshToken() | Refresh the access token | | setTokens(tokens) | Set tokens manually | | handleTokensRedirect() | Handle tokens from redirect | | authRedirectUrl(options) | Generate an auth redirect URL | | authRedirect(options) | Redirect to the auth page |

Tenant Methods

| Method | Description | | ---------------------------------- | ---------------------------- | | createTenant(name, refreshToken) | Create a new tenant | | joinInvitation(token, scopes) | Join a tenant via invitation |

Invitation Methods

| Method | Description | | ---------------------------- | -------------------------- | | requestInviteLink(payload) | Request an invitation link | | getInvitations(options) | Get all active invitations | | deleteInvitation(token) | Delete an invitation |

Passkey Methods

| Method | Description | | ------------------------------------ | ----------------------------- | | getUserPasskeys() | Get all user passkeys | | renameUserPasskey(name, passkeyId) | Rename a passkey | | deleteUserPasskey(passkeyId) | Delete a passkey | | addUserPasskey(options) | Add a passkey to user account |

Event Methods

| Method | Description | | --------------------------------- | ---------------------------- | | subscribe(subscriber, events) | Subscribe to auth events | | unsubscribe(subscriber, events) | Unsubscribe from auth events |

Services

The SDK includes several services that handle specific functionality:

  • AuthService: Handles authentication and session management
  • UserService: Handles user-related operations
  • TenantService: Handles tenant operations
  • InvitationService: Handles invitation operations
  • DeviceService: Manages device identification
  • TokenService: Handles token parsing and validation
  • StorageManager: Manages token storage

Types

Key types used in the SDK:

PassflowConfig

type PassflowConfig = {
  url?: string;
  appId?: string;
  scopes?: string[];
  createTenantForNewUser?: boolean;
  parseQueryParams?: boolean;
  keyStoragePrefix?: string;
};

Tokens

type Tokens = {
  access_token: string;
  id_token?: string;
  refresh_token?: string;
  scopes?: string[];
};

Token

type Token = {
  aud: string[];
  exp: number;
  iat: number;
  iss: string;
  jti: string;
  sub: string;
  type: string;
  email?: string;
  passflow_tm?: RawUserMembership;
  payload?: unknown;
  membership?: UserMembership;
};

PassflowSignInPayload

type PassflowSignInPayload = {
  password: string;
  scopes?: string[];
  email?: string;
  phone?: string;
  username?: string;
} & ({ email: string } | { phone: string } | { username: string });

PassflowSignUpPayload

type PassflowSignUpPayload = {
  user: PassflowUserPayload;
  scopes?: string[];
  create_tenant?: boolean;
  anonymous?: boolean;
  invite?: string;
};

UserMembership

type UserMembership = {
  raw: RawUserMembership;
  tenants: TenantMembership[];
};

PassflowEvent

enum PassflowEvent {
  SignIn = "signin",
  Register = "register",
  SignOut = "signout",
  Error = "error",
  Refresh = "refresh",
}

Detailed Examples

Complete Sign In Flow

import { Passflow, PassflowError } from "passflow-js";

// Initialize Passflow
const passflow = new Passflow({
  appId: "your-app-id",
  parseQueryParams: true,
});

// Set up session management
passflow.session({
  createSession: (tokens) => {
    // Store authentication state
    localStorage.setItem("isAuthenticated", "true");

    // Update UI
    document.getElementById("login-form").style.display = "none";
    document.getElementById("user-dashboard").style.display = "block";
  },
  expiredSession: () => {
    // Clear authentication state
    localStorage.removeItem("isAuthenticated");

    // Update UI
    document.getElementById("login-form").style.display = "block";
    document.getElementById("user-dashboard").style.display = "none";
  },
  doRefresh: true,
});

// Handle form submission
document
  .getElementById("login-form")
  .addEventListener("submit", async (event) => {
    event.preventDefault();

    const email = document.getElementById("email").value;
    const password = document.getElementById("password").value;

    try {
      await passflow.signIn({ email, password });
      // Login successful - session callback will handle UI update
    } catch (error) {
      if (error instanceof PassflowError) {
        document.getElementById("error-message").textContent = error.message;
      } else {
        document.getElementById("error-message").textContent =
          "An unexpected error occurred";
        console.error(error);
      }
    }
  });

// Handle logout
document.getElementById("logout-button").addEventListener("click", async () => {
  await passflow.logOut();
  // Logout successful - session callback will handle UI update
});

// Check for redirect tokens on page load
window.addEventListener("load", () => {
  const tokens = passflow.handleTokensRedirect();
  if (tokens) {
    console.log("Authenticated via redirect");
  }
});

Authentication with Passkeys

import { Passflow } from "passflow-js";

// Initialize Passflow
const passflow = new Passflow({
  appId: "your-app-id",
});

// Set up session management
passflow.session({
  createSession: (tokens) => {
    console.log("Session created", tokens);
  },
  expiredSession: () => {
    console.log("Session expired");
  },
  doRefresh: true,
});

// Handle passkey registration
document
  .getElementById("register-passkey-button")
  .addEventListener("click", async () => {
    try {
      const email = document.getElementById("email").value;

      await passflow.passkeyRegister({
        passkey_display_name: "My Passkey",
        passkey_username: email,
        relying_party_id: window.location.hostname,
        redirect_url: window.location.origin,
        scopes: ["id", "offline", "tenant", "email"],
      });

      alert("Passkey registered successfully!");
    } catch (error) {
      alert(`Passkey registration failed: ${error.message}`);
      console.error(error);
    }
  });

// Handle passkey authentication
document
  .getElementById("login-with-passkey-button")
  .addEventListener("click", async () => {
    try {
      await passflow.passkeyAuthenticate({
        relying_party_id: window.location.hostname,
      });

      alert("Authenticated with passkey successfully!");
    } catch (error) {
      alert(`Passkey authentication failed: ${error.message}`);
      console.error(error);
    }
  });

Multi-tenant Application

import { Passflow, PassflowEvent } from "passflow-js";

// Initialize Passflow
const passflow = new Passflow({
  appId: "your-app-id",
});

// Track current tenant
let currentTenant = null;

// Set up session management
passflow.session({
  createSession: (tokens) => {
    const parsedTokens = passflow.getParsedTokenCache();
    if (parsedTokens?.access_token?.membership?.tenants?.length > 0) {
      currentTenant = parsedTokens.access_token.membership.tenants[0];
      updateTenantUI();
    } else {
      showCreateTenantUI();
    }
  },
  expiredSession: () => {
    currentTenant = null;
    showLoginUI();
  },
  doRefresh: true,
});

// Subscribe to auth events
passflow.subscribe({
  onAuthChange: (eventType) => {
    if (
      eventType === PassflowEvent.SignIn ||
      eventType === PassflowEvent.Register
    ) {
      const parsedTokens = passflow.getParsedTokenCache();
      if (parsedTokens?.access_token?.membership?.tenants?.length > 0) {
        currentTenant = parsedTokens.access_token.membership.tenants[0];
        updateTenantUI();
      } else {
        showCreateTenantUI();
      }
    }
  },
});

// Create tenant function
async function createNewTenant() {
  const tenantName = document.getElementById("tenant-name").value;
  try {
    await passflow.createTenant(tenantName, true);
    const parsedTokens = passflow.getParsedTokenCache();
    currentTenant = parsedTokens.access_token.membership.tenants[0];
    updateTenantUI();
  } catch (error) {
    console.error("Failed to create tenant", error);
    alert(`Failed to create tenant: ${error.message}`);
  }
}

// Invite user function
async function inviteUser() {
  const email = document.getElementById("invite-email").value;
  try {
    const response = await passflow.requestInviteLink({
      email,
      tenant: currentTenant.tenant.id,
      send_to_email: true,
    });
    alert(`Invitation sent to ${email}`);
    console.log("Invitation link:", response.link);
  } catch (error) {
    console.error("Failed to invite user", error);
    alert(`Failed to invite user: ${error.message}`);
  }
}

// UI update functions
function updateTenantUI() {
  document.getElementById("tenant-name-display").textContent =
    currentTenant.tenant.name;
  document.getElementById("tenant-id-display").textContent =
    currentTenant.tenant.id;
  document.getElementById("tenant-section").style.display = "block";
  document.getElementById("create-tenant-section").style.display = "none";
}

function showCreateTenantUI() {
  document.getElementById("tenant-section").style.display = "none";
  document.getElementById("create-tenant-section").style.display = "block";
}

function showLoginUI() {
  document.getElementById("tenant-section").style.display = "none";
  document.getElementById("create-tenant-section").style.display = "none";
  document.getElementById("login-section").style.display = "block";
}

// Event listeners
document
  .getElementById("create-tenant-form")
  .addEventListener("submit", (e) => {
    e.preventDefault();
    createNewTenant();
  });

document.getElementById("invite-form").addEventListener("submit", (e) => {
  e.preventDefault();
  inviteUser();
});