Authentication

Same-user auth

Understand how MCPSail keeps embedded tool calls scoped to the active signed-in user while brokering Gateway OAuth server-side.

7 sections

Same-user auth means the agent acts as the same person who is currently signed in to your product.

The browser does not receive a downstream API token and the host app does not have to manage MCP callback routes. MCPSail handles the MCP-facing OAuth surface, stores the downstream grant server-side, and makes the actual API call under the user's identity.

The actors#

The Gateway-backed flow has three zones:

  • User / client zone: your app or another MCP client
  • MCPSail Gateway zone: the public auth edge plus the private runtime
  • Customer app zone: your auth server and API

In the Todo reference flow documented in the repo README.md, the actors are:

  • user
  • MCP client
  • MCPSail Gateway
  • generated app runtime
  • Todo auth server
  • Todo API

Request flow#

The end-to-end sequence looks like this:

  1. The user clicks connect from the embedded app or agent.
  2. The MCP client starts OAuth against the MCPSail Gateway URL.
  3. MCPSail redirects the user to your auth server.
  4. Your auth server returns an authorization code to MCPSail.
  5. MCPSail exchanges that code for the downstream grant and stores it server-side.
  6. The client receives an MCPSail MCP token, not the downstream API token.
  7. Later, the user asks the agent to do something.
  8. The client sends the tool call to MCPSail using the MCPSail MCP token.
  9. MCPSail validates that token, loads the stored downstream grant, and sends the request to the configured MCP runtime.
  10. The runtime calls your API with the downstream access token for that user.
  11. The API authorizes the request exactly the way it would for any normal first-party user call.

That is the core boundary:

  • the client talks to MCPSail
  • MCPSail holds the downstream grant
  • your API still sees a normal user-scoped token

Why this matters#

This model keeps the security boundary clean:

  • your browser app does not need to hold downstream refresh tokens
  • your host app does not need to mint or forward MCP tokens
  • your existing RBAC stays in the customer API where it already belongs
  • audit logs, rate limits, and permission checks still run at the real API boundary

Embedded mode and host identity#

For the embedded SDK, pass the current signed-in user through userIdentity:

TSX
<MCPSailChat
  apiKey="emcy_sk_xxxx"
  agentId="ag_xxxxx"
  userIdentity={{
    subject: session?.user?.id,
    email: session?.user?.email,
    organizationId: session?.organizationId,
    displayName: session?.user?.name,
  }}
/>

That gives MCPSail a host-side identity hint so the popup flow can prefer the same downstream account and block mismatches when the resolved account is clearly different.

What MCPSail owns vs what your app owns#

MCPSail owns:

  • MCP-facing OAuth discovery
  • popup callback handling
  • server-side grant storage
  • MCP token validation
  • runtime forwarding to your generated tools

Your app owns:

  • the signed-in host session
  • the identity values passed through userIdentity
  • the downstream auth server and API
  • the authorization rules enforced by the API itself

Failure cases to expect#

The safe failure modes are intentional.

If MCPSail cannot confidently match the downstream account to the host account, it should fail closed and ask the user to switch or reconnect.

Common cases:

  • no downstream grant exists yet
  • the downstream provider resolves a different user
  • subject or email cannot be compared cleanly
  • organization context does not match