Landscape painting of cattle grazing in an open valley, used as the article cover
← Back to blog

PRODUCTION

Why AI Agents Need Permissioning, Audit Logs, and Revocation

Three controls, and one of them promises more than it delivers. RFC 7009's meaningless 200, Entra's 15-minute propagation window, the AI Act's six-month log floor, and what each provider's revoke really does.

•May 11, 2026•Updated Oct 13, 2026•16 min
PermissioningAuditRevocation

TL;DR

Of the three controls every security review asks about, two do roughly what the name says and one does not. Revocation is the one that does not.

RFC 7009 makes the gap explicit. A revocation endpoint returns HTTP 200 "if the token has been revoked successfully or if the client submitted an invalid token", so a 200 is not evidence that anything was killed. Google's own revoke documentation adds that "it might take some time before the revocation has full effect."

The common assumption is that clicking disconnect stops every copy of a credential everywhere, instantly. That holds for a session the revoking system actually issued. It does not hold for an OAuth token sitting in a provider's database, because only the issuer can invalidate it, and a self-contained access token may not be checked against the issuer at all before it is honoured.

We are not arguing against building these three. We are arguing for building each one scoped to what it can reach, and for writing down the boundary before an incident finds it for you.

Do three things this week. Log the deny decisions, not only the allows. Measure the gap between "revoked" in your console and "rejected" at the provider. And write down, per integration, which of those two your disconnect button actually performs.

Overview

Three controls come up whenever an agent stack heads toward production: permissioning, audit logging, and revocation. Security review asks about all three, usually in that order, and a team with good answers clears the process faster than one without.

The trouble is what a good answer looks like. Permissioning gets described as something clever when the thing underneath is a scope lookup per call, which is fine and should be said plainly. Audit gets described as a complete forensic record, true for the decisions a gate makes and false for everything nobody instrumented. And revocation gets called a kill switch, which is where the damage happens, because the word implies authority that a platform sitting downstream of a provider does not have.

None of the three is wrong to want. What is wrong is describing any of them with more reach than the code has. This post is about the mechanisms: the error codes, the documented propagation windows and the retention floors, rather than the philosophy of what an agent should be allowed to do.

The distinction worth holding onto: a control honest about its own boundary is safer to build a posture around than one that oversells. The gap between the two only shows up during an incident, which is the worst possible time to find it.

Permissioning: a decision per call

Most agents still run on standing access. A service-account token with broad scopes, usable for anything the token allows, at any hour, for any reason. That is the setup behind most stories about an agent touching something nobody sanctioned. Nobody attacked anything. The credential was simply wider than the task.

NIST's zero-trust architecture guidance describes the alternative in one line: "Access to individual enterprise resources is granted on a per-session basis", with "the least privileges needed to complete the task", and it adds the part people skip, that "authentication and authorization to one resource will not automatically grant access to a different resource". SP 800-207 frames the whole model as enforcing "accurate, least privilege per-request access decisions". Per request. Not per session start, not per deployment, which is aact:authorization-model/per-action-policy rather than aact:authorization-model/scope-at-grant.

OWASP's LLM06:2025 entry names the failure from the other direction. It splits excessive agency into "excessive functionality; excessive permissions; excessive autonomy", and those three causes are independent. An agent can hold exactly the right scopes and still be dangerous because the tool it was handed is open-ended, which is why OWASP tells you to "avoid the use of open-ended extensions where possible (e.g., run a shell command, fetch a URL, etc.)".

The useful version of the gate is default-deny, evaluated at the moment of the call, at org, team and member level, so a broad grant at the top does not silently become a broad grant for everyone underneath. And the denial should say what was missing. MCP's authorization specification, revision 2026-07-28, defines that shape: a server responds 403 Forbidden with a header carrying error="insufficient_scope" and scope="files:write", naming the scope the operation needed. It also requires servers to "account for scope hierarchies, where a broader scope implies narrower ones".

The same spec's security guidance is blunt about the scope design that breaks this: publishing every possible scope in scopes_supported, and "using wildcard or omnibus scopes (*, all, full-access)". A gate in front of an omnibus scope is theatre.

