Securing your Model Context Protocol (MCP) server is as important as locking the front door of your house. Leaving your MCP server open exposes your tools and data to unauthorized access, which can lead to security breaches. Microsoft Entra ID provides a robust cloud-based identity and access management solution, helping ensure that only authorized users and applications can interact with your MCP server. In this section, you’ll learn how to protect your AI workflows using Entra ID authentication.
By the end of this section, you will be able to:
- Understand the importance of securing MCP servers.
- Explain the basics of Microsoft Entra ID and OAuth 2.0 authentication.
- Recognize the difference between public and confidential clients.
- Implement Entra ID authentication in both local (public client) and remote (confidential client) MCP server scenarios.
- Apply security best practices when developing AI workflows.
Just as you wouldn't leave the front door of your house unlocked, you shouldn't leave your MCP server open for anyone to access. Securing your AI workflows is essential for building robust, trustworthy, and safe applications. This chapter will introduce you to using Microsoft Entra ID to secure your MCP servers, ensuring that only authorized users and applications can interact with your tools and data.
Imagine your MCP server has a tool that can send emails or access a customer database. An unsecured server would mean anyone could potentially use that tool, leading to unauthorized data access, spam, or other malicious activities.
By implementing authentication, you ensure that every request to your server is verified, confirming the identity of the user or application making the request. This is the first and most critical step in securing your AI workflows.
Microsoft Entra ID is a cloud-based identity and access management service. Think of it as a universal security guard for your applications. It handles the complex process of verifying user identities (authentication) and determining what they are allowed to do (authorization).
By using Entra ID, you can:
- Enable secure sign-in for users.
- Protect APIs and services.
- Manage access policies from a central location.
For MCP servers, Entra ID provides a robust and widely-trusted solution to manage who can access your server's capabilities.
Entra ID uses open standards like OAuth 2.0 to handle authentication. While the details can be complex, the core concept is simple and can be understood with an analogy.
Think of OAuth 2.0 like a valet service for your car. When you arrive at a restaurant, you don't give the valet your master key. Instead, you provide a valet key that has limited permissions—it can start the car and lock the doors, but it can't open the trunk or the glove compartment.
In this analogy:
- You are the User.
- Your car is the MCP Server with its valuable tools and data.
- The Valet is Microsoft Entra ID.
- The Parking Attendant is the MCP Client (the application trying to access the server).
- The Valet Key is the Access Token.
The access token is a secure string of text that the MCP client receives from Entra ID after you sign in. The client then presents this token to the MCP server with every request. The server can verify the token to ensure the request is legitimate and that the client has the necessary permissions, all without ever needing to handle your actual credentials (like your password).
Here’s how the process works in practice:
sequenceDiagram
actor User as 👤 User
participant Client as 🖥️ MCP Client
participant Entra as 🔐 Microsoft Entra ID
participant Server as 🔧 MCP Server
Client->>+User: Please sign in to continue.
User->>+Entra: Enters credentials (username/password).
Entra-->>Client: Here is your access token.
User-->>-Client: (Returns to the application)
Client->>+Server: I need to use a tool. Here is my access token.
Server->>+Entra: Is this access token valid?
Entra-->>-Server: Yes, it is.
Server-->>-Client: Token is valid. Here is the result of the tool.
Before we dive into the code, it's important to introduce a key component you'll see in the examples: the Microsoft Authentication Library (MSAL).
MSAL is a library developed by Microsoft that makes it much easier for developers to handle authentication. Instead of you having to write all the complex code to handle security tokens, manage sign-ins, and refresh sessions, MSAL takes care of the heavy lifting.
Using a library like MSAL is highly recommended because:
- It's Secure: It implements industry-standard protocols and security best practices, reducing the risk of vulnerabilities in your code.
- It Simplifies Development: It abstracts away the complexity of the OAuth 2.0 and OpenID Connect protocols, allowing you to add robust authentication to your application with just a few lines of code.
- It's Maintained: Microsoft actively maintains and updates MSAL to address new security threats and platform changes.
MSAL supports a wide variety of languages and application frameworks, including .NET, JavaScript/TypeScript, Python, Java, Go, and mobile platforms like iOS and Android. This means you can use the same consistent authentication patterns across your entire technology stack.
To learn more about MSAL, you can check out the official MSAL overview documentation.
Now, let's walk through how to secure a local MCP server (one that communicates over stdio) using Entra ID. This example uses a public client, which is suitable for applications running on a user's machine, like a desktop app or a local development server.
In this scenario, we'll look at an MCP server that runs locally, communicates over stdio, and uses Entra ID to authenticate the user before allowing access to its tools. The server will have a single tool that fetches the user's profile information from the Microsoft Graph API.
Before writing any code, you need to register your application in Microsoft Entra ID. This tells Entra ID about your application and grants it permission to use the authentication service.
- Navigate to the Microsoft Entra portal.
- Go to App registrations and click New registration.
- Give your application a name (e.g., "My Local MCP Server").
- For Supported account types, select Accounts in this organizational directory only.
- You can leave the Redirect URI blank for this example.
- Click Register.
Once registered, take note of the Application (client) ID and Directory (tenant) ID. You'll need these in your code.
Let's look at the key parts of the code that handle authentication. The full code for this example is available in the Entra ID - Local - WAM folder of the mcp-auth-servers GitHub repository.
AuthenticationService.cs
This class is responsible for handling the interaction with Entra ID.
CreateAsync: This method initializes thePublicClientApplicationfrom the MSAL (Microsoft Authentication Library). It's configured with your application'sclientIdandtenantId.WithBroker: This enables the use of a broker (like the Windows Web Account Manager), which provides a more secure and seamless single sign-on experience.AcquireTokenAsync: This is the core method. It first tries to get a token silently (meaning the user won't have to sign in again if they already have a valid session). If a silent token can't be acquired, it will prompt the user to sign in interactively.
// Simplified for clarity
public static async Task<AuthenticationService> CreateAsync(ILogger<AuthenticationService> logger)
{
var msalClient = PublicClientApplicationBuilder
.Create(_clientId) // Your Application (client) ID
.WithAuthority(AadAuthorityAudience.AzureAdMyOrg)
.WithTenantId(_tenantId) // Your Directory (tenant) ID
.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.Windows))
.Build();
// ... cache registration ...
return new AuthenticationService(logger, msalClient);
}
public async Task<string> AcquireTokenAsync()
{
try
{
// Try silent authentication first
var accounts = await _msalClient.GetAccountsAsync();
var account = accounts.FirstOrDefault();
AuthenticationResult? result = null;
if (account != null)
{
result = await _msalClient.AcquireTokenSilent(_scopes, account).ExecuteAsync();
}
else
{
// If no account, or silent fails, go interactive
result = await _msalClient.AcquireTokenInteractive(_scopes).ExecuteAsync();
}
return result.AccessToken;
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while acquiring the token.");
throw; // Optionally rethrow the exception for higher-level handling
}
}Program.cs
This is where the MCP server is set up and the authentication service is integrated.
AddSingleton<AuthenticationService>: This registers theAuthenticationServicewith the dependency injection container, so it can be used by other parts of the application (like our tool).GetUserDetailsFromGraphtool: This tool requires an instance ofAuthenticationService. Before it does anything, it callsauthService.AcquireTokenAsync()to get a valid access token. If authentication is successful, it uses the token to call the Microsoft Graph API and fetch the user's details.
// Simplified for clarity
[McpServerTool(Name = "GetUserDetailsFromGraph")]
public static async Task<string> GetUserDetailsFromGraph(
AuthenticationService authService)
{
try
{
// This will trigger the authentication flow
var accessToken = await authService.AcquireTokenAsync();
// Use the token to create a GraphServiceClient
var graphClient = new GraphServiceClient(
new BaseBearerTokenAuthenticationProvider(new TokenProvider(authService)));
var user = await graphClient.Me.GetAsync();
return System.Text.Json.JsonSerializer.Serialize(user);
}
catch (Exception ex)
{
return $"Error: {ex.Message}";
}
}- When the MCP client tries to use the
GetUserDetailsFromGraphtool, the tool first callsAcquireTokenAsync. AcquireTokenAsynctriggers the MSAL library to check for a valid token.- If no token is found, MSAL, through the broker, will prompt the user to sign in with their Entra ID account.
- Once the user signs in, Entra ID issues an access token.
- The tool receives the token and uses it to make a secure call to the Microsoft Graph API.
- The user's details are returned to the MCP client.
This process ensures that only authenticated users can use the tool, effectively securing your local MCP server.
When your MCP server is running on a remote machine (like a cloud server) and communicates over a protocol like HTTP Streaming, the security requirements are different. In this case, you should use a confidential client and the Authorization Code Flow. This is a more secure method because the application's secrets are never exposed to the browser.
This example uses a TypeScript-based MCP server that uses Express.js to handle HTTP requests.
The setup in Entra ID is similar to the public client, but with one key difference: you need to create a client secret.
- Navigate to the Microsoft Entra portal.
- In your app registration, go to the Certificates & secrets tab.
- Click New client secret, give it a description, and click Add.
- Important: Copy the secret value immediately. You will not be able to see it again.
- You also need to configure a Redirect URI. Go to the Authentication tab, click Add a platform, select Web, and enter the redirect URI for your application (e.g.,
http://localhost:3001/auth/callback).
⚠️ Important Security Note: For production applications, Microsoft strongly recommends using secretless authentication methods such as Managed Identity or Workload Identity Federation instead of client secrets. Client secrets pose security risks as they can be exposed or compromised. Managed identities provide a more secure approach by eliminating the need to store credentials in your code or configuration.For more information about managed identities and how to implement them, see the Managed identities for Azure resources overview.
This example uses a session-based approach. When the user authenticates, the server stores the access token and refresh token in a session and gives the user a session token. This session token is then used for subsequent requests. The full code for this example is available in the Entra ID - Confidential client folder of the mcp-auth-servers GitHub repository.
Server.ts
This file sets up the Express server and the MCP transport layer.
requireBearerAuth: This is middleware that protects the/sseand/messageendpoints. It checks for a valid bearer token in theAuthorizationheader of the request.EntraIdServerAuthProvider: This is a custom class that implements theMcpServerAuthorizationProviderinterface. It's responsible for handling the OAuth 2.0 flow./auth/callback: This endpoint handles the redirect from Entra ID after the user has authenticated. It exchanges the authorization code for an access token and a refresh token.
// Simplified for clarity
const app = express();
const { server } = createServer();
const provider = new EntraIdServerAuthProvider();
// Protect the SSE endpoint
app.get("/sse", requireBearerAuth({
provider,
requiredScopes: ["User.Read"]
}), async (req, res) => {
// ... connect to the transport ...
});
// Protect the message endpoint
app.post("/message", requireBearerAuth({
provider,
requiredScopes: ["User.Read"]
}), async (req, res) => {
// ... handle the message ...
});
// Handle the OAuth 2.0 callback
app.get("/auth/callback", (req, res) => {
provider.handleCallback(req.query.code, req.query.state)
.then(result => {
// ... handle success or failure ...
});
});Tools.ts
This file defines the tools that the MCP server provides. The getUserDetails tool is similar to the one in the previous example, but it gets the access token from the session.
// Simplified for clarity
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name } = request.params;
const context = request.params?.context as { token?: string } | undefined;
const sessionToken = context?.token;
if (name === ToolName.GET_USER_DETAILS) {
if (!sessionToken) {
throw new AuthenticationError("Authentication token is missing or invalid. Ensure the token is provided in the request context.");
}
// Get the Entra ID token from the session store
const tokenData = tokenStore.getToken(sessionToken);
const entraIdToken = tokenData.accessToken;
const graphClient = Client.init({
authProvider: (done) => {
done(null, entraIdToken);
}
});
const user = await graphClient.api('/me').get();
// ... return user details ...
}
});auth/EntraIdServerAuthProvider.ts
This class handles the logic for:
- Redirecting the user to the Entra ID sign-in page.
- Exchanging the authorization code for an access token.
- Storing the tokens in the
tokenStore. - Refreshing the access token when it expires.
- When a user first tries to connect to the MCP server, the
requireBearerAuthmiddleware will see that they don't have a valid session and will redirect them to the Entra ID sign-in page. - The user signs in with their Entra ID account.
- Entra ID redirects the user back to the
/auth/callbackendpoint with an authorization code. - The server exchanges the code for an access token and a refresh token, stores them, and creates a session token which is sent to the client.
- The client can now use this session token in the
Authorizationheader for all future requests to the MCP server. - When the
getUserDetailstool is called, it uses the session token to look up the Entra ID access token and then uses that to call the Microsoft Graph API.
This flow is more complex than the public client flow, but is required for internet-facing endpoints. Since remote MCP servers are accessible over the public internet, they need stronger security measures to protect against unauthorized access and potential attacks.
- Always use HTTPS: Encrypt communication between the client and server to protect tokens from being intercepted.
- Implement Role-Based Access Control (RBAC): Don't just check if a user is authenticated; check what they are authorized to do. You can define roles in Entra ID and check for them in your MCP server.
- Monitor and audit: Log all authentication events so you can detect and respond to suspicious activity.
- Handle rate limiting and throttling: Microsoft Graph and other APIs implement rate limiting to prevent abuse. Implement exponential backoff and retry logic in your MCP server to gracefully handle HTTP 429 (Too Many Requests) responses. Consider caching frequently accessed data to reduce API calls.
- Secure token storage: Store access tokens and refresh tokens securely. For local applications, use the system's secure storage mechanisms. For server applications, consider using encrypted storage or secure key management services like Azure Key Vault.
- Token expiration handling: Access tokens have a limited lifetime. Implement automatic token refresh using refresh tokens to maintain seamless user experience without requiring re-authentication.
- Consider using Azure API Management: While implementing security directly in your MCP server gives you fine-grained control, API Gateways like Azure API Management can handle many of these security concerns automatically, including authentication, authorization, rate limiting, and monitoring. They provide a centralized security layer that sits between your clients and your MCP servers. For more details on using API Gateways with MCP, see our Azure API Management Your Auth Gateway For MCP Servers.
- Securing your MCP server is crucial for protecting your data and tools.
- Microsoft Entra ID provides a robust and scalable solution for authentication and authorization.
- Use a public client for local applications and a confidential client for remote servers.
- The Authorization Code Flow is the most secure option for web applications.
- Think about an MCP server you might build. Would it be a local server or a remote server?
- Based on your answer, would you use a public or confidential client?
- What permission would your MCP server request for performing actions against Microsoft Graph?
Navigate to the Microsoft Entra portal. Register a new application for your MCP server. Record the Application (client) ID and Directory (tenant) ID.
- Follow the code example to integrate MSAL (Microsoft Authentication Library) for user authentication.
- Test the authentication flow by calling the MCP tool that fetches user details from Microsoft Graph.
- Register a confidential client in Entra ID and create a client secret.
- Configure your Express.js MCP server to use the Authorization Code Flow.
- Test the protected endpoints and confirm token-based access.
- Enable HTTPS for your local or remote server.
- Implement role-based access control (RBAC) in your server logic.
- Add token expiration handling and secure token storage.
-
MSAL Overview Documentation
Learn how the Microsoft Authentication Library (MSAL) enables secure token acquisition across platforms:
MSAL Overview on Microsoft Learn -
Azure-Samples/mcp-auth-servers GitHub Repository
Reference implementations of MCP servers demonstrating authentication flows:
Azure-Samples/mcp-auth-servers on GitHub -
Managed Identities for Azure Resources Overview
Understand how to eliminate secrets by using system- or user-assigned managed identities:
Managed Identities Overview on Microsoft Learn -
Azure API Management: Your Auth Gateway for MCP Servers
A deep dive into using APIM as a secure OAuth2 gateway for MCP servers:
Azure API Management Your Auth Gateway For MCP Servers -
Microsoft Graph Permissions Reference
Comprehensive list of delegated and application permissions for Microsoft Graph:
Microsoft Graph Permissions Reference
After completing this section, you will be able to:
- Articulate why authentication is critical for MCP servers and AI workflows.
- Set up and configure Entra ID authentication for both local and remote MCP server scenarios.
- Choose the appropriate client type (public or confidential) based on your server’s deployment.
- Implement secure coding practices, including token storage and role-based authorization.
- Confidently protect your MCP server and its tools from unauthorized access.