Hosting Remote MCP Servers Securely on AWS and Azure: Token Validation, Gateways and Network Placement

A local MCP server inherits the trust of the laptop it runs on. A remote one does not. The moment a tool server gets a URL, it becomes an API that a language model can drive, reachable by anything that can route to it, and the questions change from “does the tool work” to “who is calling, what may they do, and what can reach this endpoint at all”.

This article is confined to those three questions: where to host a remote MCP server on AWS and Azure, how OAuth works for MCP and what the server must check itself, and where the endpoint should sit on the network. It does not cover tool design or the protocol basics, which earlier posts on this blog already do.

The position reflects 14 July 2026: MCP specification revision 2025-11-25, the Python SDK at 1.28.1 and the C# SDK at 1.4.0. The Python server below was run against thirteen positive and negative checks and the results are shown. The C# version and the cloud deployments are flagged where I could not verify them.

What the specification asks of a server

Authorization is optional in MCP. When an HTTP server does implement it, the 2025-11-25 revision is specific about the roles. The MCP server is an OAuth 2.1 resource server. It does not issue tokens, host login pages or store passwords. A separate authorization server does that: Microsoft Entra ID, Amazon Cognito, Okta, Auth0 or whichever one your organisation already runs.

  • Publish protected resource metadata (RFC 9728). A JSON document, served by the MCP server, that names the authorization servers it trusts. This is how a client that has never seen your server finds out where to sign in.
  • Challenge properly. A request without a valid token gets HTTP 401 with a WWW-Authenticate header pointing at that metadata. A valid token without enough scope gets 403 with insufficient_scope.
  • Validate the audience. The server must accept only tokens issued for itself. A valid token from the right issuer, minted for a different API, must be refused.
  • Do not pass tokens through. The token a client presents is for the MCP server. If a tool calls another API, it uses a separate token obtained for that API.
  • Expect the resource parameter. Clients must send the server’s canonical URL as a resource indicator (RFC 8707) when requesting a token, so that the authorization server can bind the token to it.
%%{init: {'theme':'base','themeVariables':{'actorBkg':'#FFF3E0','actorBorder':'#FF9900','actorTextColor':'#232F3E','signalColor':'#0073BB','signalTextColor':'#0F172A','noteBkgColor':'#FFF8E7','noteBorderColor':'#FF9900','noteTextColor':'#232F3E','labelBoxBkgColor':'#FFF3E0','labelBoxBorderColor':'#FF9900','labelTextColor':'#232F3E','loopTextColor':'#232F3E','activationBkgColor':'#EBF8FF','activationBorderColor':'#0073BB','lineColor':'#232F3E','textColor':'#232F3E'}}}%%
sequenceDiagram
    participant C as MCP client
    participant S as MCP server
    participant A as Authorization server
    C->>S: POST /mcp, no token
    S-->>C: 401 + resource_metadata URL
    C->>S: GET protected resource metadata
    S-->>C: Trusted authorization servers, scopes
    C->>A: Discover endpoints, authorise with PKCE + resource
    A-->>C: Access token, audience = this server
    C->>S: POST /mcp + Bearer token
    S-->>C: 200 MCP response

Client registration is the part that changed most in this revision. Pre-registered clients come first, then Client ID Metadata Documents, where the client identifier is an HTTPS URL the authorization server can fetch. Dynamic Client Registration is kept for backward compatibility. That ordering matters on Azure, as a later section shows.

Four layers, four different checks

The common failure is not an absence of security. It is one layer of it, with the assumption that it covers the rest. A gateway that validates a signature does not know which tool is being called. A private subnet does not know who the caller is.

Diagram of an MCP request passing through four layers, edge, gateway, MCP server and downstream API, with the check each layer performs and the AWS and Azure services that provide it.
One request, four layers. The highlighted layer is the only one that knows which tool is being called.

The rest of the article works from the inside out: first the checks that only the server can make, then the hosting options and what each one takes off your hands, then the network.

A server that validates its own tokens

Token validation is a small amount of code with a large number of ways to get it wrong. This verifier pins the signing algorithm, checks issuer and audience, requires the claims it depends on, and fetches signing keys from the issuer’s JWKS endpoint with caching.

