Skip to content

Session Management

Pulsabase acts as a fully-featured Identity Provider (IDP) implementing the OAuth 2.0 / OpenID Connect (OIDC) protocols. When a user authenticates, the SDK stores a short-lived access_token and a longer-lived refresh_token.

By default, the SDK automatically handles token refreshing in the background. If a request is made within the configured leeway before the access_token expires, the SDK transparently requests a new token pair using the refresh_token.

import { PulsaClient } from '@pulsabase/sdk';
const pb = new PulsaClient({
engineUrl: 'https://api.yourproject.pulsabase.io',
apiKey: 'pk_live_your_api_key',
auth: { clientId: 'your-client-id' },
autoRefreshToken: true, // Default: true
tokenRefreshLeewaySeconds: 60, // Default: 60 seconds before expiry
});
// You can also manually trigger a refresh
const tokens = await pb.auth.refreshSession();

Fetch the current user’s profile from the OIDC /userinfo endpoint. This makes a network request.

const user = await pb.auth.getUser();
console.log(`Hello, ${user.given_name} ${user.family_name}`);
console.log(`Email: ${user.email}`);
console.log(`Email verified: ${user.email_verified}`);
console.log(`Roles: ${user.roles?.join(', ')}`);

Because Pulsabase uses standard JWTs, the SDK provides helper methods to inspect claims inside the access_token without a network request. This is extremely fast and ideal for conditional UI rendering.

// Check authentication status
if (pb.auth.isAuthenticated()) {
// Check a single role
if (pb.auth.hasRole('admin')) {
showAdminPanel();
}
// Check if the user has ANY of these roles
if (pb.auth.hasAnyRole('admin', 'moderator')) {
showModerationTools();
}
// Check a specific permission
if (pb.auth.hasPermission('invoices:delete')) {
enableDeleteButton();
}
// Check if the user has ALL of these permissions
if (pb.auth.hasAllPermissions('billing:read', 'billing:write')) {
showBillingSection();
}
// Get all roles and permissions
const roles = pb.auth.getRoles(); // string[]
const permissions = pb.auth.getPermissions(); // string[]
}

[!IMPORTANT] JWT claims (roles, permissions) are only updated when a token is refreshed or the user signs in again. If you assign a new role to a user, they must sign in or their token must refresh before the change is reflected in hasRole().

// Is the user currently signed in?
if (pb.auth.isAuthenticated()) {
// Show authenticated UI
} else {
// Show login screen
}
// Get the raw tokens (may be expired)
const tokens = pb.auth.getTokens();
// Get the cached user profile (no network call — null if getUser() hasn't been called)
const cachedUser = pb.auth.getCachedUser();

If you are building a B2B SaaS where multiple organizations (tenants) share your single Pulsabase Project, use Sub-Tenants. Specify a sub-tenant ID when initializing the client for a given organization.

const pb = new PulsaClient({
engineUrl: 'https://api.yourproject.pulsabase.io',
apiKey: 'pk_live_your_api_key',
auth: {
clientId: 'your-client-id',
},
subTenantId: 'org_acme_12345', // The organization's ID
});

When a subTenantId is provided:

  1. It is passed via the X-Sub-Tenant-ID header during authentication
  2. It is automatically embedded into the resulting JWT claims
  3. Your Row-Level Security policies can restrict data access to that specific organization’s rows
// Signs the user out, clears session tokens, and closes the WebSocket connection
await pb.auth.signOut();

When your application unmounts (e.g. a SPA route change or app shutdown):

// Closes the WebSocket connection and removes all listeners
pb.destroy();