Skip to content
Article

Giving AI Agents Real Employee Identity With MCP OAuth

Shared API keys make agent actions anonymous. Here is how we unified Google Workspace SSO with an MCP OAuth 2.1 authorization server so remote agent harnesses act as signed-in employees—with attribution, consent, and automation still available when humans are not in the loop.
TLDR
  • Shared MCP API keys collapse human agents, in-app AI, and CI into one anonymous actor.
  • Host MCP OAuth 2.1 (auth code + PKCE) with Google Workspace as the interactive IdP; keep API keys for automation only.
  • Require an EmployeeUser principal on mutating tools and log auth_method (oauth | jwt | api_key) for attribution.
  • Map SSO to existing employees—do not auto-provision strangers—so offboarding stays authoritative.
  • OAuth answers who called; still keep draft-only defaults and human publication gates.

TL;DR: Remote MCP clients should not share one API key if humans are driving them. We hosted an MCP OAuth 2.1 authorization server in our agency portal, put Google Workspace behind the consent screen, and made mutating tools require an employee principal—while keeping API keys for CI only.

When we first exposed a remote MCP server to Cursor and similar harnesses, the easy path was a shared API key. It worked. It also made every write look like the same system user. That is fine for overnight automation. It is a poor fit for a human asking an agent to create a blog draft, book time, or touch CRM records.

This post is the production pattern we shipped: unify Google Workspace SSO for the web app with MCP OAuth for agent harnesses, keep JWT for in-app chat, and reserve API keys for non-human jobs.

The problem with “one key to rule them all”

A single automation credential collapses three different trust models:

  • Human-driven agents (Cursor, Claude Desktop, etc.) need interactive login, consent, and per-employee attribution.
  • In-app AI already has a signed-in employee JWT and should reuse it.
  • CI and scheduled jobs need a non-interactive secret and a deliberate system actor.

If you only ship the third option, your audit trail lies. Drafts appear under a shared bot account. Tool logs cannot answer “who asked for this mutate?” And rotating one leaked key knocks every harness offline.

What we built

On the Symfony / API Platform backend that powers our internal portal (“Ansible”), we shipped three complementary auth paths on the same MCP resource server:

  1. Google Workspace SSO for employee web login—map a verified Workspace email (and stable Google subject) to an existing enabled employee user, then issue the same Lexik JWT the portal already trusts via a BFF session.
  2. MCP OAuth 2.1 authorization server hosted by Ansible—authorization code + PKCE + refresh, with Google as the interactive IdP behind /oauth/authorize.
  3. MCP resource server that accepts API key (automation), employee JWT (in-app chat), and MCP OAuth access tokens—returning 401 plus Protected Resource Metadata discovery headers when unauthenticated.

Mutating MCP tools require an EmployeeUser principal and log auth_method as api_key, jwt, or oauth. That single field turns “the bot did it” into forensic-grade attribution.

Discovery first, secrets second

Employee harnesses should not need a private key in their MCP config. URL-only setup is enough:

{
  "mcpServers": {
    "Ansible": {
      "url": "https://ansible-backend.endertech.com/api/mcp"
    }
  }
}

On first connect the client discovers Protected Resource Metadata and Authorization Server Metadata, opens a browser login (Google Workspace via Ansible), and stores the resulting access token. Writes then attribute to that employee—authors on drafts, reporters on tickets, created-by on records.

Pre-registered public clients cover the common harnesses; dynamic client registration remains available for experiments. Automation keeps a separate API-key path that still resolves to an explicit system employee user rather than “anonymous.”

Practical lessons from the cutover

  • Map identity; do not auto-provision strangers. SSO only succeeds when the Workspace email (or Google subject) matches an existing enabled employee. That keeps the employee table the source of truth for roles and offboarding.
  • Reuse JWT machinery for OAuth access tokens. Issuing Lexik JWTs from the authorization server meant the MCP endpoint could reuse the existing JWT authenticator after the API-key check—less parallel crypto, fewer edge cases.
  • Consent is part of the product. A real consent screen for public MCP clients makes the trust boundary visible. Agents are powerful; humans should see what they are authorizing.
  • Keep automation boring. CI still uses an API key. Do not force interactive OAuth into pipelines. Do force pipelines to declare which system user they act as.
  • Log the auth method on every mutate. When something unexpected lands in CMS or CRM, “oauth vs api_key” is the first triage question.

What this is not

This is not a substitute for tool-level authorization, optimistic concurrency, or human publication gates. OAuth answers who is calling. You still need allowlists, draft-only defaults, and review workflows for anything customer-facing. We treat those as separate layers—and we keep publishing decisions with humans.

Why it matters for agencies and product teams

Agent harnesses are becoming a first-class client of internal APIs. If your MCP surface can create content, touch money-adjacent systems, or enqueue outreach, shared secrets are a temporary hack. Employee-attributed OAuth is the difference between “we tried agents” and “we can operate agents in production.”

If you are evaluating MCP for operations, marketing automation, or client delivery, start with identity: interactive OAuth for humans, JWT for in-app AI, API keys only for jobs that truly have no user in the loop.

Want help designing an MCP surface your team can actually trust? Talk to us about AI orchestration or explore our MCP technology page—or start a conversation.

Drag to pan. Use +/− or Ctrl/Cmd + scroll to zoom. Pinch to zoom on touch devices.

MCP OAuth + Google Workspace SSO for Attributed AI Agents