"""token_verifier.py - validates JWT access tokens for an MCP resource server."""
import asyncio
import logging

import jwt
from jwt import PyJWKClient
from mcp.server.auth.provider import AccessToken, TokenVerifier

log = logging.getLogger("mcp.auth")


class JwksTokenVerifier(TokenVerifier):
    """Accepts a token only if it was signed by the expected issuer AND minted for this server."""

    def __init__(self, issuer: str, audiences: list[str], jwks_uri: str) -> None:
        self._issuer = issuer
        self._audiences = audiences
        # Signing keys are cached and re-fetched on an unknown key id, which covers key rotation.
        self._jwks = PyJWKClient(jwks_uri, cache_keys=True, lifespan=3600, timeout=5)

    async def verify_token(self, token: str) -> AccessToken | None:
        try:
            # PyJWKClient does blocking I/O on a cache miss; keep it off the event loop.
            claims = await asyncio.to_thread(self._decode, token)
        except jwt.PyJWTError as exc:
            # Log the reason, never the token.
            log.warning("token rejected: %s", type(exc).__name__)
            return None

        # Authorization servers disagree on the claim name: 'scope' (RFC 9068) or 'scp' (Entra ID).
        scopes = str(claims.get("scope") or claims.get("scp") or "").split()
        client_id = claims.get("client_id") or claims.get("azp") or claims.get("appid") or "unknown"
        return AccessToken(
            token=token,
            client_id=str(client_id),
            scopes=scopes,
            expires_at=int(claims["exp"]),
            subject=str(claims["sub"]),
            claims={"iss": claims["iss"], "aud": claims["aud"]},
        )

    def _decode(self, token: str) -> dict:
        key = self._jwks.get_signing_key_from_jwt(token).key
        return jwt.decode(
            token,
            key,
            algorithms=["RS256"],            # pinned: never read the algorithm from the token
            issuer=self._issuer,
            audience=self._audiences,        # the audience check is what stops token reuse
            options={"require": ["exp", "iss", "aud", "sub"]},
            leeway=30,
        )

The server wires that verifier into the SDK, declares the scope every request needs, and adds a second, finer check inside each tool. It is stateless streamable HTTP on port 8000 at /mcp, which is the layout AgentCore Runtime expects and which also runs unchanged in a container on either cloud.

"""server.py - a remote MCP server acting as an OAuth 2.1 resource server."""
import os
import re

from mcp.server.auth.middleware.auth_context import get_access_token
from mcp.server.auth.settings import AuthSettings
from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings

from token_verifier import JwksTokenVerifier

ISSUER = os.environ["MCP_ISSUER"]                    # e.g. https://login.microsoftonline.com/<tenant>/v2.0
JWKS_URI = os.environ["MCP_JWKS_URI"]
RESOURCE_URL = os.environ["MCP_RESOURCE_URL"]        # the public URL clients use, e.g. https://mcp.example.com/mcp
AUDIENCES = os.environ.get("MCP_AUDIENCES", RESOURCE_URL).split(",")
ALLOWED_HOSTS = os.environ["MCP_ALLOWED_HOSTS"].split(",")           # e.g. mcp.example.com
ALLOWED_ORIGINS = [o for o in os.environ.get("MCP_ALLOWED_ORIGINS", "").split(",") if o]

READ, WRITE = "orders.read", "orders.write"
ORDER_ID = re.compile(r"ORD-\d{3,10}")

mcp = FastMCP(
    "orders",
    host="0.0.0.0",
    port=8000,
    stateless_http=True,          # no session affinity: any instance can serve any request
    json_response=True,           # plain JSON replies; friendlier to gateways that buffer
    token_verifier=JwksTokenVerifier(ISSUER, AUDIENCES, JWKS_URI),
    auth=AuthSettings(
        issuer_url=ISSUER,                    # advertised in the protected resource metadata
        resource_server_url=RESOURCE_URL,
        required_scopes=[READ],               # enforced on every HTTP request, before any MCP code runs
    ),
    transport_security=TransportSecuritySettings(
        enable_dns_rebinding_protection=True,
        allowed_hosts=ALLOWED_HOSTS,
        allowed_origins=ALLOWED_ORIGINS,
    ),
)


