FlawOpen

flawopen.com/AI Security/langgraph-js-custom-auth-cross-user-store-access

● CWE-639 / CWE-863 · CVSS v4 8.6[1] · High
FlawOpen Security Research

LangGraph JS Auth: Cross-User Store Access

langgraph-api <0.15.0 could drop JS/TS custom-auth Store namespace rewrites, enabling cross-user access. Upgrade to 0.15.0.[1], [2]

CVSS vectorCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N[1]
💡 Plain English Explainer (ELI5)

Imagine a librarian who checks your library card, then asks a separate desk to fetch a book. The desk writes your own shelf number on the request, but the handoff silently loses that change and fetches the shelf number the visitor originally supplied. LangGraph’s affected JavaScript/TypeScript auth path could lose the namespace rewrite between its authorization runtime and Store operation. The fix carries the authorized namespace across that boundary and validates it before using the Store.[1]

Core Concepts & Subsystem Terms

GHSA-4hm6-w6qq-w73v
A high-severity LangGraph advisory published September 28, 2026. It has no assigned CVE. The affected package is langgraph-api, fixed in 0.15.0.[1]
Store namespace
A hierarchical name used to group LangGraph Store records. Applications often place a user or tenant identifier in the namespace to separate data.[1]
Custom authorization handler
Application code that decides whether an authenticated request may access a resource and can rewrite the Store namespace before the operation.[1]
CWE-639 / CWE-863
Authorization bypass through a user-controlled key and incorrect authorization. The issue is failure to apply the authorization handler’s namespace restriction.[1], [3], [4]

Framework mappings

  • CWE reference for the weakness described in this guide: CWE-639 CWE-863

Step-by-Step Attack Flow

Step 1

Confirm the deployment matches the advisory

Check langgraph-api is >=0.1.2 and <0.15.0, and the server uses a JavaScript or TypeScript custom-auth handler.[1]

Step 2

Identify the namespace rewrite

The handler changes a requested Store namespace to include or enforce the authenticated user’s identity, instead of rejecting a request outside that namespace.[1]

Step 3

The authorization result crosses a runtime boundary

In affected versions the handler’s namespace change may not be propagated back to the Store operation. The original caller-supplied namespace can remain in use.[1]

Step 4

An authenticated user targets another namespace

A normal account can request Store operations against records outside its own namespace. The advisory lists read, overwrite, search, and delete as possible impacts.[1]

Step 5

Upgrade or deny unauthorized namespaces

Upgrade to langgraph-api>=0.15.0. If that cannot happen immediately, reject unauthorized namespaces in every Store action and verify that the Store sees only the authorized namespace.[1], [2]

Root Cause Analysis

The LangGraph maintainers’ GHSA published September 28, 2026 describes a flaw in langgraph-api versions >=0.1.2 and <0.15.0 when all of the following hold: the deployment uses JavaScript or TypeScript custom authentication; its Store authorization handler rewrites a requested namespace to scope data to the current user; a regular authenticated user can send Store operations; and multiple users’ data flows through that authorization path. The advisory says Python custom-auth handlers are not affected, and JavaScript/TypeScript handlers that reject unauthorized namespace requests instead of rewriting them are not affected. The authorization handler’s namespace change could be lost across a runtime boundary, so the Store operation used the original requested namespace. An authenticated user could then read, overwrite, search, or delete records outside their authorized namespace. This does not bypass login and is not an unauthenticated remote attack. The advisory scores it High, CVSS 4.0 8.6, and assigns CWE-639 and CWE-863. Version 0.15.0 carries the handler’s changes across the boundary, validates the resulting namespace, and rejects malformed or unapplied authorization changes. If upgrade is delayed, the maintainer’s workaround is to reject unauthorized namespace requests rather than rewrite them, for every exposed Store action. The code pair is a reduced TypeScript illustration of the data-flow invariant, not copied SDK source.[1]

Source Code: Flaw vs. Secure Implementation

This reduced TypeScript example illustrates a boundary contract; the function names and result shape are illustrative, not LangGraph SDK source. The advisory establishes that JS/TS authorization changes could be lost before the Store operation, but does not publish this exact internal implementation. The vulnerable side therefore models only the documented invariant: the Store uses the request namespace instead of the authorized namespace. The fixed side consumes and validates the authorized result. The maintainer’s documented workaround is to reject unauthorized namespaces rather than rewrite them, for every Store action.[1]

Source excerpt: LangGraph API authorization-to-Store boundary — reduced illustrative bridge contract

UNPATCHED FLAW
type StoreRequest = { namespace: string[]; key: string };
type AuthorizationResult = { allowed: boolean; namespace: string[] };

async function readStore(userId: string, request: StoreRequest) {
  const authorization = await authorizeStore(userId, request); // Illustrative bridge contract.
  return store.get(request.namespace, request.key); // The bridge drops the authorized result.
}
HARDENED SECURE PATCH
type StoreRequest = { namespace: string[]; key: string };
type AuthorizationResult = { allowed: boolean; namespace: string[] };

async function readStore(userId: string, request: StoreRequest) {
  const authorization = await authorizeStore(userId, request); // Illustrative bridge contract.
  if (!authorization.allowed) throw new Error("forbidden");
  const namespace = validateAuthorizedNamespace(userId, authorization.namespace);
  return store.get(namespace, request.key); // Apply the authorized result.
}

Engineering & System Hardening Checklist

Sources

← AI Security Hub Directory →