SaaS APIVaultGatewayAgentSaaS APIVaultGatewayAgentalt[scope missing][allowed]tool call (agent id + user id)scope lookup: user ∩ agent403 insufficient_scope, scope="files:write"write deny to audit logfetch credential by referencetokenauthorized requestresponsewrite allow to audit logresult, token never exposed

Gateway Gateway: scope lookup, user scopes intersect agent scopes |-- scope missing -> 403 insufficient_scope (scope="files:write") + deny written to audit log |-- allowed -------> fetch credential from vault by reference -> authorized request to the SaaS API -> allow written to audit log -> result returned; the token never reaches the agent -->

Figure 1 — Both branches write a record. The deny branch is the one most stacks discard.

We think permissioning is a lookup, not a judgment. "Is this allowed" should resolve against a scope table in single-digit milliseconds, with two outcomes. A system that also returns "wait for a human" is describing routing, notification and a reviewer, all of which are separate machinery that does not exist because the words appear in a diagram.

Audit logs: the record of the decision

Audit logs get asked four questions after something goes wrong. Which agent did this, on whose behalf, what did it touch, and when. A useful log answers all four for any action, including the ones nobody expected to matter.

What a well-built trail captures is the gate's own decision: the identity pair, the scope checked, the action attempted, and the verdict, on every outcome rather than only the successes. That is aact:audit-granularity/per-action-with-principal-chain. Denials belong in the record as much as allows. Google Cloud's audit logging shows how easily the asymmetry creeps in. Admin Activity audit logs "are always written; you can't configure, exclude, or disable them", while "except for BigQuery, Data Access audit logs are disabled by default". The writes are recorded for you. The reads, which are what an exfiltration looks like, are the ones you have to turn on.

Regulation now sets a floor under this. EU AI Act Article 12 requires that high-risk systems "technically allow for the automatic recording of events (logs) over the lifetime of the system". Article 19 puts retention on providers "for a period appropriate to the intended purpose" with a minimum of six months. Article 26(6) puts the same six-month floor on deployers for logs under their control. Those obligations are not live yet: after the Digital Omnibus, the European Commission's own digital-strategy page says the Annex III high-risk rules "will apply from 2 December 2027", and for systems embedded in regulated products such as lifts or toys, "the rules will apply from 2 August 2028". Six months is a floor, and financial-services record-keeping regimes reach further.

NIST's log management guide predates all of this and still contains the most practical item on the list: "ensuring that each logging host's clock is synched to a common time source". Agent traces span your orchestrator, your gateway and three SaaS providers. Without synchronised clocks you cannot order the events, and a trail you cannot order is a pile of records.

One more failure destroys attribution silently. If a component forwards a token it received rather than obtaining its own, MCP's security guidance notes that "the downstream Resource Server's logs may show requests that appear to come from a different source with a different identity". Your log is fine. The provider's log is wrong, and the provider's is the one a customer will ask to see.

A useful test: pick one action from yesterday and reconstruct it from the log alone. Which identity made the call, what scope was checked, what the gate decided, what happened next. If that takes more than a minute, or needs a system other than the log, the pipeline is not finished.

Revocation: what the word actually buys

Revocation is what a team reaches for after something has gone wrong. Someone left, a credential leaked, an agent did something it should not have. The instinct is one button that makes the problem stop being true everywhere at once. What the button does depends on which credential is behind it.

Start with the spec. RFC 7009 says implementations "MUST support the revocation of refresh tokens and SHOULD support the revocation of access tokens". Access-token revocation is a SHOULD. The reason is in section 3, which notes that access tokens "may be self-contained so that a resource server needs no further interaction with an authorization server issuing these tokens to perform an authorization decision". A self-contained token is honoured because it validates, not because anyone asked the issuer whether it still should be. Revoking it changes nothing until it expires.

Then there is the response. Section 2.2 specifies HTTP 200 "if the token has been revoked successfully or if the client submitted an invalid token". A 200 tells you the request was well-formed. It does not tell you a credential died.

Providers differ in what a single revoke reaches. Google's is the widest. Its documentation says revocation "removes all OAuth 2.0 scopes previously granted to a project, invalidating any issued access or refresh tokens for all clients registered under that project", which is a blast radius in the other direction and worth knowing before you wire a revoke call into a cleanup job.