def _require(scope: str) -> str:
    """Per-tool authorization. Returns the caller's subject for the audit trail."""
    token = get_access_token()
    if token is None or scope not in token.scopes:
        raise PermissionError(f"This tool requires the '{scope}' scope.")
    return token.subject or token.client_id


def _order_id(value: str) -> str:
    if not ORDER_ID.fullmatch(value):
        raise ValueError("order_id must look like ORD-10042")
    return value


@mcp.tool()
def get_order_status(order_id: str) -> dict:
    """Return the fulfilment status of one order. order_id looks like ORD-10042."""
    _require(READ)
    return {"order_id": _order_id(order_id), "status": "SHIPPED"}


@mcp.tool()
def cancel_order(order_id: str) -> dict:
    """Cancel one order. Irreversible. order_id looks like ORD-10042."""
    subject = _require(WRITE)
    # Downstream calls use this server's own credential, never the caller's token.
    return {"order_id": _order_id(order_id), "status": "CANCELLED", "cancelled_by": subject}


app = mcp.streamable_http_app()

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

What the tests showed

The test starts a throwaway issuer, an RSA key with a JWKS endpoint, and sends the real server a series of requests over HTTP.

RequestResultWhy it matters
No token401 with resource_metadata in WWW-AuthenticateA client can discover where to sign in
Metadata documentLists the resource URL, the issuer and orders.readServed by the SDK at /.well-known/oauth-protected-resource/mcp
Valid signature, wrong audience401A token for another API is useless here
Expired token401Lifetime is enforced, with 30 seconds of leeway
Wrong issuer, or signed by an unknown key401Only the configured issuer’s keys count
Unsigned token (alg: none)401The algorithm is pinned, not read from the token
Valid token without orders.read403 with insufficient_scopeCoarse scope is enforced before any MCP code runs
Valid token, foreign Host header421DNS rebinding protection is on
Read token calling get_order_statusTool resultThe normal path
Read token calling cancel_orderHTTP 200, tool result with isError: truePer-tool scope is enforced inside the tool
Read and write token calling cancel_orderTool result, with the caller’s subject recordedThe audit trail has a real identity

Two results deserve attention. First, the SDK’s challenge carries the metadata URL but no scope parameter, which the specification recommends. Clients then fall back to requesting everything in scopes_supported, so keep that list minimal. Second, a per-tool denial is not an HTTP 403. The request already passed the transport check, so the refusal comes back as a tool error inside a 200 response, and a client cannot use it to trigger a step-up sign-in. If you need step-up behaviour, split read and write tools across two servers, each with its own required scope.

💡
HOW THIS WAS CHECKED

Python 3.13, mcp 1.28.1 and PyJWT 2.15.1. The server ran under Uvicorn on a local port and every row in the table above is an assertion in test_server.py. Nothing was deployed to AWS or Azure for this article, so the platform behaviour described below comes from vendor documentation.

The same rules in ASP.NET Core with Entra ID

The C# SDK expresses the same design with ASP.NET Core’s own authentication stack. JWT bearer validation does the cryptography, the SDK’s MCP scheme writes the challenge and serves the metadata document, and ordinary authorization policies carry the scopes. AddAuthorizationFilters() makes [Authorize] work on individual tools.

// dotnet add package ModelContextProtocol.AspNetCore --version 1.4.0
// dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
using System.ComponentModel;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using ModelContextProtocol.AspNetCore.Authentication;
using ModelContextProtocol.Server;

var builder = WebApplication.CreateBuilder(args);

string Required(string key) =>
    builder.Configuration[key] ?? throw new InvalidOperationException($"{key} is not configured");

