> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.security/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage

> Persist tokens across sessions with custom storage backends.

## Default behavior

The client uses `MemoryStorage` by default. Tokens live in memory and are lost when the page reloads or the process exits. This works for quick prototypes and single-session CLI tools, but most production applications need persistent storage.

## KontextStorage interface

Any object that implements `getJson` and `setJson` works as a storage backend:

```typescript theme={"system"}
interface KontextStorage {
  getJson<T>(key: string): Promise<T | undefined>;
  setJson<T>(key: string, value: T | undefined): Promise<void>;
}
```

Pass `undefined` as the value to delete a key.

## localStorage adapter

For browser applications where tokens should survive page reloads:

```typescript theme={"system"}
const localStorageAdapter: KontextStorage = {
  async getJson<T>(key: string): Promise<T | undefined> {
    const raw = localStorage.getItem(key);
    return raw ? JSON.parse(raw) : undefined;
  },
  async setJson<T>(key: string, value: T | undefined): Promise<void> {
    if (value === undefined) {
      localStorage.removeItem(key);
    } else {
      localStorage.setItem(key, JSON.stringify(value));
    }
  },
};

const client = createKontextClient({
  clientId: "your-client-id",
  redirectUri: "http://localhost:3000/callback",
  storage: localStorageAdapter,
  onAuthRequired: (url) => {
    window.location.href = url.toString();
  },
});
```

## sessionStorage adapter

Same pattern, scoped to the browser tab. Tokens are cleared when the tab closes:

```typescript theme={"system"}
const sessionStorageAdapter: KontextStorage = {
  async getJson<T>(key: string): Promise<T | undefined> {
    const raw = sessionStorage.getItem(key);
    return raw ? JSON.parse(raw) : undefined;
  },
  async setJson<T>(key: string, value: T | undefined): Promise<void> {
    if (value === undefined) {
      sessionStorage.removeItem(key);
    } else {
      sessionStorage.setItem(key, JSON.stringify(value));
    }
  },
};
```

## Database-backed storage

For server-side applications that manage multiple users, store tokens in a database. This example uses a generic key-value table:

```typescript theme={"system"}
import { db } from "./db";

function createDbStorage(userId: string): KontextStorage {
  return {
    async getJson<T>(key: string): Promise<T | undefined> {
      const row = await db.kvStore.findUnique({
        where: { userId_key: { userId, key } },
      });
      return row?.value as T | undefined;
    },
    async setJson<T>(key: string, value: T | undefined): Promise<void> {
      if (value === undefined) {
        await db.kvStore.delete({
          where: { userId_key: { userId, key } },
        });
      } else {
        await db.kvStore.upsert({
          where: { userId_key: { userId, key } },
          create: { userId, key, value: value as any },
          update: { value: value as any },
        });
      }
    },
  };
}

const client = createKontextClient({
  clientId: "your-client-id",
  redirectUri: "https://myapp.com/callback",
  storage: createDbStorage(currentUser.id),
  onAuthRequired: (url) => {
    // server-side redirect
  },
});
```

## Session keys

The `sessionKey` config option namespaces all storage keys. Use it when multiple users share the same storage backend but you want to keep their tokens separate.

```typescript theme={"system"}
const client = createKontextClient({
  clientId: "your-client-id",
  redirectUri: "https://myapp.com/callback",
  storage: localStorageAdapter,
  sessionKey: currentUser.id,
  onAuthRequired: (url) => {
    window.location.href = url.toString();
  },
});
```

With `sessionKey` set to `"user-123"`, storage keys look like `kontext:your-client-id:user-123:tokens`. Without an explicit `sessionKey`, the SDK defaults to `"default"`, producing keys like `kontext:your-client-id:default:tokens`.

In hybrid mode (the default when no `url` is provided), the SDK appends a suffix per connection:

* Gateway: `kontext:your-client-id:default:gateway:tokens`
* Internal integrations: `kontext:your-client-id:default:internal:<integrationId>:tokens`

In single-endpoint mode (when `url` is set), the base format above is used as-is.

This prevents token collisions when switching between users on the same device.

## When to use custom storage

* **Browser SPA**: Use `localStorage` so users stay signed in across page reloads.
* **Browser with multiple accounts**: Use `localStorage` + `sessionKey` to isolate per user.
* **Server-side with multiple users**: Use database storage with a user-scoped factory.
* **CLI tool**: Default `MemoryStorage` is fine for single-session use. For persistent CLI auth, write to a file in the user's config directory.
* **Testing**: Default `MemoryStorage` works. Each test gets a fresh instance.

## Next steps

* [Authentication](/client/authentication) -- OAuth flows that use your storage backend.
* [Client Types](/sdks/typescript/client) -- Full `KontextStorage` interface reference.