Slack sits at the opposite end, stating plainly that "OAuth tokens do not expire. If they are no longer needed, they can be revoked." No clock is doing the work for you. Slack also revokes automatically when "a workspace owner fully uninstalls the app, a user individually removes their configurations, or the account of the user who initially authenticated the Slack app is deactivated".

does not reach

self-contained token,
until expiry

revoke call made
to the issuer

disconnect the tool

platform's stored copy
of the credential is cleared

new calls routed through
the platform fail immediately

the token record held
by the issuing provider

still honoured
at the provider?

usable directly against
the provider, outside
the platform's view

provider invalidates it,
after propagation

Figure 2 — Clearing a stored copy and invalidating a grant are two different acts, and only the issuer can perform the second.

This is not a defect in one product. It is a property of OAuth. The party that issued a token is the only party that can end it early, and most platforms sit downstream of that authority rather than inside it.

What "revoked" should mean, precisely: cleared from this vault, immediately, so nothing routed through here can use it again. Not: invalidated everywhere it was ever presented. Only the issuer can promise the second, and a 200 from its endpoint does not prove it happened.

How Long Revocation Takes

The second thing teams get wrong is timing. "Immediate" is a property of one narrow path, and the numbers are documented if you go looking.

CredentialWhat revoke reachesDocumented timing
OAuth refresh token, RFC 7009MUST be supported; revoking one SHOULD "also invalidate all access tokens based on the same authorization grant"Next refresh attempt fails; already-issued access tokens may survive
Google OAuth tokenBoth tokens in the pair, and all scopes granted to the project across every client registered under it200 on success, 400 on error, and "it might take some time before the revocation has full effect"
Slack app tokenauth.revoke kills the bot token, removes the bot user, drops slash commands and incoming webhooksTokens "do not expire", so nothing lapses on its own
Microsoft Entra access token, no CAENothing directly; you wait for the refreshDefault access token lifetime is 1 hour
Microsoft Entra with CAECritical events, including an administrator revoking all refresh tokens for a userNear real time, but "latency of up to 15 minutes might be observed"; token lifetime rises to "long-lived, up to 28 hours"
Entra policy or group-membership changeConditional Access evaluation inside the resource provider"Could take up to one day"; optimised paths "reduce the delay to two hours"
AWS temporary credentialsNot deletion. You deny the principal, and "permissions assigned to temporary security credentials are evaluated each time they are used"Sessions run 15 minutes to 36 hours, default 12; "it might take a few minutes for policy updates to take effect"

Table 1 — Revocation reach and latency, read from each provider's own reference pages.

Two things fall out of that table. The first is that longer-lived tokens are not automatically worse. Entra pushes access-token lifetime up to 28 hours precisely because the resource provider is now subscribed to revocation events, so "critical events and policy evaluation drive revocation, not just an arbitrary time period". Short lifetimes are a crude substitute for a revocation channel, and Microsoft says so, describing reduced lifetimes as a "blunt object" that degraded reliability without eliminating the risk.

The second is that "near real time" has a number attached. Fifteen minutes is the documented window for Entra critical events. If your runbook says access stops instantly and the mechanism underneath is an event feed, the runbook is wrong by a quarter of an hour on a good day. The AWS model is the most honest of the set, and the least like a kill switch: nothing is recalled, you attach a deny, and the next request loses.

How They Work Together

These are one system seen from three angles. Permissioning decides what happens. Audit records what the gate decided. Revocation removes an identity's ability to pass the gate, inside whatever boundary the revoking system controls.

Remove any one and the other two lose most of their value. A gate with no trail cannot be checked against what it claims to enforce. A trail with no working revocation documents an incident you cannot stop. And revocation without a real gate underneath is a switch wired to a system nobody was controlling, because standing access was doing the work the whole time.

The connective tissue is the identity pair. Every call should carry both the agent and the user it acts for, both should appear in the record, and both should be revocable independently.

Production Checklist