string tenantId = Required("Mcp:TenantId");
string clientId = Required("Mcp:ClientId");          // the app registration that represents THIS server
string resourceUrl = Required("Mcp:ResourceUrl");    // the public URL clients use
string authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        // The MCP scheme writes the 401 challenge and serves the protected resource metadata.
        options.DefaultChallengeScheme = McpAuthenticationDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.Authority = authority;
        options.MapInboundClaims = false;            // keep the claim name "scp" as issued
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = authority,
            ValidateAudience = true,
            ValidAudiences = [clientId, $"api://{clientId}"],   // v2 and v1 token formats
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidAlgorithms = [SecurityAlgorithms.RsaSha256],
            ClockSkew = TimeSpan.FromSeconds(30),
        };
    })
    .AddMcp(options =>
    {
        options.ResourceMetadata = new()
        {
            Resource = resourceUrl,
            AuthorizationServers = { authority },
            ScopesSupported = [$"api://{clientId}/orders.read"],
        };
    });

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("orders.read", p => p.RequireAuthenticatedUser().RequireAssertion(c => HasScope(c, "orders.read")))
    .AddPolicy("orders.write", p => p.RequireAuthenticatedUser().RequireAssertion(c => HasScope(c, "orders.write")));

builder.Services.AddMcpServer()
    .WithHttpTransport(options => options.Stateless = true)   // no session affinity
    .AddAuthorizationFilters()                                 // honours [Authorize] on individual tools
    .WithTools<OrderTools>();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/healthz", () => Results.Ok()).AllowAnonymous();
app.MapMcp("/mcp").RequireAuthorization("orders.read");       // coarse scope on every request

app.Run();

static bool HasScope(AuthorizationHandlerContext context, string scope) =>
    (context.User.FindFirst("scp")?.Value ?? string.Empty)
        .Split(' ', StringSplitOptions.RemoveEmptyEntries)
        .Contains(scope);

[McpServerToolType]
public sealed class OrderTools
{
    [McpServerTool, Description("Return the fulfilment status of one order. orderId looks like ORD-10042.")]
    public static object GetOrderStatus(string orderId) =>
        new { orderId = Validate(orderId), status = "SHIPPED" };

    [McpServerTool, Description("Cancel one order. Irreversible. orderId looks like ORD-10042.")]
    [Authorize(Policy = "orders.write")]                       // per-tool scope
    public static object CancelOrder(string orderId) =>
        new { orderId = Validate(orderId), status = "CANCELLED" };

    private static string Validate(string orderId) =>
        System.Text.RegularExpressions.Regex.IsMatch(orderId, @"^ORD-\d{3,10}$")
            ? orderId
            : throw new ArgumentException("orderId must look like ORD-10042");
}
⚠️
THIS C# HAS NOT BEEN COMPILED

I had no .NET SDK or NuGet access while writing this. The listing follows the ProtectedMcpServer sample at tag v1.4.0 of the MCP C# SDK, and I checked the extension methods and the metadata type against that tag’s source. Build it before you rely on it, and test it with the same negative cases as the Python server: wrong audience, expired token, missing scope.

Entra ID brings three behaviours that surprise teams coming from the specification text.

  • No Dynamic Client Registration. Microsoft’s documentation states that Entra ID does not support it. Every MCP client needs an app registration, and the practical route is to pre-authorise the known clients on the server’s registration. Clients that cannot show a consent prompt will not work any other way.
  • Scopes identify the resource. Entra ID works out the target API from the scope, for example api://<client-id>/orders.read. MCP clients also send the resource parameter, and when the two do not resolve to the same application the sign-in fails with AADSTS9010010. Reports of exactly that appeared in the week before this article. The fix discussed there is to add the server’s URL as an additional Application ID URI on the registration.
  • Two audience formats. Depending on the token version configured on the registration, aud is either the client id or the api:// URI. Accept both explicitly. Do not turn audience validation off to make an error go away.

Where to host it

Every option below can run the server above. They differ in who validates the token before your code runs, how they scale, and what they cost when nobody is calling.

Table of eight hosting options for remote MCP servers on AWS and Azure in July 2026, showing which component validates the access token and what each option suits.
Hosting options on 14 July 2026 and the component that validates the token first.

