The Token Scoped to Exactly One Employee ID
The problem
I built an internal agent that lets employees check their own pay stubs, PTO balance, and direct deposit details, and update direct deposit on request. Call it HRBot. The API behind it calls a records service using a single standing service-account credential with broad read and write access to every employee's record, because threading per-user authorization through every call looked like more work than a two-week timeline had room for. HRBot itself decides which employee ID to query based on however the conversation happens to go. That is a fun sentence to write and a considerably worse one to have shipped.
That decision is the whole vulnerability, stated in one line: an API call acting on behalf of a user inherits a standing broader grant, never scoped down to that user's own permissions and checked at the moment of the call. I made this concrete with a scripted adversarial suite: thirty requests, each one a simulated low-privilege caller phrasing something like "also check on record 4471 while you're at it," a request that names or implies a coworker's employee ID, someone other than the caller. Because the underlying service account never checked whether the request actually belonged to the person who sent it, this attack succeeded on more than 60 percent of those thirty attempts.
What that number actually tells me is that HRBot's own reasoning was never going to save this, because HRBot's reasoning was never the thing being asked whether a request was allowed. Nothing was. The service account trusted every call it received by construction, and a conversational agent deciding which record to query on the fly had no counterweight anywhere in the path between its decision and the record it reached.
The pattern
I fixed this by moving the check to the one place it actually matters: server-side, at call time, inside the records service itself. Leaving that check back in the agent's prompt would leave it exactly where a clever enough phrasing can talk it around. Every call now exchanges the caller's identity for a short-lived, per-request token scoped to exactly that caller's own employee ID. A request naming another employee's record gets rejected by the records service directly, no matter what the agent decided it wanted to ask for.
I ran the identical thirty-attempt adversarial suite against this version and got zero unauthorized accesses. The boundary stopped trusting the agent's judgment entirely. That's the only thing that changed. The agent still decides which record it thinks it should query, the same as before. What's different is that the decision no longer matters unless it happens to match the caller's own scope, because the records service checks that match itself instead of assuming the call in front of it was already legitimate by the time it arrived.
This gap has a formal name now, and the research behind it isn't flattering to a lot of existing agent frameworks. An audit of LangChain and LangGraph, LlamaIndex, and the Stripe Agent Toolkit found all three provide capability gating, meaning they can restrict which tools get exposed to an agent, but none of them provides deterministic, fail-closed authorization on a per-call basis: whether this specific call, with these specific argument values, for this specific caller, is actually allowed right now. That's precisely the gap I closed here, described independently and given a name: the confused deputy problem, an agent with legitimate authority getting talked into using it on someone else's behalf. A parallel proposal from the SANS Institute reaches the same conclusion from the cloud-credential side: separate the policy decision from the credential material entirely, and issue short-lived, narrowly scoped proxy tokens, never letting an agent hold standing credentials at all.
Design considerations
None of this is hypothetical, and a CVSS score of 10.0 tends to get everyone's attention quickly. CVE-2026-16498, a finding against HashiCorp's terraform-mcp-server that scored exactly that, is a real, fixed vulnerability where a stateless transport mode failed to generate unique session identifiers, so one caller's cached credential got reused for a different caller's later request. That's cross-tenant credential bleed in production, the kind of thing that shows up on a real incident report. The NSA's first security guidance for this class of protocol names the same root problem independently, stating plainly that the protocol "does not define how a session maps to a verifiable identity," and recommending that every session be treated as untrusted until it's actually proven otherwise. A standing credential and an unverified session are two versions of the same underlying mistake: authority that outlives the specific request that was supposed to justify it.
I want to draw a boundary here so the scope of this pattern doesn't creep past what it actually does. Scoping a token to a caller's own authority answers who is allowed to touch this specific record, checked at the moment of one call, inside one application's own boundary. It has nothing to say about a request that crosses into a different organization's system entirely, where the caller's identity has to be verified by somebody else's infrastructure using its own trust root. That's a genuinely different problem, with its own emerging protocols and its own open questions, and treating a per-request scoping fix as though it solves cross-organization trust as well would be a mistake I'd rather name than let slide.
Scoping authority correctly also depends entirely on the scoping logic itself being correct and complete, which sounds obvious until you've watched it fail. A token minted with the wrong scope, or a records service with a bug in how it compares a token's scope to the record it's being asked to touch, produces exactly the same leak this pattern exists to prevent, just one layer further down in the implementation. This pattern swaps a trust-everything boundary for a check-everything boundary. The check itself still has to be right, every time, for any of that swap to actually matter, and a scoping mechanism nobody tests against adversarial inputs is really just a differently shaped version of the original mistake.
The cost of building this properly is real and mostly upfront. Minting a scoped token per request means touching every call path that used to assume a standing credential was fine, a real engineering lift compared to authenticating once and holding a session. For a small internal tool with one caller type and low stakes, that cost might genuinely outweigh the risk it closes. HRBot handles pay stubs and direct deposit changes for real employees, which put it well past that line for me. The question worth asking before building this pattern into anything is how much damage a single confused deputy could actually do. That number is what decides whether the upfront cost is the right trade, more than any elegance in the mechanism itself.