Quick answer: OAuth 2.0's client credentials grant was designed for services — long-lived, broadly scoped, shared across every instance. That model breaks at agent scale. Agents need per-agent tokens that are scoped to specific actions, revocable independently, and issued through a broker that can enforce approval gates before anything runs.
How client credentials actually work
The OAuth 2.0 client credentials grant is the standard flow for machine-to-machine authentication. Your service has a client ID and client secret. It exchanges those for an access token. The token has a scope (often very broad) and an expiry. When it expires, the service exchanges credentials again.
For traditional services this is fine. A billing service that needs to read subscription data from Stripe authenticates once, gets a token, and uses it. The token is tied to the service, not to an individual request.
The design assumptions:
- One identity per service — there's one client, not hundreds
- Broad scope is acceptable — the service needs to do many things across its lifetime
- Long-lived tokens are fine — services are trusted, managed systems
- Revocation is rare — you revoke when decommissioning, not as a routine operation
These assumptions made sense for the systems OAuth 2.0 was designed for. They don't hold for AI agents.
What breaks at agent scale
When teams start using OAuth to give AI agents access to Google Workspace, GitHub, Vercel, or any other OAuth-supporting service, they typically do one of two things:
Option A: One shared client credential for all agents. Every agent authenticates with the same client ID and secret. Every agent gets the same token scope. If one agent is compromised or goes rogue, the only remediation is rotating the client secret — which breaks every other agent using it.
Option B: One client credential per agent. Each agent gets its own OAuth client. This solves the revocation problem but requires registering a new OAuth application for every agent you deploy. Most OAuth providers have limits on the number of applications. Managing dozens of OAuth clients manually is operationally painful. And the tokens still have whatever scope the OAuth application was registered with — typically read/write on a broad set of resources.
Neither option gives you what you actually need: tokens scoped to a specific task, revocable per agent without touching anyone else, issued with an approval gate on destructive actions.
The shared-token blast radius problem
Here's a concrete version of the problem:
- Your team registers one Google OAuth application for agent use
- You issue the same refresh token to your Cursor agent, your n8n workflow, and your custom deployment agent
- Cursor agent needs to read a Google Doc — it uses the token
- The n8n workflow needs to send a Gmail — it uses the same token
- The deployment agent needs to modify a Google Cloud project — same token, same scope
Now one of those agents has a bug. It's making unintended calls — maybe reading files it shouldn't, or spamming API calls that rack up quota. The only way to stop it without taking down the other agents: rotate the refresh token. Every agent goes down while you update credentials everywhere.
The audit log shows requests to Google's API from your application's client ID. It does not tell you which of the three agents made which call.
The scope sprawl problem
OAuth scopes were designed to limit what a token can do. In practice, most applications request broader scopes than they need because it's easier than figuring out exactly which permissions each workflow requires — and because requesting too narrow a scope causes permission errors that are annoying to debug.
The result is that OAuth tokens in production routinely have permissions like repo (full GitHub access), drive (full Google Drive access), or admin:org (full GitHub organization access) — granted because the application might someday need them, or because a developer included them as a precaution.
When those tokens are handed to AI agents, the agent can exercise the full scope of the token. A Cursor agent with a repo-scoped GitHub token can push to any repository in your organization, delete branches, modify workflow files, or change repository settings — regardless of whether its task requires any of that.
Refresh token lifetime and rotation
OAuth refresh tokens add another complexity at agent scale. Refresh tokens are long-lived — often indefinite — and are used to obtain new access tokens when the current one expires. If your application implements refresh token rotation (issuing a new refresh token with each access token refresh), each agent needs to store its current refresh token, replace it atomically, and handle the case where two concurrent requests both try to use the same refresh token.
For traditional services with a single process, this is manageable. For AI agents that might run as multiple concurrent instances (multiple Cursor windows, multiple n8n executions), refresh token race conditions cause authentication failures that are difficult to diagnose. The error looks like a random "invalid token" failure, not a concurrency bug.
A credential broker eliminates this problem: the broker holds the refresh token and handles all token lifecycle management. Agents get ephemeral, already-valid access tokens or scoped credentials. They never see the refresh token and never need to manage its rotation.
A better pattern: per-agent scoped tokens through a broker
The fix isn't more OAuth registrations — it's a credential broker that sits between your agents and the OAuth credentials.
The flow:
- Your team connects Google OAuth to Gazebo once, storing the credentials in the vault
- Each agent gets its own Gazebo access profile — a named identity with a defined scope (e.g., "read Google Docs in /projects folder, send to one Gmail address")
- When the agent needs Google access, it presents its Gazebo token — not the underlying OAuth credential
- Gazebo checks the agent's profile, optionally requires approval for destructive actions, then makes the API call on the agent's behalf (or issues a narrowly scoped credential)
- The audit log shows which agent called which endpoint, not just that the OAuth application made a request
The underlying OAuth client credential is stored once. The per-agent access layer lives in Gazebo. Revoking one agent's access deletes its profile in Gazebo — the OAuth credential is untouched, every other agent keeps working.
The Google OAuth workflow guide covers how to connect your Google OAuth application to Gazebo and issue per-agent access profiles from it.
Migrating from direct OAuth to a broker
If you're currently giving agents direct OAuth tokens, migration can happen incrementally:
Step 1: Connect your existing OAuth application to the broker. The broker stores your client credentials and can issue access tokens on demand. Nothing changes for your agents yet.
Step 2: Create access profiles in the broker that approximate your current OAuth scopes. Start broad — matching what the tokens currently grant — and narrow over time as you understand what each agent actually uses.
Step 3: Update agents to fetch their credentials from the broker rather than from environment variables or config files. The agent receives an access token (or scoped credential) it can use directly. From the agent's perspective, the only change is where it gets the token from.
Step 4: Once agents are routing through the broker, tighten the profiles. Look at the audit log: which endpoints is each agent actually calling? Reduce each profile to only those operations.
The incremental approach means you get the revocation and audit benefits immediately (step 3) while the least-privilege hardening happens over time (step 4).
When OAuth client credentials are the right tool
The client credentials grant is still appropriate for:
- Infrastructure services with a single, bounded identity — a billing microservice that only ever calls Stripe, a webhook processor that only writes to your database
- Non-autonomous workloads — cron jobs, CI/CD steps, batch processors that run on a fixed schedule with a predictable scope
- Internal services on well-controlled infrastructure — where the "agent" is really just your application calling its own API
The signal that you've outgrown plain client credentials: when you have more than one actor sharing the credential, when you need to revoke one without affecting others, or when you need an audit trail that distinguishes between agents — not just applications.
For background on why the identity model needs to change when agents are involved, IAM for AI agents covers the full shift from service-account thinking to per-agent identity. The open standard that formalises this identity and consent model is the Agent Identity Protocol.
A note on PKCE and agent compatibility
PKCE (Proof Key for Code Exchange) is an OAuth 2.0 extension that protects authorization code flows by requiring a challenge-response step. Some services have started requiring PKCE even for confidential clients — partly for security hygiene, partly as OAuth 2.1 adoption increases.
This creates a compatibility problem for AI agents: PKCE's challenge-response flow assumes a browser-based redirect, which agents don't have. An agent trying to initiate a PKCE flow has no mechanism for the redirect URI callback that completes the exchange.
The practical consequence is that services requiring PKCE for all flows can't be accessed by agents using the authorization code grant. Client credentials remains available for machine-to-machine access, but as covered above, client credentials at agent scale introduces its own problems — shared tokens, broad scope, no per-agent auditability.
The cleaner solution when a service requires PKCE: route access through a broker that handles the OAuth flow server-side. The broker completes the PKCE exchange once, holds the resulting tokens, and issues scoped, per-agent access when agents need it. Agents never need to participate in the OAuth redirect flow at all — the broker manages the credential lifecycle and exposes only what each agent's policy permits. This is the same model as the migration path described above, applied specifically to PKCE-gated services.