On AWS, the choice is mainly between the managed agent platform and general-purpose compute.

  • AgentCore Runtime in MCP mode runs your container with per-session isolation and validates inbound identity for you. A runtime is configured for IAM SigV4 or for JWT bearer tokens, not both at once. The JWT authorizer takes a discovery URL plus allowed audiences, clients and scopes. The billing model is covered in the AgentCore Runtime vs. Lambda article.
  • AgentCore Gateway puts one MCP endpoint in front of many targets, including Lambda functions, OpenAPI services and other MCP servers, with a JWT authorizer or IAM on the way in. Choose it when the problem is a sprawl of tools more than a single server.
  • Lambda behind API Gateway suits stateless servers with short tool calls. An authorizer can reject anonymous traffic at the edge. Remember the integration time limit, 29 seconds by default on REST APIs. A Lambda function URL on its own offers only IAM or no authentication, so OAuth validation falls entirely to your code.
  • ECS on Fargate behind a load balancer is the option for long-running streams and servers that need sessions. You pay for running tasks whether or not they are called, and your code does all the validation.

On Azure, the split is between Functions, containers and a gateway in front of either.

  • Azure Functions MCP extension, generally available since January 2026, exposes functions as tools through triggers and includes built-in authorization with Entra ID, including the 401 challenge and the metadata document.
  • Azure Functions custom handlers host a server built with an official MCP SDK, such as either listing in this article. In July 2026 this was a public preview, limited to stateless streamable HTTP on the Flex Consumption plan.
  • Container Apps or App Service run any container. App Service authentication can take over the OAuth challenge and metadata for an MCP server, a capability still marked preview. It authorises the server as a whole, not individual tools.
  • API Management in front of an existing MCP server adds token validation with the validate-azure-ad-token policy, rate limits and logging. It handles MCP tools only, not resources or prompts, and policies apply to the whole server, not to single tools.
⚠️
A PLATFORM CHECK IS NOT YOUR CHECK

When a platform or gateway validates the token, find out what reaches your code. AgentCore Runtime forwards the Authorization header to the container only if you allowlist it. Without the token, the server cannot enforce per-tool scopes or record who made a call. With it, validate again inside the server: the signature check is cheap, and the audience check is the one control that survives a misconfigured gateway.

Network placement

OAuth answers who is calling. Network placement decides who can try. The right placement follows from who the callers are.

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FFF3E0','primaryTextColor':'#232F3E','primaryBorderColor':'#FF9900','lineColor':'#232F3E','clusterBkg':'#F8FAFC','clusterBorder':'#CBD5E1','edgeLabelBackground':'#FFFFFF','nodeTextColor':'#232F3E'}}}%%
flowchart TD
    Q1{Who calls this server?}
    Q1 -- "Agents in the same
network or account" --> P1[Private endpoint only
no public DNS record] Q1 -- "Agents in another cloud
or a partner" --> P2[Public endpoint behind
gateway and WAF] Q1 -- "People using IDEs
and desktop clients" --> P3[Public endpoint with
interactive sign-in] P1 --> A1[OAuth still required
client credentials flow] P2 --> A2[Client credentials flow
IP allowlist as a second factor] P3 --> A3[Authorization code + PKCE
pre-registered clients] class Q1 edge class P1 store class P2,P3 compute class A1,A2,A3 mesh classDef compute fill:#FFF3E0,stroke:#FF9900,stroke-width:2px,color:#232F3E classDef edge fill:#EBF8FF,stroke:#0073BB,stroke-width:2px,color:#004C74 classDef store fill:#F0FFF4,stroke:#1D8102,stroke-width:2px,color:#145202 classDef alert fill:#FEF2F2,stroke:#D13212,stroke-width:2px,color:#991B1B classDef mesh fill:#F3E8FF,stroke:#7C3AED,stroke-width:2px,color:#4C1D95
  • Private by default. If every caller is inside your estate, the server needs no public address. On AWS that means interface endpoints through PrivateLink, an internal load balancer or VPC Lattice. On Azure, a private endpoint on the Functions or Container Apps environment with VNet integration for outbound calls.
  • Keep OAuth on private endpoints. A private network narrows who can connect. It says nothing about which agent is calling or on whose behalf, and a prompt-injected agent inside the network is still inside the network.
  • One public door. When the server must be reachable from another cloud, publish it once, through the gateway, behind a web application firewall. The compute behind it accepts traffic only from the gateway.
  • Leave the metadata reachable. The protected resource metadata and the 401 challenge must be served without a token, or clients cannot begin the flow. Exempt those paths from gateway authentication and nothing else.
  • Control egress too. A tool server that can reach the whole internet is a data exfiltration path. Restrict outbound traffic to the APIs the tools call and the issuer’s JWKS endpoint.

