Authdog

Custom channel auth for LangGraph

Last updated Oct 21, 2026
View as Markdown

LangGraph compiles a graph. It does not authenticate the HTTP caller. The auth boundary is the route that invokes the graph. require_auth runs before ainvoke. The graph reads the subject from state. It does not read the Authorization header.

Getting started, including the runnable sample, is LangGraph.

The route is the hook

authdog = Authdog(public_key=public_key)
compiled = build_graph()

@app.post("/invoke")
async def invoke(user=Depends(authdog.require_auth)):
    result = await compiled.ainvoke(
        {"subject": subject_of(user), "reply": ""}
    )
    return {"reply": result["reply"]}

Authdog comes from authdog.fastapi. require_auth is a FastAPI dependency, so a missing or rejected token never enters invoke. subject_of reads id, then sub, from the userinfo user.

Call graph.invoke or ainvoke only from a route that already depends on require_auth. A second route that invokes the compiled graph directly skips Authdog. This sample has one route.

The process that serves the graph holds PK_AUTHDOG. It does not hold AUTHDOG_CLIENT_SECRET. The caller mints the bearer elsewhere. See API keys and M2M for LangGraph.

What you should see

POST /invoke with no token returns 401. The body does not contain subject:. The same call with a bearer userinfo accepts returns {"reply":"subject:<id>"}.

Read To learn how to
LangGraph Run the sample
API keys and M2M for LangGraph Mint the bearer the route expects
Authorizing tool calls for LangGraph Stop a node the subject may not run
Enriching instructions for LangGraph Pass the subject in state

Learn more