Auth & Security

The GECX Chat SDK enforces a strict browser/server boundary. Credentials never touch browser code.

The browser/server boundary

The SDK runs in the browser. Your secrets (API keys, service account credentials, GCP access tokens) live on your server. The browser gets a short-lived, narrowly-scoped chat token from your server -- never the underlying credentials.

Auth providers

The SDK ships four AuthProvider implementations:

ProviderImportUse case
Mock (default)Built-inLocal development. No credentials needed.
tokenEndpointAuthgecx-chatProduction. Polls a server endpoint you control for tokens.
customAuthgecx-chatWrap any async function that returns a token string.
googleTokenBrokerAuthgecx-chatServer-side only. Exchanges a GCP access token via the CES token broker.

If you pass no auth to createChatClient(), the SDK uses a mock provider that issues non-cryptographic placeholder tokens. This is fine for local dev with the mock transport, but will be rejected by any real backend.

The recommended production pattern uses tokenEndpointAuth on the browser side and createChatTokenHandler on the server side:

Browser                          Your Server                   Google CES
  |                                  |                              |
  |-- POST /api/chat-token --------->|                              |
  |                                  |-- generateChatToken -------->|
  |                                  |<-- { chatToken, expiry } ----|
  |<-- { token, expiresAt } ---------|                              |
  |                                                                 |
  |-- chat messages (Bearer token) -------------------------------->|

The browser never sees your GCP credentials. The token auto-refreshes before expiry.

Setting up a token route

On your server (e.g. a Next.js API route):

// app/api/chat-token/route.ts
import { createChatTokenHandler } from 'gecx-chat/server';

const handler = createChatTokenHandler({
  allowedOrigins: ['https://your-app.com'],
  issueToken: async ({ request }) => {
    // Exchange your server-side credentials with Google here
    const chatToken = await yourCredentialExchange();
    return { token: chatToken, tokenType: 'Bearer' };
  },
});

export async function POST(request: Request) {
  return handler(request);
}

In the browser:

import { createChatClient, tokenEndpointAuth } from 'gecx-chat';

const client = createChatClient({
  auth: tokenEndpointAuth({ endpoint: '/api/chat-token' }),
});

Security rules

The SDK applies these security defaults:

  • No secrets in browser code. Auth providers that need GCP credentials (googleTokenBrokerAuth) are designed to run server-side only.
  • Token values are redacted from debug bundles and diagnostic traces.
  • Rich content is rendered inertly. URLs are checked against an allowlist. No embedded scripts execute.
  • HTML rendering is disabled by default. Enable it explicitly via capabilities if your deployment needs it.
  • Unknown tool calls fail closed. If the AI requests a tool that is not registered, the SDK returns an error to the AI rather than silently ignoring it.
  • Auth prefetch state is released on shutdown. createChatClient.shutdown() calls auth.clear?.() in try/catch so background token refresh timers and cached bearer tokens do not leak across teardown.
  • Cross-tab signed-out cache. IdentityManager.signOut() broadcasts identity.signed_out. The ConversationRegistry resets on this event so sibling tabs cannot surface a previous identity's cached conversations after a fresh sign-in.

Device permissions (microphone, camera, screen, geolocation) are orthogonal to API auth. They live in PermissionManager (always present on ChatClient) and synchronize one-directionally into ChatGovernance.consent — every grant emits a governance.permission_granted audit event and auto-grants the matching ConsentFlag; revokes emit permission_revoked and withdraw the flag.

A user can hold full analytics consent and still need to grant microphone access explicitly the first time voice is used. The two systems do not collapse into one another. See Permissions.

Computer-use boundary

The computer_use server tool lets an agent drive a sandboxed browser on the user's behalf. The security boundary is the vendor's browser sandbox (Browserbase or similar) — not the iframe in the host page. The iframe is a display sandbox (sandbox="", no scripts, no same-origin); the real isolation lives in the hosted Chromium environment.

The SDK applies its allowlist as a fast-fail UX. The proxy reapplies every check independently and is the authoritative enforcement point. Never widen the allowlist at the SDK to "fix" an error — the proxy will reject it again. See Computer-use Threat Model for the full threat model and the penetration-test runbook.

What's next

Source: docs/concepts/auth-and-security.md