
BEST PRACTICES
Agent Permissions Best Practices
Six practices for sizing an agent's access, with the real scope names, the vendor limits, the token shape decision, and what revoking a grant actually does at Google, Microsoft, Slack and GitHub.
TL;DR
The permission that causes the incident is rarely the one anyone approved on purpose. It is the one nobody took back.
The providers have already conceded the point in their own defaults. Microsoft calls application permissions "highly privileged" and says the delegated model "is the recommended approach whenever it meets the application's requirements." Google requires that an app "that only reads data must not request access to read, write, and delete content when a more narrow scope is available." GitHub now tells you outright to stop using classic tokens.
The common instinct is that granting broadly is efficient, because enumerating actions is slow and a broad role works on the first try. It is efficient exactly once. Every capability an agent holds but does not use is still in its blast radius the day a prompt gets manipulated or a tool call goes wrong.
We are not arguing for a review board on every scope. Most of this is mechanical: pick the token shape that carries the user, name the action rather than the system, and let a scheduled review delete what nobody can justify.
Start with the agent nobody has looked at since it shipped, and read its granted scopes against the provider's own least-privileged recommendation for each call it makes.
Overview
Agent permissions decide two things: what an agent can reach, and what it may do once it gets there. As agents connect to CRMs, warehouses, ticketing systems and payment processors, the second question carries almost all of the risk, and it is the one most configuration screens are worst at asking.
How authorization gets checked at request time is its own subject, covered in agent authorization vs. authentication. What follows is about sizing: how to decide what goes in the grant in the first place, independent of the enforcement point that later reads it.
Six practices carry the weight, and we think they are all mechanical rather than philosophical. Grant narrowly. Scope by action rather than by system. Evaluate every request against the person the agent acts for. Keep environments apart. Review on a cadence. Remove access cleanly when an agent retires. None of them is exotic. Skipping any one of them is how an agent ends up holding access nobody can explain.
The shift that matters: permissions are not a setup field you fill in once. They are a claim that has to keep being true.
Least Privilege, With the Provider's Own Numbers
Least privilege is easy to state and constantly skipped: an agent gets only the permissions its workflow requires, with nothing held in reserve. NIST's zero trust tenets put the same rule at the resource level, requiring that access "be granted with the least privileges needed to complete the task."
The useful part is that the major providers now publish what "least" means for specific calls, so the sizing argument does not have to be conducted from first principles.
Microsoft's guidance is the most concrete. An app that needs to read the signed-in user's profile requires
User.Read, and granting User.ReadWrite instead "makes it over-privileged because the app doesn't need
to update the user's profile." Google's requirement is a rule rather than advice: request "scopes that
require the least amount of access to user data necessary to provide that functionality", and an app "that
only reads data must not request access to read, write, and delete content when a more narrow scope is
available." Google also enforces it commercially. Sensitive scopes "require review by Google before any
Google Account can grant access", and that verification typically runs three to five business days, so an
over-broad ask costs you a week.
RFC 9700, the OAuth Security Best Current Practice, generalises both: the privileges associated with an access token "SHOULD be restricted to the minimum required for the particular application or use case", and tokens "SHOULD be audience-restricted to a specific resource server or, if that is not feasible, to a small set of resource servers."
Take a marketing analytics agent that posts a weekly campaign digest. It reads engagement metrics and writes one message to one channel. It does not need to edit dashboards, adjust ad spend, or export the customer list. Every capability it lacks is an incident that cannot happen to it.
Rule of thumb: if you cannot name the workflow step that justifies a permission, the agent does not get it. If you can name the step but not the narrowest scope that covers it, you have not finished reading the provider's docs.
Action-Level Scopes
Connecting an agent to a system is not the same as letting it do everything that system supports. Inside a single tool the distinct actions carry wildly different risk, and modern scope vocabularies are built to express that.
Microsoft Graph encodes it in the name itself, as {resource}.{operation}.{constraint}. The operation is
Read, ReadBasic, ReadWrite, Create, Manage or Migrate. The constraint is All, AppFolder,
OwnedBy, Selected, Shared or Hidden, and when it is left undeclared "the default constraint is
limited to data that's owned by the signed-in user." So Application.ReadWrite.OwnedBy and
Application.ReadWrite.All differ by one word and by the entire tenant.
| Action class | What it looks like as a real scope | Why it needs its own grant |
|---|---|---|
| Read | User.Read, Slack files:read | Recoverable, but still the whole exfiltration surface |
| Create | Group.Create | Adds state someone else has to clean up |
| Update | Calendars.ReadWrite | Overwrites work that existed before the agent arrived |
| Delete | User.DeleteRestore.All | Often irreversible, and blocked outright for Entra agent identities |
| Export | Warehouse-level SELECT on raw tables | Moves data past the boundary that was protecting it |
| Send | Slack chat:write | Leaves the building. No recall |
Table 1 — The same connection, six different grants. "Access to the finance system" is not a control; "read policy limits, draft reimbursements" is.
Take an expense-report agent connected to the finance system. Reading policy limits and drafting a reimbursement is reasonable. Posting that reimbursement into payroll is a different grant entirely, and the two should not share a scope because they happen to touch the same tool.
There is a ceiling here worth knowing about, because it catches teams who solve breadth by adding scopes. Microsoft Entra caps a single app at 400 requested permissions, and a single consent request at roughly 155 delegated or 300 application permissions. An app whose signInAudience includes personal Microsoft accounts is capped at 30. If an agent is approaching those numbers, the answer is more agents with narrower jobs, not a larger manifest.
User-Aware Permissions
When an agent acts for a person, its effective permissions are the intersection of what the agent may do
and what that person may do. Never the union. The taxonomy calls that
aact:authorization-model/intersection-of-agent-and-user. Microsoft states this as a property of the delegated
permission model rather than as advice: an app granted a delegated permission "can't access anything the
signed-in user couldn't access", and the privileges it has acting for someone are determined by the
permissions the app was granted and the user's own permissions.
effective_access = agent_permissions ∩ requesting_user_permissions
Take an HR assistant answering compensation questions. A team lead asking about their direct reports should get an answer. An individual contributor asking about a colleague's salary should get whatever that requester is entitled to, which here is nothing. If the agent only consults its own access, every employee inherits whatever the service account can see, and the agent has become a side door around controls that already worked.
Slack's token prefixes make the distinction legible at a glance. A bot token, xoxb-, carries the app's
own granular scopes. A user token, xoxp-, represents a workspace member and carries that member's access.
Reading the prefix tells you which half of the intersection you are actually enforcing.
This one rule is worth more than most of the others combined, because it is the difference between an agent that respects existing access controls and one that quietly launders around them for anyone who knows how to ask.
Which Token Shape to Give It
We think the sizing decision that matters most is made before any scope is picked, and it is usually made
by accident. Four token shapes are available, and they are not interchangeable. The first two rows are the
taxonomy's aact:delegation-model/autonomous and
aact:delegation-model/on-behalf-of-user.
| Token shape | Who it acts as | Effective access | When it is the right answer |
|---|---|---|---|
Application / app-only (xoxb-, Graph app roles) | The app itself | Exactly what was granted, tenant-wide | Background jobs with no user, where "any record" is genuinely correct |
Delegated / user (xoxp-, Graph scopes) | A specific person | Grant ∩ that person's access | Anything an agent does on someone's behalf |
| Fine-grained resource-scoped (GitHub fine-grained PAT) | The app, bounded to named resources | Grant, narrowed to selected repos or objects | Where the provider lets you name the objects, not just the verbs |
| Classic broad token (GitHub PAT classic) | The person, unbounded | Everything that person can reach anywhere | Legacy paths the provider has not migrated yet |
Table 2 — Pick the row before you pick the scopes. Row two is the default for agent work, and row four is what you are probably still using.
Microsoft is blunt about the first two rows. Application permissions "are highly privileged because they allow applications to access and modify resources without requiring a signed-in user", and "from a least-privilege perspective, the delegated permission model is the recommended approach whenever it meets the application's requirements."
GitHub is blunt about the last two. It "recommends that you use fine-grained personal access tokens instead of personal access tokens (classic) whenever possible", because each fine-grained token "is limited to access resources owned by a single user or organization" and "can be further limited to only access specific repositories". A classic token, by contrast, grants access to "all repositories within the organizations that you have access to, as well as all personal repositories in your personal account." Handing a coding agent a classic PAT gives it every repository you have ever been added to, including the ones you forgot about.
Environment Separation
Agents in development should not carry production permissions. Experimental agents change fast, run logic nobody has stress-tested, and are built under time pressure, which makes them the wrong candidate for live systems and real customer data.
A team prototyping an agent that joins customer and revenue tables should work against masked or synthetic data with read-only access while it is still finding its footing. Promotion is the moment to attach real credentials and the action-level scopes the workflow needs, under review, rather than by default because the prototype happened to run.
AWS frames the same idea as a sequencing rule worth copying: start with broad permissions "while you explore the permissions that are required for your workload", then reduce them as the use case matures. The failure is not starting broad. It is never running the second step.
Periodic Reviews
Permissions are not granted forever, even when nobody revokes them. Workflows change, integrations retire, and a scope that was essential six months ago sits there widening the surface an attacker has to work with.
The mechanical version of this review already exists in the platforms. AWS publishes last-accessed information specifically "to help you identify the users, roles, permissions, policies, and credentials that you no longer need so that you can remove them", and IAM Access Analyzer will generate a tightened policy from what a role actually did in CloudTrail. Microsoft Entra ID Governance extends access reviews to agent identities so you can "periodically verify that agent identities still need the access they have", and it attaches a named sponsor to each agent who gets notified when an assignment approaches expiry.
What makes a review land is the shape of the artifact. A wall of raw grants gets skimmed. A line reading
"this agent holds files:write, last used 94 days ago, last reviewed in March" is a decision someone can
make in four seconds. A contract-review agent still holding an export scope it has not touched in a quarter
is an obvious call once that history is visible, and invisible otherwise.
Google's incremental authorization is the prevention side of the same idea: request scopes "as they are needed" rather than all at consent time, which keeps the standing grant equal to what has actually been used. Google notes the incentive plainly, warning of "an inverse relationship between the number of scopes requested and the likelihood of obtaining user consent."
Revocation and Offboarding
When an agent retires, its permissions come off immediately. A decommissioned agent holding live credentials is an access path nobody is watching.
Revocation has to be a checkable step, not a status flag, and this is where most runbooks quietly fail.
Deleting a stored token deletes your copy. It does not tell the provider anything. Google exposes a
revocation endpoint at https://oauth2.googleapis.com/revoke for exactly this reason, and the observable
result of calling it is that the next API call returns invalid_grant. Until something calls it, the grant
is still live on Google's side and a second copy of that token still works.
So the offboarding sequence is three steps, in order. Disable the agent identity so nothing new can be minted. Call the provider's revocation endpoint for every connection it held. Then confirm, by making a call, that the credential is dead. A registry that says "inactive" while the underlying token still answers has revoked nothing.
Clearing your copy of a credential is not the same act as invalidating the grant upstream. Anything that claims otherwise is describing a deletion, not a revocation.
Avoiding Permission Creep
Every practice above defends against the same slow failure. An agent starts narrow and justified. A feature needs one more scope. A workaround grants temporary access that never gets withdrawn. A broad role gets attached because enumerating actions was slower. No single decision looks reckless. The agent that results from all of them usually is.
Figure 1 — Without a scheduled review, an unused permission's default state is staying, not going.
The alternative to a standing grant is asking for scope at the moment it is needed, which the MCP
specification now describes as a first-class flow. A server that receives a call with insufficient scope
answers 403 with error="insufficient_scope" and a scope parameter naming what the operation requires,
and the client runs a step-up authorization for the union of what it had and what was challenged.
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="files:write",
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
error_description="File write permission required for this operation"The spec is careful about the failure mode on the other side, too. Servers should send every scope the
operation needs in one challenge, because "challenging incrementally (returning one missing scope, then
another on the subsequent retry) forces multiple authorization round-trips for a single operation and
degrades user experience." The scopes_supported field is meant to be "the minimal set of scopes necessary
for basic functionality", with everything else requested step by step.
Figure 2 — Step-up authorization turns a permission into an event you can audit instead of a setting nobody remembers changing.
Where Fabriq Fits
Agentic Fabriq holds credentials per user in a vault and attaches them server-side at the moment of the call, so the agent never handles the secret. An agent's effective tool list is the intersection of the user's scopes and the agent's, which is the rule in the user-aware section enforced in the backend rather than left to each integration. Access is default-deny across organisation, team and member, with per-user overrides, and every call carries both the agent and the acting user into an audit record.
It does not revoke anything at the provider for you. Clearing the vault copy is a deletion on our side, and the call to Google's revoke endpoint or its equivalent is still yours to make.
Frequently Asked Questions
What permissions should an AI agent have? The narrowest scope the provider publishes for each call the
agent actually makes, attached to a token that carries the acting user. Microsoft's own example is the
clean one: User.Read rather than User.ReadWrite when the agent only reads a profile.
What is the principle of least privilege for AI agents? The same principle as for any workload, with one addition. NIST's zero trust tenets say access should carry "the least privileges needed to complete the task"; for an agent you also bound it by the person it is acting for, so the effective grant is the intersection rather than the agent's grant alone.
Should an agent use a bot token or a user token? A user or delegated token for anything done on someone's behalf. Microsoft's guidance is that application permissions are "highly privileged" and the delegated model "is the recommended approach whenever it meets the application's requirements." Reserve app-only tokens for background work with no user in the picture.
How do I stop an agent from accumulating permissions? Two mechanisms, and you want both. Request scope incrementally so the standing grant only grows when an operation needed it, and run a scheduled review against last-used data so anything dormant gets pulled. AWS's last-accessed information and Entra's access reviews for agent identities both exist for the second half.
How do I actually revoke an agent's access? Disable the agent identity, call the provider's revocation endpoint for each connection, then verify with a live call. Deleting your stored copy of the token leaves the grant intact upstream, and any other copy of that token keeps working.
Are there permissions an agent should never be given? Microsoft has published a list, which is the
closest thing to an industry answer. Entra blocks a set of high-privilege Graph permissions for agent
identities outright, including Directory.ReadWrite.All, RoleManagement.ReadWrite.Directory and
User-PasswordProfile.ReadWrite.All, and rejects the grant with an HTTP 400.
Do agents need different permissions in dev and production? Yes, and the direction of travel matters more than the split. AWS suggests starting broad while you discover what a workload needs and then reducing toward least privilege. Development is where the first half is legitimate. Production is where the second half has to have happened.
Conclusion
Strong permission governance is less about telling agents no than about making sure each one can do its job and nothing more, and that this stays true as the agent, its workflows and the systems around it change.
Grant narrowly, using the provider's published least-privileged scope rather than your own guess. Scope by action. Pick a token shape that carries the user, so the effective grant is an intersection. Keep environments apart. Review against last-used data. Revoke at the provider, not just in your database. Each is simple. Applied together they keep autonomy useful instead of letting it become power nobody signed off on.
The agents worth trusting are not the ones with the fewest permissions. They are the ones whose access you can still account for a year from now.
Sources
All URLs read 2026-09-29.
- Overview of Microsoft Graph permissions — the
{resource}.{operation}.{constraint}naming pattern, delegated versus application permissions, theUser.Readover-privilege example, and the 400/155/300/30 permission caps. - Microsoft Entra Agent ID APIs in Microsoft Graph — the Graph permissions blocked for agent identities, the HTTP 400 rejection, sponsors, and access reviews for agents.
- Security best practices in IAM, AWS — apply least-privilege permissions, start broad then reduce, last accessed information, Access Analyzer policy generation, and permissions boundaries.
- Using OAuth 2.0 for Web Server Applications, Google — incremental authorization, the consent-rate warning, and the
https://oauth2.googleapis.com/revokeendpoint. - Sensitive scope verification, Google — the narrowest-scope requirement and the review timeline for sensitive scopes.
- Managing your personal access tokens, GitHub — the recommendation to use fine-grained tokens, per-repository limiting, and what a classic token actually reaches.
- Token types, Slack —
xoxb-bot tokens on a granular scope model,xoxp-user tokens carrying a member's own access, andxapp-app-level tokens. - RFC 9700: Best Current Practice for OAuth 2.0 Security — minimum-privilege and audience restriction for access tokens (§2.3).
- MCP specification 2026-07-28, Authorization — scope selection strategy,
scopes_supportedas a minimal set, theinsufficient_scope403 challenge, and the step-up flow. - NIST SP 800-207: Zero Trust Architecture — per-session access granted with the least privileges needed to complete the task (tenet 3).
- Agent Access Control Taxonomy — the authorization model and delegation model dimensions;
intersection-of-agent-and-userfor the user-aware rule, andautonomousagainston-behalf-of-userfor the first two rows of Table 2. - RFC 9728: OAuth 2.0 Protected Resource Metadata —
scopes_supportedand theresource_metadataparameter carried onWWW-Authenticate.