> ## 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.

# Authentication

> Handle OAuth sign-in, callbacks, and session management in the client SDK.

## Auth API

The `client.auth` namespace manages the OAuth PKCE flow. Here is the full interface:

```typescript theme={"system"}
client.auth.signIn();                    // Start OAuth flow
client.auth.handleCallback(url);         // Complete the flow with the callback URL
client.auth.isCallback(url);             // Check if a URL is an OAuth callback
client.auth.signOut();                   // Clear tokens and disconnect
client.auth.isAuthenticated;             // true when the client is ready or connected
```

## How it works

1. You call `client.connect()` (or `client.auth.signIn()`).
2. If the user has no valid tokens, the client fires `onAuthRequired` with the OAuth URL.
3. The user authorizes in the browser. The OAuth server redirects to your `redirectUri`.
4. You pass the callback URL to `client.auth.handleCallback()` to exchange the code for tokens.
5. The client reconnects and transitions to `ready`.

Most of this is handled for you. Your job is to implement `onAuthRequired` and route the callback URL back to the client.

## The onAuthRequired callback

This callback controls how your application opens the OAuth page. It receives a `URL` and can return in three ways:

* **Return the callback URL** (string or URL): The client completes the flow inline, no separate `handleCallback()` call needed.
* **Return void**: You handle the callback yourself by calling `client.auth.handleCallback()` later.
* **Return a Promise**: Same options, but async.

## Browser redirect

The most common pattern for web applications. The user leaves the page, authorizes, and comes back to your redirect URI.

<Note>
  Use persistent storage (for example `localStorage`) for browser redirect flows.
  `MemoryStorage` resets on reload, which can drop PKCE state before the callback
  is handled. See [Storage](/client/storage).
</Note>

```typescript theme={"system"}
import { createKontextClient } from "@kontext-dev/js-sdk/client";

const client = createKontextClient({
  clientId: "your-client-id",
  redirectUri: window.location.origin + "/callback",
  storage: myPersistentStorage, // implements KontextStorage
  onAuthRequired: (url) => {
    window.location.href = url.toString();
  },
});
```

On your callback page, detect and handle the redirect:

```typescript theme={"system"}
// /callback route handler
if (client.auth.isCallback(window.location.href)) {
  await client.auth.handleCallback(window.location.href);
}
```

## Popup window

Open OAuth in a popup and listen for the callback. The main page stays loaded.

```typescript theme={"system"}
const client = createKontextClient({
  clientId: "your-client-id",
  redirectUri: window.location.origin + "/callback",
  onAuthRequired: (url) => {
    const popup = window.open(url.toString(), "kontext-auth", "width=500,height=600");

    // Poll for the callback URL
    const interval = setInterval(async () => {
      try {
        const popupUrl = popup?.location.href;
        if (popupUrl && client.auth.isCallback(popupUrl)) {
          clearInterval(interval);
          await client.auth.handleCallback(popupUrl);
          popup?.close();
        }
      } catch {
        // Cross-origin errors are expected while on the OAuth page
      }
    }, 500);
  },
});
```

Your callback page should include a script that signals the parent window, or you can rely on the polling approach shown above.

## CLI / Node.js

For command-line tools, open the browser and start a local HTTP server to catch the callback.

```typescript theme={"system"}
import http from "node:http";
import open from "open";

const CALLBACK_PORT = 3333;
const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/callback`;

function waitForCallback(): Promise<string> {
  return new Promise((resolve, reject) => {
    const server = http.createServer((req, res) => {
      const url = new URL(req.url ?? "/", `http://localhost:${CALLBACK_PORT}`);
      if (url.pathname === "/callback") {
        res.writeHead(200, { "Content-Type": "text/html" });
        res.end("<h1>Authenticated. You can close this tab.</h1>");
        server.close();
        resolve(url.toString());
      }
    });
    server.listen(CALLBACK_PORT);
    server.on("error", reject);
  });
}

const client = createKontextClient({
  clientId: "your-client-id",
  redirectUri: REDIRECT_URI,
  onAuthRequired: async (url) => {
    await open(url.toString());
    return await waitForCallback();
  },
});

await client.connect();
// Client is now authenticated and ready
```

The key detail: `onAuthRequired` returns the callback URL as a string. The client exchanges the authorization code for tokens internally, so you do not need to call `handleCallback()` separately.

## Sign out

`client.auth.signOut()` clears stored tokens and moves the client back to the `idle` state. The next `connect()` call will trigger `onAuthRequired` again.

```typescript theme={"system"}
await client.auth.signOut();
// client.state is now "idle"
// client.auth.isAuthenticated is now false
```

## Check authentication status

`client.auth.isAuthenticated` returns `true` when the client is in the `ready` state or has an active MCP connection. Use this to conditionally render sign-in UI.

```typescript theme={"system"}
if (!client.auth.isAuthenticated) {
  showSignInButton();
}
```

## Next steps

* [Storage](/client/storage) -- Persist tokens across page reloads and process restarts.
* [Tools](/client/tools) -- Discover and execute MCP tools.
* [Client Types](/sdks/typescript/client) -- Full type reference for `KontextClient` and auth interfaces.
