Zero Trust Architecture for MCP Servers Using Pomerium

We’ve been building some open-source tools to make it easier to run remote MCP servers securely using Zero Trust principles — especially when those servers need to access upstream OAuth-based services like GitHub or Notion.

Pomerium acts as an identity-aware proxy to:

  • Terminate TLS and enforce Zero Trust at the edge
  • Handle the full OAuth 2.1 flow for your MCP server
  • Keep upstream tokens (e.g., GitHub, Notion) out of reach from clients
routes:
  - from: https://github.your-domain
    to: http://github-mcp.int:8080/mcp
    name: GitHub
    mcp:
      upstream_oauth2:
        client_id: xxxxxxxxxxxx
        client_secret: yyyyyyyyy
        scopes: ['read:user', 'user:email']
        endpoint:
          auth_url: 'https://github.com/login/oauth/authorize'
          token_url: 'https://github.com/login/oauth/access_token'
sequenceDiagram
  actor U as User
  participant C as MCP Client
  participant O as Upstream OAuth
  participant P as Pomerium
  participant S as MCP Server
  U ->> C: Adds a server URL
  C ->> P: Registers client, initiates auth
  P ->> C: Sign-in URL
  C ->> U: Redirect to sign-in URL
  U ->> P: Sign-in
  P ->> U: Redirect to upstream OAuth
  U ->> O: Authenticate with upstream OAuth
  O ->> P: Return Internal Token (TI)
  P ->> C: Redirect to client
  C ->> P: Obtain External Token (TE)
  C ->> P: GET https://mcp-server Authorization: Bearer (TE)
  P ->> O: Refresh (TI) if necessary
  P ->> S: Proxy request to MCP Server, Bearer (TI)

If no upstream service, like GitHub, you still get Zero Trust

routes:
  - from: https://my-mcp-server.your-domain.com
    to: http://my-mcp-server.int:8080/mcp
    name: My MCP Server
    mcp: {}
sequenceDiagram
  actor U as User
  participant C as MCP Client
  participant P as Pomerium
  participant S as MCP Server
  U ->> C: Adds a server URL
  C ->> P: Registers client, initiates auth
  P ->> C: Sign-in URL
  C ->> U: Redirect to sign-in URL
  U ->> P: Sign-in
  P ->> C: Redirect to client
  C ->> P: Obtain Token
  C ->> P: GET https://mcp-server Authorization: Bearer Token
  P ->> S: Proxy request to MCP Server

Our demo app (MCP App Demo) uses the OpenAI Responses API to show how LLM clients (like ChatGPT’s Connectors) can securely call MCP servers — without embedding tokens or managing OAuth themselves.

The companion project, MCP Servers, includes reference implementations (e.g., Notion) already integrated with Pomerium.

3 Likes
3 Likes

Looks like someone already shared them as a comment. Thanks @EricGT and @aprendendo.next!

4 Likes

I was lucky enough to be able to attend the first ever MCP Dev Summit, and there were so many great talks. There wasn’t enough room in the one day for all the talks, so they moved some to online meetups where I gave a talk about these projects, “Agentic Access: OAuth Isn’t Enough | Zero Trust for AI Agents w/ Nick Taylor (Pomerium + MCP)”. See youtube dot com/watch?v=KY1kCZkqUh0

2 Likes
2 Likes

I’m still highly skeptical of the mcp approach, but zero trust as a security standard is great

if mcp turns out to be the way, great, if not…

has anything changed in the 2 months since this was posted by fireship?

2 Likes

Totally fair to be skeptical. It’s still unclear if this is the long-term direction but it’s been cool to see the ecosystem evolve so quickly since this only came out last November. :sweat_smile:

But what I saw in the Fireship video is exactly why we’re doing what we’re doing. It’s a super cool demo, but the server is wired directly to prod data with no auth, no rate limits, and zero guardrails. Security feels like an afterthought — or maybe just not considered at all.

Even with OAuth support in the MCP spec now, that’s not enough. I mentioned in a comment above that OAuth alone doesn’t solve dynamic authorization or token exposure. If this pattern keeps gaining traction, we need stronger defaults that handle identity and policy enforcement before tools get called.

Like Spiderman’s Uncle Ben always said…

1 Like