@passflow/passflow-js-sdk v0.1.45
Passflow JavaScript SDK Documentation
Changelog
Version 0.1.45
- Fixed bug related to null/undefined checks in the codebase
Table of Contents
- Passflow JavaScript SDK Documentation
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-jsGetting 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 IDscopes: 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 managementUserService: Handles user-related operationsTenantService: Handles tenant operationsInvitationService: Handles invitation operationsDeviceService: Manages device identificationTokenService: Handles token parsing and validationStorageManager: 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();
});6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
11 months ago
11 months ago