# Sessions & Tokens

Session duration, persistence, and automatic permission refresh.

Sessions are managed automatically by the SDK.

| Behavior         | Detail                                                                                                                        |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Session duration | 30 days by default — configurable per session, capped at 180 days    |
| Permission sync  | hourly — role changes from the dashboard update automatically |
| Persistence      | Store the session ID in a cookie or localStorage                                                                              |

## Session storage callbacks

Your `SaaSOSProvider` auth config takes three callbacks — nested under `auth.callbacks` — to persist the session. See the [Installation](/quick-start/installation) page for the full provider setup.

```tsx
// In your SaaSOSProvider auth config
auth={{
  clientId: 'your-client-id',
  redirectUrl: '/callback',
  callbacks: {
    getSession: async () => localStorage.getItem('sessionId'),
    handleAuthentication: async (code) => {
      const res = await fetch('/api/auth/verify', {
        method: 'POST',
        body: JSON.stringify({ code }),
      });
      const data = await res.json();
      return { sessionId: data.sessionId };
    },
    onSignOut: async () => localStorage.removeItem('sessionId'),
  },
}}
```

The three callbacks:

- `getSession` — Return the stored session ID (from a cookie or your backend)
- `handleAuthentication` — Called after sign-in with the auth code. Exchange it for a session on your backend.
- `onSignOut` — Clear the stored session

## Where to store the session ID

You choose how to persist the session ID. Each option has trade-offs:

| Approach                          | Security                                                | Setup                                      | Best for                                                    |
| --------------------------------- | ------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------- |
| **HttpOnly cookie** (recommended) | Immune to XSS — JavaScript cannot read it               | Requires a backend route to set the cookie | SPAs with a Node.js backend                                 |
| **localStorage**                  | Vulnerable to XSS — any script on the page can read it  | One line — `localStorage.setItem()`        | Quick prototypes, internal tools                            |
| **Hybrid**                        | HttpOnly cookie for session + localStorage for UI state | More complex                               | Production apps needing both security and client-side state |

> **Note:**
  If your app handles sensitive data (billing, PII), use HttpOnly cookies.
  localStorage is convenient for prototypes but exposes the session to
  cross-site scripting attacks.


## Devices and active sessions

The SDK tracks the devices a user signs in from and the live sessions on each. Users can review their devices, rename them, and sign out individual sessions or entire devices.

**Drop-in components** — `` lists live sessions with a revoke action; `` lists devices with rename, sign-out, and remove actions. Both are session-authed and need no config beyond `SaaSOSProvider`:

```tsx
import { Devices, Sessions } from '@buildbase/sdk/react';

function SecurityPage() {
  return (
    <>
      
      
    </>
  );
}
```

**Headless hooks** — `useSessions()` and `useDevices()` return the same data plus actions for a custom UI:

```tsx
import { useSessions } from '@buildbase/sdk/react';

function ActiveSessions() {
  const { sessions, loading, revoke } = useSessions();

  if (loading) return <p>Loading…</p>;

  return (
    <ul>
      {sessions.map((s) => (
        <li key={s.id}>
          {s.device?.name ?? s.userAgent} — {s.ip}
          {!s.current && <button onClick={() => revoke(s.id)}>Sign out</button>}
        </li>
      ))}
    </ul>
  );
}
```

**Direct API access** — `useSessionsApi()` and `useDevicesApi()` return memoized client instances:

| Client        | Methods                                                                                       |
| ------------- | --------------------------------------------------------------------------------------------- |
| `SessionsApi` | `list()` → `ISessionView[]`, `revoke(id)`                                                     |
| `DevicesApi`  | `list()` → `IDeviceView[]`, `rename(deviceId, name)`, `signOut(deviceId)`, `forget(deviceId)` |

`signOut(deviceId)` revokes every live session on a device but keeps it in the list as signed out; `forget(deviceId)` also removes the device entirely. `revoke(id)` takes the session's public handle (`ISessionView.id`), not the raw session ID.

The built-in settings dialog includes `security` and `devices` screens — open them with `openWorkspaceSettings('security')` or `openWorkspaceSettings('devices')` from `useSaaSAuth()`.

## Common questions

**Role changes not taking effect?** Permissions refresh hourly. Sign out and back in for immediate effect.

**Session lost on refresh?** Make sure your `getSession` callback reads from persistent storage, not memory.

**Session expired?** Sessions last 30 days by default and can be configured to last up to 180 days. Trusted devices and the org session policy also affect expiry. When a session expires, the user must sign in again.

## Next Steps

- [Protected Routes](/authentication/protected-routes) — Guard routes based on auth state.
- [Workspaces Overview](/workspaces/overview) — Multi-tenant workspaces.
