@passflow/passflow-js-sdk
TypeScript icon, indicating that this package has built-in type declarations

0.1.43 • Public • Published

Passflow JavaScript SDK Documentation

Changelog

Version 0.1.42

  • 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: "user@example.com",
      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: "user@example.com",
        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: "user@example.com",
      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: "user@example.com",
    });
    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: "user@example.com",
      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: "user@example.com",
        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: "user@example.com",
      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: "user@example.com",
      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: "user@example.com",
      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: "user@example.com",
      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: "newuser@example.com",
      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: "user@example.com",
    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();
});

Readme

Keywords

none

Package Sidebar

Install

npm i @passflow/passflow-js-sdk

Weekly Downloads

122

Version

0.1.43

License

MIT

Unpacked Size

669 kB

Total Files

68

Last publish

Collaborators

  • ivan_holiak
  • jackrudenko