Authentication
Same-user auth
Understand how Emcy keeps embedded tool calls scoped to the active signed-in user while brokering Gateway OAuth server-side.
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. Emcy 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
- Emcy 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
- Emcy Gateway
- generated app runtime
- Todo auth server
- Todo API
Request flow#
The end-to-end sequence looks like this:
- The user clicks connect from the embedded app or agent.
- The MCP client starts OAuth against the Emcy Gateway URL.
- Emcy redirects the user to your auth server.
- Your auth server returns an authorization code to Emcy.
- Emcy exchanges that code for the downstream grant and stores it server-side.
- The client receives an Emcy MCP token, not the downstream API token.
- Later, the user asks the agent to do something.
- The client sends the tool call to Emcy using the Emcy MCP token.
- Emcy validates that token, loads the stored downstream grant, and sends the request to the configured MCP runtime.
- The runtime calls your API with the downstream access token for that user.
- 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 Emcy
- Emcy 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 embeddedAuth:
<EmcyChat
apiKey="emcy_sk_xxxx"
agentId="ag_xxxxx"
embeddedAuth={{
hostIdentity: {
subject: session?.user?.id,
email: session?.user?.email,
organizationId: session?.organizationId,
displayName: session?.user?.name,
},
mismatchPolicy: "block_with_switch",
}}
/>That gives Emcy 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 Emcy owns vs what your app owns#
Emcy 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
embeddedAuth - 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 Emcy 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