Before an agent stack ships, a team should be able to say yes to each of these and mean the narrow version rather than the flattering one.

  • Every agent holds a distinct identity, not a credential shared across everything it might run.
  • Every tool call is checked against an explicit scope at the moment it is made, with two outcomes, and a denial names the scope that was missing the way an insufficient_scope challenge does.
  • No scope in the catalogue is a wildcard, and the advertised set is the minimum rather than everything the API can do.
  • Every check is logged with both identities, the scope evaluated and the verdict, on success, failure and timeout alike, and reads are logged as well as writes.
  • Logging hosts share a time source, and retention meets the longest applicable floor, six months being the AI Act's minimum rather than a target.
  • Records are exportable to wherever the security team actually looks.
  • For each integration, someone has written down whether disconnect clears a stored copy, calls the issuer's revoke endpoint, or both.
  • Someone has tested it. Revoke a live grant in staging and measure the seconds until the next call fails. That number is the blast radius.

Where Fabriq Fits

This is the model our own permission layer follows. Default-deny scopes are checked at call time across org, team and member grants, and an agent's effective tool list is the intersection of the user's scopes and the agent's, so widening one side alone changes nothing. Credentials sit in a vault and are attached server-side at the moment of the call. Every call carries two identities, the agent and the acting user, and both land in the audit record with the scope check and the verdict.

The boundary is the one described above, and we would rather state it than imply otherwise. Disconnecting a connection clears our stored copy of the credential immediately. Reaching the issuer to invalidate the grant upstream is a separate call, and it is yours to make. SSO sessions through Okta or another OIDC provider are the exception, because there the session being ended is one we control.

Frequently Asked Questions

Does revoking an OAuth token stop access immediately? Usually not. RFC 7009 requires refresh-token revocation but only recommends access-token revocation, because access tokens "may be self-contained" and validated without contacting the issuer. Revocation stops the next refresh; a token already in flight keeps working until it expires.

What does a 200 from a revocation endpoint prove? That the request parsed. RFC 7009 returns 200 "if the token has been revoked successfully or if the client submitted an invalid token", which means success and "that token was already garbage" are indistinguishable. If you need proof, test with a real call afterwards rather than trusting the status code.

How fast is "near real time" revocation? For Microsoft Entra's continuous access evaluation, the documentation says the goal is near real time but "latency of up to 15 minutes might be observed because of event propagation time", while IP-location enforcement is instant. Conditional Access policy and group-membership changes are much slower, up to a day, or two hours on optimised paths.

Do Slack tokens expire on their own? No. Slack's documentation states that "OAuth tokens do not expire" and must be revoked when no longer needed. Uninstalling the app or deactivating the authenticating user's account revokes them automatically, which matters because an agent built on a departing employee's authorisation stops working the day IT disables the account.

How do I revoke an AWS session an agent is using? You do not delete it. You deny it. Temporary credentials run 15 minutes to 36 hours with a 12-hour default, and permissions "are evaluated each time they are used", so a deny on the principal ends access on the next request. Allow a few minutes.

What has to be in an agent audit record? At minimum the agent identity, the user it acted for, the tool and action attempted, the scope checked, the verdict, and a timestamp from a synchronised clock. EU AI Act Article 12 requires high-risk systems to support automatic event recording across their lifetime; Articles 19 and 26(6) set a six-month retention floor on providers and deployers.

When do the AI Act's high-risk logging duties start? After the Digital Omnibus, the European Commission's digital-strategy page gives 2 December 2027 for the Annex III high-risk areas and 2 August 2028 for high-risk systems embedded in regulated products. Earlier guidance citing 2 August 2026 is superseded.

Should denied calls be logged? Yes, and they are the more useful half. A cluster of denials on one scope says an agent is repeatedly reaching outside its remit, which is either a misconfiguration or the early shape of an incident.

Conclusion

None of these controls has to be exotic to work. They have to be described accurately. Permissioning is a scope lookup with two outcomes and a denial that names what was missing. Audit is the gate's own decisions, both verdicts, on a synchronised clock, kept as long as the longest rule that applies. Revocation is scoped to what the revoking system issued, and no further.

Then do three things. Log denials, because they are the signal you are currently discarding. Write down, per integration, whether disconnect clears a copy or ends a grant. And measure the gap once, in staging, with a stopwatch.

A revocation path nobody has tested is a claim, not a control. Revoke a live grant by hand, watch how long the agent keeps working, and write the number down. Whatever it is in staging is what it will be during the incident.

Sources

All URLs read 2026-09-29.