Skip to main content

Enable auth for your MCP-powered apps with Logto

tip:

Explore Logto's AI solutions: authentication and authorization for MCP servers, AI agents, and apps.

This guide walks you through integrating Logto with your MCP server using mcp-auth and the MCP official SDK v2, allowing you to authenticate users and securely read their verified identity from the access token.

You'll learn how to:

  • Configure Logto as the authorization server for your MCP server.
  • Set up a “whoami” tool in your MCP server to return the current user's identity claims.
  • Test the flow with VS Code (MCP client).

After this tutorial, your MCP server will:

  • Authenticate users in your Logto tenant.
  • Verify JWT access tokens issued by Logto (signature, issuer, audience, and expiration).
  • Return the verified identity claims (sub, iss, aud, etc.) for the "whoami" tool invocation.

Once the integration is complete, you can replace VS Code with your own MCP client, such as a web app, to access the tools and resources exposed by your MCP server.

Sample code:

The complete, runnable sample code for this guide can be found in the mcp-auth/js repository:

  • whoami-express: the "whoami" server in this guide, on Node.js with Express.
  • whoami: the same server built fetch-native (web-standard Request / Response with Hono), deployable to Cloudflare Workers.

Prerequisites

  • A Logto Cloud (or self-hosted) tenant
  • Node.js >= 20 environment

Understanding the architecture

  • MCP server: The server that exposes tools and resources to MCP clients. Following the latest MCP specification, it acts as an OAuth 2.0 resource server that validates access tokens issued by Logto.
  • MCP client: A client used to initiate the authentication flow and test the integration. We'll use VS Code (with built-in MCP support) as the client in this guide.
  • Logto: Serves as the OpenID Connect provider (authorization server), manages user identities, and issues audience-bound JWT access tokens for your MCP server.

A non-normative sequence diagram illustrates the overall flow of the process:

note:

Due to MCP is quickly evolving, the above diagram may not be fully up to date. Please refer to the mcp-auth documentation for the latest information.

Set up app in Logto

Your MCP client needs to be registered as an application in Logto to initiate the authorization flow. We'll register VS Code as the client in this guide:

  1. Sign in to your Logto Console.

  2. Go ApplicationsCreate applicationCreate app without framework.

  3. Choose type: Native app.

  4. Fill in the app name (e.g., "VS Code") and other required fields, then click Create application.

  5. In the Settings / Redirect URIs section, add the following redirect URIs for VS Code, then save the changes:

    http://127.0.0.1
    https://vscode.dev/redirect
  6. Save and copy the App ID and Issuer endpoint.

Set up the MCP server

We will use the MCP official SDK v2 and mcp-auth to create an MCP server with a "whoami" tool that returns the current user's identity claims.

Create project and install dependencies

mkdir mcp-server
cd mcp-server
npm init -y
npm pkg set type="module"
npm pkg set main="whoami.js"
npm pkg set scripts.start="node whoami.js"
npm install @modelcontextprotocol/server @modelcontextprotocol/express @modelcontextprotocol/node express mcp-auth
  • @modelcontextprotocol/server is the core MCP SDK v2, which speaks web-standard Request / Response.
  • @modelcontextprotocol/express and @modelcontextprotocol/node adapt it to Express on Node.js.
  • mcp-auth supplies the token verifier and the OAuth discovery metadata for the MCP SDK.
note:

The MCP SDK v2 and mcp-auth are ESM only and require Node.js >= 20.

Register the MCP server as an API resource

The latest MCP specification requires access tokens to be bound to the resource they are issued for (RFC 8707), and mcp-auth enforces it: the token's aud claim must match your MCP server's resource identifier. In Logto, this is done by creating an API resource whose indicator matches your MCP server's URL:

  1. Sign in to your Logto Console.
  2. Go to API resourcesCreate API resource.
  3. Fill in the details, then click Create API resource:
    • API name: Enter a name, e.g., "Who am I".
    • API identifier: Enter http://localhost:3001/. It must match the resource identifier we'll configure in the MCP server.
Trailing slash in resource indicator:

Always include a trailing slash (/) in the resource indicator. Due to a current bug in the MCP official SDK, clients using the SDK will automatically append a trailing slash to resource identifiers when initiating auth requests. If your resource indicator doesn't include the trailing slash, resource validation will fail for those clients.

