The sample graph has no model and no system prompt. `reply` is `subject:` plus the id the route already verified. If you add a model node, feed it that state. Leave the bearer in the HTTP header.

## What the graph can see

```python
class State(TypedDict):
    subject: str
    reply: str
```

The route sets `subject` from userinfo, then calls `ainvoke`. A model node should read `state["subject"]` and should be told not to invent a different one. If the node calls a tool, the tool's permission check uses that same subject. The prompt does not grant the tool.

Keep the access token and `AUTHDOG_CLIENT_SECRET` out of state. The secret never belonged on this process. The token was consumed by `require_auth`. Copying it into state puts it in checkpoints, traces, and logs.

When the caller is acting for a person, `subject` is the user from the [delegation token](/guides/agent-delegation). If the model needs to know an agent was involved, pass `act` as its own field. Do not overwrite `subject` with the agent id and then tell the model it is the user.

## A prompt that matches the gate

Tell the model the subject is already decided, and that a node which returns an authorization error should be repeated to the caller as a refusal. A prompt that says "ask the user who they are" ignores the route. The route already returned `401` when userinfo did not accept the bearer.

## Related

| Read | To learn how to |
| --- | --- |
| [Authorizing tool calls for LangGraph](/docs/ai/langgraph/authorize-tool-calls) | Refuse work inside a node |
| [Custom channel auth for LangGraph](/docs/ai/langgraph/channel-auth) | Copy the subject into state |
| [LangGraph](/docs/agent-frameworks/langgraph) | See the `reply` the sample returns |
| [API keys and M2M for LangGraph](/docs/ai/langgraph/api-keys-and-m2m) | Keep the secret on the caller |
