Third-Party Agents Logging In As Your Users

8 min read

S29
Operation · Safety, Alignment & Agentic Security

An agent holding your user's password is your user, and no request header fixes that.

Blocking third-party AI agents became a stated policy at five large consumer businesses inside four weeks of October 2026 — and not one of them can reliably detect the thing it says it is blocking. An agent signing in with your customer's own credential presents a session that is, by construction, your customer's: there is no field to inspect, and every signal it could carry is one the agent chooses to send. So the control that works is not detection, it is substitution — issue a scoped surface of your own, make it strictly better than screen-scraping, and put the operations you actually care about behind it, so the write is refused by shape rather than by guess.

Step 1Write down what you can observe, and then notice that none of it is a decision.

Every signal available to a server falls into one of two piles, and both piles are losing ones. Either the agent controls the signal, or you are inferring from behaviour that a determined builder will smooth out next sprint.

  • Declared identity — a User-Agent, a custom header, a signed bot-auth credential. Entirely voluntary. The agents you most want to exclude are precisely the ones with a reason not to declare; Amazon's public complaint about Meta's Muse was that it did not identify itself as AI while browsing, which is a complaint about a signal that was never mandatory.
  • Client fingerprints — headless-browser tells, TLS or JA4 fingerprints, canvas and font entropy. These work until the agent drives an ordinary signed-in Chrome profile on the user's own machine, which several consumer agents now do by design. Then the fingerprint is the user's.
  • Network origin — datacentre IP ranges, ASN reputation. Catches cloud-hosted scrapers, misses anything running on the user's laptop or through their residential connection.
  • Behavioural inference — request velocity, navigation shape, dwell time, mouse entropy. The best of the four and still a probability, not a fact. Tune it tight enough to catch a careful agent and you start blocking screen-reader users, keyboard-only users, and your own power users with form-fillers.

This is ambient authority arriving from outside your building. Whatever the credential can do, the holder can do, and your authorisation layer was never given a way to ask who is holding it. The general catalogue of signals and their limits is bot verification and agent access; the operational point here is narrower and harsher — a detector good enough to be a security control would have a false-positive rate that your support organisation will not survive.

Do not let a vendor sell you "agent detection" as the answer to this. Buy it, if you like, as a telemetry source and a rate-limiting input. The moment it becomes the thing standing between a stranger and a money movement, your control is a classifier with an adversary and a business cost on both error types.

Step 2Separate the three things you might be refusing, because they have different answers.

"We block AI agents" collapses three independent decisions, and teams that do not split them end up defending the wrong one. Schwab's October policy, as reported, is the clearest published example of all three being decided separately in one breath.

  • The channel. A browser session opened with a customer's credential. Schwab is reported to block these, and to receive a weekly report of the blocks — the big providers stopped "in the tens and twenties each week."
  • The capability. Money movement. Barred regardless of who asks or how they authenticated.
  • The counterparty. Reads. Kept open to outside models, through Schwab's own MCP servers, for balances and research.

Those are three policies with three enforcement mechanisms. The channel decision is the one you cannot enforce well (step 1). The capability decision is enforceable perfectly, because an operation that does not exist on a surface cannot be invoked through it. The counterparty decision is enforceable because you issued the grant and can revoke it. Notice which two of the three are cheap, and that they are the two that do not require you to recognise anybody.

Run the same split on your own product before writing a policy. For each sensitive operation, ask: would I refuse this to a correctly authenticated agent acting for its owner? If yes, the control belongs on the capability and the channel question is a distraction. If no, you are trying to build a detector, and step 1 applies.

Step 3A block without a sanctioned path is a feature request for a workaround.

Refusing a channel you cannot detect produces a predictable outcome: the builders on the other side invest in being less detectable, and your block decays at the speed of their next release. The institutions whose policies look durable all shipped the refusal and a replacement together.

  • Schwab — outside models retain read access via its own MCP servers; everything else routes to Charley, its in-house assistant.
  • Yelp — admits agents enrolled in a paid data-licensing programme. The policy is not "no agents," it is "agents whose makers have a contract."
  • Meta, as the agent's builder rather than the gatekeeper, answered the Amazon block by pointing at retailers it has arrangements with — Walmart, Best Buy, Gap, Sephora, Wayfair.