Configure MCP Auth with Logto

Declare your MCP server as a protected resource: its resource identifier and the authorization server it trusts. Remember to replace <your-logto-issuer-endpoint> with the issuer endpoint you copied earlier (found in the application details page under Endpoints & Credentials, e.g., https://my-project.logto.app/oidc).

In whoami.js:

import { MCPAuth } from 'mcp-auth';

const authIssuer = '<your-logto-issuer-endpoint>';

const mcpAuth = new MCPAuth({
protectedResourceMetadata: {
// The resource identifier; must match the API resource indicator registered in Logto
resource: 'http://localhost:3001/',
// The authorization server trusted by this MCP server
authorizationServer: { issuer: authIssuer, type: 'oidc' },
},
});

The authorization server metadata is fetched lazily when first needed and cached afterwards. The MCPAuth instance verifies JWT access tokens against Logto's JWKS — signature, issuer, audience, and expiration are all enforced. No hand-written token verification is needed.

Implement the "whoami" tool

Now, let's implement the "whoami" tool that returns the current user's identity claims from the verified access token. Use getAuthInfo to read the auth info that mcp-auth has verified from the tool's callback context.

import { McpServer } from '@modelcontextprotocol/server';
import { getAuthInfo } from 'mcp-auth';

// Factory function to create an MCP server instance
// Each request gets its own server instance, keeping requests isolated
const createMcpServer = () => {
const mcpServer = new McpServer({
name: 'WhoAmI',
version: '0.0.0',
});

// Add a tool to the server that returns the current user's information
mcpServer.registerTool(
'whoami',
{
description: 'Get the current user information',
},
(context) => {
const { claims } = getAuthInfo(context);
return {
content: [{ type: 'text', text: JSON.stringify(claims) }],
};
}
);

return mcpServer;
};

Wire up the server

Finally, serve the OAuth discovery documents (RFC 9728 / RFC 8414) so MCP clients can find your authorization server, and protect the MCP endpoint with the SDK's Bearer auth middleware.

import {
createMcpExpressApp,
mcpAuthMetadataRouter,
requireBearerAuth,
} from '@modelcontextprotocol/express';
import { toNodeHandler } from '@modelcontextprotocol/node';
import { createMcpHandler } from '@modelcontextprotocol/server';

const PORT = 3001;

// The MCP handler speaks web-standard Request / Response; `toNodeHandler` adapts it to Express
const mcpNodeHandler = toNodeHandler(createMcpHandler(createMcpServer));

const app = createMcpExpressApp();

// Serve the OAuth discovery documents (`/.well-known/...`), public by design
app.use(mcpAuthMetadataRouter(await mcpAuth.getAuthMetadataOptions()));

app.all(
'/',
// Require a valid Bearer token; the verified auth info flows to the handler via `req.auth`
requireBearerAuth(mcpAuth.getBearerAuthOptions()),
// `createMcpExpressApp` applies `express.json()`, which drains the request stream, so the
// parsed body is passed along explicitly
async (request, response) => mcpNodeHandler(request, response, request.body)
);

app.listen(PORT);

Run the server with:

npm start

Test the integration

  1. Start the MCP server:

    npm start
  2. Connect VS Code to your MCP server:

    1. Press Command + Shift + P (macOS) or Ctrl + Shift + P (Windows / Linux) to open the Command Palette.
    2. Type MCP: Add Server... and select it.
    3. Choose HTTP as the server type.
    4. Enter the MCP server URL: http://localhost:3001/
    5. When the OAuth flow starts, VS Code prompts for the Client ID: paste the App ID you copied earlier.
    6. Since it's a public client without an app secret, just press Enter to skip the secret.
    7. Complete the sign-in flow in your browser.
  3. After signing in, run the whoami tool in VS Code.

You should see the verified claims from the access token, such as:

{
"iss": "https://my-project.logto.app/oidc",
"sub": "user_XXXX",
"aud": "http://localhost:3001/",
"client_id": "<your-app-id>"
}

Further reading

Your MCP server now verifies inbound access tokens. As a next step, learn how it can securely call your downstream business APIs on behalf of users, without token passthrough or losing user context:

How an MCP server calls your API on behalf of users: a production token strategy