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.
Automatic Token Refresh
Section titled “Automatic Token Refresh”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 refreshconst tokens = await pb.auth.refreshSession();import 'package:pulsabase/pulsabase.dart';
final pb = PulsaClient(PulsabaseClientOptions( engineUrl: 'https://api.yourproject.pulsabase.io', apiKey: 'pk_live_your_api_key', clientId: 'your-client-id', autoRefreshToken: true, tokenRefreshLeewaySeconds: 60,));
// Manual refreshfinal tokens = await pb.auth.refreshSession();Accessing User Information
Section titled “Accessing User Information”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(', ')}`);final user = await pb.auth.getUser();
print('Hello, ${user.givenName} ${user.familyName}');print('Email verified: ${user.emailVerified}');Reading Claims Locally (Without Network)
Section titled “Reading Claims Locally (Without Network)”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 statusif (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[]}if (pb.auth.isAuthenticated) {
if (pb.auth.hasRole('admin')) { showAdminPanel(); }
if (pb.auth.hasPermission('invoices:delete')) { enableDeleteButton(); }}[!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().
Checking Authentication State
Section titled “Checking Authentication State”// 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();Sub-Tenants (B2B SaaS)
Section titled “Sub-Tenants (B2B SaaS)”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});final pb = PulsaClient(PulsabaseClientOptions( engineUrl: 'https://api.yourproject.pulsabase.io', apiKey: 'pk_live_your_api_key', clientId: 'your-client-id', subTenantId: 'org_acme_12345',));When a subTenantId is provided:
- It is passed via the
X-Sub-Tenant-IDheader during authentication - It is automatically embedded into the resulting JWT claims
- Your Row-Level Security policies can restrict data access to that specific organization’s rows
Sign Out
Section titled “Sign Out”// Signs the user out, clears session tokens, and closes the WebSocket connectionawait pb.auth.signOut();Cleaning Up
Section titled “Cleaning Up”When your application unmounts (e.g. a SPA route change or app shutdown):
// Closes the WebSocket connection and removes all listenerspb.destroy();