DeltaKitDeltaKit
OpenAI Agents

Secure Your Chat API

Recipe for adding authentication with JWT tokens, handling 401 errors, and validating requests server-side.

This recipe shows how to secure your chat API with authentication. You'll learn how to pass tokens from the client, handle unauthorized errors, and validate requests on the server.

Problem

You need to:

  • Authenticate users before allowing chat access
  • Pass tokens securely from React to your API
  • Handle token expiration and refresh
  • Validate tokens on the server

Solution Overview

Use the headers option in useStreamChat to send authentication tokens. Handle 401 errors with onError. Validate tokens server-side before processing requests.

Bearer Token

The most common pattern is sending a JWT or API key via the Authorization header:

import { useStreamChat } from "@deltakit/react";

function Chat({ token }: { token: string }) {
  const { messages, sendMessage } = useStreamChat({
    api: "/api/chat",
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  return (/* render messages */);
}

Token from Auth Context

If you use a React auth provider (Auth0, Clerk, Supabase, custom), pull the token from context:

import { useStreamChat } from "@deltakit/react";
import { useAuth } from "./auth-context";

function Chat() {
  const { token } = useAuth();

  const { messages, sendMessage } = useStreamChat({
    api: "/api/chat",
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  return (/* render messages */);
}

The hook reads headers on each sendMessage call, so token refreshes are picked up automatically.

Handling 401 Errors

Use onError to detect unauthorized responses and redirect or refresh:

import { useStreamChat } from "@deltakit/react";
import { useAuth } from "./auth-context";

function Chat() {
  const { token, refreshToken, logout } = useAuth();

  const { messages, sendMessage, error } = useStreamChat({
    api: "/api/chat",
    headers: {
      Authorization: `Bearer ${token}`,
    },
    onError: (error) => {
      if (error.message.includes("401")) {
        // Try refreshing the token, or redirect to login
        refreshToken().catch(() => logout());
      }
    },
  });

  return (/* render messages */);
}

API Key Authentication

For simpler setups with API keys:

useStreamChat({
  api: "/api/chat",
  headers: {
    "X-API-Key": import.meta.env.VITE_API_KEY,
  },
});

Server-Side Verification

Your backend should validate the token on every request:

# FastAPI example
from fastapi import Request, HTTPException

@app.post("/api/chat")
async def chat(request: Request):
    auth = request.headers.get("Authorization")
    if not auth or not auth.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Unauthorized")

    token = auth.split(" ")[1]
    user = verify_token(token)  # Your verification logic
    # ... proceed with chat

On this page