How it works
Application permissions let a program authenticate as itself instead of on behalf of a person. There is no interactive login and no user session to inherit limits from. The application presents credentials it holds directly, and the identity provider issues a token scoped to whatever that identity was granted.
The flow is usually:
- The application holds a secret — a client secret, certificate, or managed identity.
- It exchanges that secret for an access token (the OAuth client-credentials grant, or an equivalent service-principal flow).
- The token carries the app's granted scopes, not a user's.
- Every downstream call is authorized against those scopes alone.
The critical property is that no user is in the request. Delegated models intersect two things — what the app may do and what the signed-in user may do — and the effective permission is the narrower of the two. Application permissions remove the second term. Whatever the app is granted, it can do everywhere that grant applies, across every tenant object, mailbox, or record the scope covers. That is why these grants are typically admin-consented: there is no per-user gate left to catch an over-broad scope.
Why it matters in an agent harness
An agent that runs unattended has no user to borrow authority from. When it drains a queue at 3am, calls a CMS, or hits an analytics API, it is almost always running under application permissions. That makes the grant the single most important control you own, because it defines the agent's reachable surface completely.
This concentrates risk in a specific way. A delegated agent is bounded by whoever is logged in; a compromised prompt can only reach what that user could. An app-only agent is bounded only by its scopes. Mail.Read as an application permission is not one inbox — it is every inbox in the tenant. So the grant is the blast radius. If prompt injection or a bad tool call turns the agent hostile, the scopes are the wall.
This is also where least-privilege stops being a slogan and becomes load-bearing. Read and write should be separate grants. Broad wildcard scopes should be refused even when they are convenient. And because the credential is long-lived by design, key rotation and secret storage are part of the permission model, not an afterthought — a leaked client secret is a standing grant until someone rotates it.
Application permissions vs delegated permissions
The difference changes how you scope and contain an agent, so it is worth being precise.
| Aspect | Application permissions | Delegated permissions |
|---|---|---|
| Acting identity | The app itself | App on behalf of a signed-in user |
| Effective access | The app's scopes | Intersection of app scopes and user rights |
| Reach | Everything the scope covers, tenant-wide | Limited to that user's own resources |
| Consent | Usually admin consent | Often user consent |
| Fit for agents | Unattended, background, autonomous | Interactive, user-present sessions |
Use delegated permissions when a human is genuinely present and their own authority should bound the action. Use application permissions when the work is autonomous — and then treat the scope list as the security review, because nothing downstream will narrow it for you.
The Rifty take
We treat the app-only grant as the agent's true permission boundary and scope it as if the agent will be turned against us, because eventually a prompt or tool result will try. We optimize for the smallest grant that still lets the loop finish its job, split read from write, and accept the friction of asking for a new scope over the convenience of a broad one. The tradeoff we take on purpose: a slightly harder setup in exchange for a blast radius we can state in one sentence.
Implementation checks
- Grant read and write as separate application permissions; never bundle them for convenience.
- State the reachable surface of each scope in plain words — "every record," not "the record" — before you consent.
- Prefer certificate or managed-identity credentials over long-lived client secrets, and rotate on a schedule.
- Give each agent its own identity so its grants, logs, and blast radius are attributable and revocable in isolation.
- Re-audit scopes when tools change; an added MCP server or API often silently wants a broader grant.
- Pair the grant with runtime containment — a sandbox or approval gate on writes — so the permission is a wall, not the only wall.
Frequently asked questions
When does an agent use application permissions instead of delegated ones?
Whenever it runs without a signed-in user present. Background drains, scheduled jobs, and autonomous loops have no user session to borrow authority from, so they authenticate as the application itself. Delegated permissions only fit interactive sessions where a real user's own rights should bound the action.
Why are application permissions considered higher risk?
Because there is no user context to narrow them. A delegated grant is limited to whatever the signed-in user can reach; an application grant applies tenant-wide to everything the scope covers. The grant alone is the blast radius, so an over-broad scope on a compromised agent has nothing downstream to contain it.
How do I limit blast radius with application permissions?
Grant the narrowest scope that lets the loop finish, split read from write, and refuse wildcard grants. Give each agent its own identity so grants are attributable and revocable in isolation. Then pair the scope with runtime containment — sandboxing or an approval gate on writes — so permission is not the only wall.
Do application permissions expire like a user session?
The issued token expires, but the underlying grant and its credential do not. A client secret or certificate stays valid until it is rotated or revoked, so a leaked credential is a standing grant, not a transient one. That makes secret storage and key rotation part of the permission model itself.