@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();
});7 months ago
7 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
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 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
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
12 months ago
12 months ago
1 year ago
1 year ago