Already have users? Mint MCP tokens without replacing your auth
If your product already has its own login system, you don't need to send your users through a second sign-in screen just to authorize an MCP server. mcpauth can mint MCP-compatible OAuth tokens directly for users your backend has already authenticated.
Who this is for
This guide is for a SaaS product that already has its own login system (email/password, SSO, whatever) and is bolting an MCP server onto users it already authenticates — for example a project-management app adding an MCP server so its users can drive it from Claude or another MCP client, using the same account they already have.
You're not asking those users to create a separate identity anywhere. Your app already knows who they are. mcpauth's job here is narrower: turn "this user, already logged in, according to you" into a valid MCP access token, without asking them to authenticate a second time.
How it works
Your backend already knows who its logged-in user is — it authenticated them through your own login system, not mcpauth's. When that user wants to use your MCP server, your backend calls mcpauth's server-to-server token exchange directly, either via the SDK's mintToken() helper or the raw POST /api/oauth/token/exchangeendpoint. You pass that user's own opaque ID as the subject.
mcpauth never authenticates that identity itself — it trusts your backend's vouching for it, the same way it trusts you with your registration secret. There's no redirect, no consent screen, no second login. Your backend calls the endpoint, gets back an access token, and hands it to your MCP server or MCP client on that user's behalf.
This is deliberately different from GET /oauth/authorize, mcpauth's browser-facing OAuth 2.1 flow where a human signs in via GitHub and approves a consent screen. That flow is for cases where a GitHub identity is the natural end-user — think an open developer tool with no login system of its own. Here, your product already has that login system, so routing users through GitHub-via-mcpauth would just be a redundant second identity nobody asked for.
Code sample
A typical integration lives in your existing backend, right after you've already checked the user's session:
import { mintToken } from "getmcpauth";
// Inside a route your app already protects with its own session/auth check
app.post("/api/mcp/token", requireLoggedInUser, async (req, res) => {
const user = req.user; // however your app already identifies the caller
const { accessToken, expiresIn, refreshToken } = await mintToken({
registrationSecret: process.env.MCPAUTH_SECRET!,
clientId: MCP_CLIENT_ID, // the client_id your MCP client registered with
subject: user.id, // your own user's opaque id — mcpauth just trusts it
scopes: ["projects:read", "projects:write"],
});
// Hand the token to your MCP client / MCP server however fits your app —
// e.g. return it to a frontend that configures the MCP client, or inject
// it directly if your backend is also standing up the MCP session.
res.json({ accessToken, expiresIn, refreshToken });
});The raw HTTP call, if you're not using the SDK, is the same request under the hood:
POST /api/oauth/token/exchange
Authorization: Bearer <project registration secret>
Content-Type: application/json
{
"client_id": "<registered client_id>",
"subject": "<your user's own id>",
"scopes": ["projects:read", "projects:write"]
}Both return the same token shape: { access_token, token_type: "Bearer", expires_in, refresh_token, scope }.
Security notes
- Scopes are entirely up to you.mcpauth doesn't define what scopes mean for your product — you decide what
projects:reador any other scope grants, and your MCP server is responsible for enforcing it. - Tokens expire. Access tokens are valid for 1 hour by default; refresh tokens for 30 days. Use the standard
grant_type=refresh_tokenflow againstPOST /api/oauth/tokento get a new access token without minting again from scratch. - Tokens are revocable. Call
POST /api/oauth/revokewith the token and yourclient_idif a user's access needs to be cut off immediately — for example on logout or account deactivation in your own system. - Guard the endpoint you build. The route that calls
mintToken()should sit behind whatever session/auth check your product already uses — it's minting a token on a specific user's behalf, so only that user's own authenticated request should be able to trigger it for their ownsubject.
Which flow should you use?
Use /oauth/authorize (GitHub login) when your MCP server's natural identity is a GitHub account — open developer tools, CLIs, and MCP servers with no login system of their own all fit here. mcpauth handles the sign-in and consent screen for you.
Use /api/oauth/token/exchange (mintToken()) when your product already has its own users and its own login. Your backend vouches for the identity it already authenticated, and mcpauth mints a token for it directly — no redirect, no second consent screen, no new identity for your users to manage.
See also the discovery document for the full list of endpoints, or the dashboard to create a project and get a registration secret.