Bold VideoDocs

Session Management

Device limits and password-sharing prevention: headless, on top of your auth

Bold's session management is a headless password-sharing prevention layer. Your app keeps owning login, membership, and access decisions; Bold tracks device sessions per viewer, enforces device limits, and flags suspicious sign-ins. Call Bold only after your app has decided the viewer may access protected content.

There are two halves, with different credentials (see the three auth layers):

HalfRuns whereCredential
Runtime sessionsBrowser / native appYour auth provider's JWT (safe for client code)
Server-side managementYour backendYour Bold API key (never in client code)

Runtime sessions (client-side)

Create an auth client with your tenant slug and the member's upstream JWT (from Outseta, Auth0, Clerk, …):

import { createAuthClient } from '@boldvideo/bold-js';

const auth = createAuthClient({
  tenantSlug: 'your-channel',
  upstreamJwt: memberJwt
});

Register the device when the member starts watching:

const result = await auth.sessions.create({
  deviceId: browserDeviceId,
  platform: 'web',
  userAgent: navigator.userAgent
});

let sessionId: string | undefined;

if ('challengeRequired' in result) {
  // Device limit hit or suspicious sign-in: Bold sent an email code.
  // Show a challenge UI, then:
  const verified = await auth.challenges.verify(result.challengeId, codeFromUser);
  sessionId = verified.sessionId ?? undefined;
} else if (result.sessionId) {
  sessionId = result.sessionId;
}

Verify the session before playing protected content:

const verification = await auth.sessions.verify(sessionId);
if (!verification.valid) {
  // session_not_found | session_revoked | session_expired
}

Members can manage their own devices (where policy allows):

const { data: sessions } = await auth.sessions.list();
await auth.sessions.revoke(oldSessionId);
await auth.sessions.revokeOthers(currentSessionId);

If your provider refreshes JWTs, pass the fresh one per call: auth.sessions.verify(id, { upstreamJwt: freshJwt }).

Server-side management (backend)

With your normal API key, your backend can inspect and control any viewer's sessions by your user ID:

import { createClient } from '@boldvideo/bold-js';
const bold = createClient(process.env.BOLD_API_KEY!);

// Where does this member stand?
const { data: state } =
  await bold.sessionManagement.getViewerSessionManagementStateByExternalId(userId);

// Raise a VIP's device limit, or clear the override
await bold.sessionManagement.setViewerDeviceLimitOverrideByExternalId(userId, 5);
await bold.sessionManagement.clearViewerDeviceLimitOverrideByExternalId(userId);

// Exempt a staff account entirely
await bold.sessionManagement.setViewerSessionManagementExemptionByExternalId(userId, true);

// Inspect sessions
const { data: sessions } =
  await bold.sessionManagement.listViewerSessionsByExternalId(userId);

for (const session of sessions) {
  if (session.travelVerdict === 'impossible_travel') {
    // Bold flagged this sign-in as physically implausible
  }
}

// Membership canceled → kick all devices
await bold.sessionManagement.revokeAllViewerSessionsByExternalId(userId, {
  reason: 'membership_changed'
});

travelVerdict is a label only ('impossible_travel' | null). Bold never exposes coordinates, IPs, or distances.

Plan availability

Session management / password-sharing controls are geared toward paid communities. Check your plan or talk to us about enabling them.

Reference

REST equivalents: Auth Sessions, Auth Challenges, Session Management, and the Admin endpoints.

On this page