Secure MCP servers with Microsoft Entra ID — covering public clients for local servers and confidential clients for remote, internet-facing deployments.
Securing your Model Context Protocol (MCP) server is as critical as locking the front door of your house. Microsoft Entra ID provides a robust cloud-based identity and access management solution, ensuring that only authorized users and applications can interact with your MCP server.
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, every request to your server is verified — confirming the identity of the user or application making the request.
Entra ID uses OAuth 2.0 for authentication. Think of it like a valet key:
Analogy
OAuth 2.0 Concept
You (the car owner)
The User
Your car
The MCP Server
The Valet
Microsoft Entra ID
The Parking Attendant
The MCP Client
The Valet Key
The Access Token
MCP Client ──► Entra ID: Please sign inUser signs in with Entra ID credentialsEntra ID ──► MCP Client: Here is your access tokenMCP Client ──► MCP Server: Here is my access tokenMCP Server ──► Entra ID: Is this token valid?Entra ID ──► MCP Server: YesMCP Server ──► MCP Client: Token valid, here is the result
// Simplified for claritypublic 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(); return new AuthenticationService(logger, msalClient);}public async Task<string> AcquireTokenAsync(){ try { var accounts = await _msalClient.GetAccountsAsync(); var account = accounts.FirstOrDefault(); AuthenticationResult? result = null; if (account != null) { // Try silent authentication first (no user prompt) result = await _msalClient .AcquireTokenSilent(_scopes, account) .ExecuteAsync(); } else { // Fall back to interactive sign-in result = await _msalClient .AcquireTokenInteractive(_scopes) .ExecuteAsync(); } return result.AccessToken; } catch (Exception ex) { _logger.LogError(ex, "An error occurred while acquiring the token."); throw; }}
Scenario 2: Remote MCP server (confidential client)
For internet-facing MCP servers, use a confidential client and the Authorization Code Flow — the application’s secrets are never exposed to the browser.