Skip to main content

Auth API

The client.auth namespace manages the OAuth PKCE flow. Here is the full interface:
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.
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.
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:
// /callback route handler
if (client.auth.isCallback(window.location.href)) {
  await client.auth.handleCallback(window.location.href);
}
Open OAuth in a popup and listen for the callback. The main page stays loaded.
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.
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.
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.
if (!client.auth.isAuthenticated) {
  showSignInButton();
}

Next steps

  • Storage — Persist tokens across page reloads and process restarts.
  • Tools — Discover and execute MCP tools.
  • Client Types — Full type reference for KontextClient and auth interfaces.