Running it in production

  • Short-lived tokens, cached keys. Keep access tokens to minutes, not days. Cache the issuer’s signing keys and refresh on an unknown key id so that key rotation needs no deployment.
  • Log identity, never tokens. Record subject, client id, tool name, outcome and a request id for every call. Bearer tokens must not appear in application logs, gateway logs or traces.
  • Rate limit per client. A model in a loop can call a tool thousands of times. Apply limits per client id at the gateway, and a lower ceiling on write tools.
  • Separate read from write. Distinct scopes at minimum. For irreversible tools, a separate server with its own audience makes an over-broad token far less dangerous.
  • Use the server’s own identity downstream. A managed identity on Azure or an execution role on AWS for calls the server makes in its own right. Where the downstream API needs the user’s identity, use an on-behalf-of token exchange. The same rule applies between agents, as the A2A and MCP article describes.
  • Treat tool descriptions as a release artefact. A changed description changes model behaviour. Review and version it like code.
  • Test the refusals. Put the negative cases from the table above in your pipeline. A resource server that accepts a wrong-audience token still passes every functional test.
⚖️
IDENTITY IN LOGS IS PERSONAL DATA

A subject claim identifies a person, so the audit trail that security needs is also personal data under GDPR. Set a retention period and restrict access to it. AWS documents that JWT-based inbound authorization on AgentCore records some token claims, including the subject, in CloudTrail, so include that trail in your records of processing. For tools that read or change health records, a shared service identity is not enough: the audit entry must name the individual on whose behalf the agent acted, and write tools need a human confirmation step where the clinical hazard log calls for one.

Key takeaways

  • An MCP server is an OAuth resource server. It validates tokens. It does not issue them.
  • The audience check belongs in the server, whatever sits in front of it. It is the control that stops a valid token for another API from working here.
  • Transport-level scopes return HTTP 403. Per-tool scopes return a tool error. Design scopes and server boundaries with that difference in mind.
  • Hosting choice decides who validates first, not whether you validate. Check what a managed platform forwards to your code.
  • Private placement reduces who can connect. It does not replace identity.

If you run remote MCP servers behind AgentCore, API Management or App Service authentication, I would like to hear how the platform’s token handling behaved in practice. Leave a comment below.

📝
UPDATE, NOVEMBER 2026

A later MCP specification revision, dated 2026-07-28, was published two weeks after this article, and the MCP C# SDK reached 2.0 on the same day. Hosting SDK-based servers on Azure Functions was still documented as a public preview in August 2026. The code here was tested against revision 2025-11-25 and the versions named above. Read the current authorization section and check package versions before starting new work.

Glossary

Resource server
The API that accepts access tokens. In MCP authorization, the MCP server.
Authorization server
The service that authenticates users and clients and issues tokens, such as Microsoft Entra ID or Amazon Cognito.
Protected resource metadata (RFC 9728)
A JSON document published by a resource server that names the authorization servers it trusts and the scopes it supports.
Resource indicator (RFC 8707)
The resource parameter a client sends so that the token is bound to one specific server.
Audience (aud)
The claim stating which API a token was issued for. A server must reject tokens whose audience is not itself.
Scope
A named permission carried in a token, such as orders.read.
JWKS
JSON Web Key Set. The public keys an issuer publishes so that others can verify its token signatures.
PKCE
Proof Key for Code Exchange. Protects the authorization code flow against interception. Required for MCP clients.
Client ID Metadata Document
A client registration method in which the client id is an HTTPS URL pointing to the client’s metadata.
Dynamic Client Registration
A protocol for clients to register themselves with an authorization server at run time. Optional in MCP and not supported by Entra ID.
Token passthrough
Forwarding a caller’s token to another API. Forbidden by the MCP specification.
Confused deputy
A service that is tricked into using its authority on behalf of a caller who lacks it.
Streamable HTTP
The MCP transport for remote servers. In stateless mode any instance can serve any request.
DNS rebinding
An attack in which a web page makes a browser send requests to a server on a private network. Host and Origin checks defeat it.

References & Further Reading


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.