The sanctioned path needs to be genuinely better than scraping, or it will not be taken: structured results instead of parsed HTML, a stable contract instead of a selector that breaks weekly, documented rate limits instead of a ban wave, and an explicit grant instead of asking a user for their password. That last point is the one that converts the agent builder into an ally — nobody prefers to collect credentials, and MCP's OAuth 2.1 authorisation model exists precisely so the resource owner, not the agent, decides what the token can do.

Record the grant as a first-class object, not as a row in an OAuth table nobody reads. Which agent, acting for which user, with which scopes, approved when, by whom, and revocable by whom — the content of delegated access and consent records. Without it you cannot answer the one question that arrives with the first incident: on whose authority did this happen?

Step 4Make the write boundary structural, so it holds without recognising anyone.

The strongest property of a surface you designed is that it has an operation list, and the list is the policy. An agent cannot be tricked into a transfer through an interface that has no transfer, whatever is injected into its context and however its credential was obtained. That is a different class of guarantee from a rule that says "do not transfer."

# The grant is the control surface. Reads and writes are not the same surface.
def mint_agent_grant(user, agent_id):
    return Grant(
        subject    = agent_id,          # the agent, not the user
        on_behalf  = user.id,           # recorded, never impersonated
        scopes     = ["positions:read", "research:read"],
        ttl        = "15m",             # short, refreshable, revocable
        audience   = "agent-api",      # not the human web session
    )

# Writes are not a scope you forgot to grant. They are absent from this API.
assert "transfer:create" not in AGENT_API.operations
# transfer:create lives only on the first-party path, behind step-up auth

Three properties are doing the work, and all three are ordinary engineering rather than AI policy. The grant names the agent as its own subject instead of letting it wear the user's session, which is what makes an audit trail possible at all — the distinction agent identity is about. The token is short-lived and audience-bound, so a leaked one is a small, expiring problem rather than an account takeover; see credential lifetime and scoped credentials for agents. And the dangerous operation is not present on the agent-facing API, which closes the confused-deputy path by removing the deputy's hands rather than by instructing it.

Resist the temptation to expose writes behind "just an approval." An approval that an agent can request, phrase and summarise for the user is a UI the agent partly controls. If a write must exist on the agent path, bind it to a value and a target the user typed on your surface, and make the confirmation unreachable from anything the agent supplied.

Step 5Write the terms now, while the volume is still a rounding error.

The striking thing about the October blocks is their scale. Schwab serves tens of millions of brokerage accounts; dozens of blocked agent logins a week is not an operational problem, it is a precedent being set before the traffic arrives. Your equivalent window is open for the same reason, and it closes once a popular agent has taught a million users that your product works through it.

What to have in place before that:

  • A stated position on credential sharing in the terms your users actually agree to, separate from your general automation clause. "Do not give your password to a third party" is a different rule from "do not scrape," and only one of them is about agents.
  • Per-grant rate limits and quotas, not per-IP. A grant is the unit you issued, so it is the unit you can throttle and revoke without collateral damage.
  • A disclosure requirement in the licensing terms: agents on the sanctioned path identify themselves on every request. You cannot enforce this on the unsanctioned path, which is the point — it becomes a reason to be on the sanctioned one.
  • A revocation drill. Pick a live grant and kill it in a staging tenant. Measure propagation. A revocation you have never exercised is a plan, and detecting agent compromise is worth very little if the response path is untested.
  • Logs that join an action to a grant, retained long enough to answer a regulator. If your access log records a session and your grant table records an approval and nothing joins them, you have two halves of an answer.

Step 6When NOT to block.

If the operation is read-only over data the user could already export, blocking buys you no security and costs you an integration you could have licensed — defend the write and the data you do not already give away, and let the reads pay for themselves through a grant you control.

Two further cases where the block is the wrong move. If your only objection is load, the answer is a rate limit on a grant, not an eviction. And if your objection is that the agent might make a mistake on the user's behalf, note that the user is entitled to make that mistake themselves — what you owe them is a surface where the mistake is bounded, reversible and attributable, which is the whole argument for issuing the grant in the first place.

Do the two cheap things this quarter and the expensive one never. Cheap: publish a read-scoped, grant-authenticated surface, and delete the dangerous operations from it rather than guarding them. Also cheap: write the credential-sharing clause and the per-grant revocation path. Expensive and futile: building the detector that tells your user's agent apart from your user. Related: the October blocking wave for what five gatekeepers actually did, and agent payments for where the write boundary is heading.