Problem
MCP servers that sit behind Azure AD B2C user flows cannot use AzureProvider today.
B2C uses a fundamentally different set of endpoints compared to standard Microsoft Entra ID:
| Attribute |
Entra ID (current support) |
Azure AD B2C |
| Authority host |
login.microsoftonline.com |
{tenant}.b2clogin.com |
| Scope prefix |
api://{client_id}/{scope} |
https://{tenant}.onmicrosoft.com/{client_id}/{scope} |
| URL structure |
/{tenant_id}/oauth2/v2.0/... |
/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/... |
| Issuer |
Fixed per tenant |
GUID-based, varies by policy |
Passing a B2C tenant to AzureProvider produces wrong authorization URLs, wrong token endpoints, and wrong scope URIs, causing authentication to fail entirely.
Use Case
Azure AD B2C is Microsoft's consumer identity service, widely used by companies to authenticate external users (customers, partners) with social logins, custom user flows, and multi-factor auth. Organizations running MCP servers for end-users — rather than internal corporate users — commonly rely on B2C.
A concrete example of what users have to do today (which doesn't work correctly):
# Workaround attempt — wrong scope URIs, wrong issuer validation:
auth = AzureProvider(
client_id="...",
tenant_id="mytenant.onmicrosoft.com/B2C_1_susi", # hack: embedding policy in tenant_id
base_authority="mytenant.b2clogin.com",
required_scopes=["mcp-access"],
base_url="https://myserver.com",
identifier_uri="https://mytenant.onmicrosoft.com/my-client-id", # non-obvious
)
# Token validation still fails: B2C issuer carries a GUID, not the .onmicrosoft.com name
Proposed Approach
Add AzureB2CProvider as a subclass of AzureProvider in src/fastmcp/server/auth/providers/azure_b2c.py.
It would accept the three B2C-specific parameters and compute the correct endpoints internally:
from fastmcp.server.auth.providers.azure_b2c import AzureB2CProvider
auth = AzureB2CProvider(
tenant_name="mytenant",
policy_name="B2C_1_susi",
client_id="00000000-...",
client_secret="secret",
required_scopes=["mcp-access"],
base_url="https://myserver.com",
)
The subclass would:
- Derive
base_authority → {tenant_name}.b2clogin.com
- Derive
identifier_uri → https://{tenant_name}.onmicrosoft.com/{client_id}
- Derive
tenant_id → {tenant_name}.onmicrosoft.com/{policy_name} (for URL path construction)
- Override
_prefix_scopes_for_azure() to use the https:// scheme identifier URI
- Default issuer validation to
None (B2C issuers carry a tenant GUID — not the .onmicrosoft.com name — and vary by policy; audience validation still enforces correct app targeting)
- Support an optional
custom_domain parameter for B2C tenants using custom domains
This reuses all of AzureProvider's OAuth proxy, PKCE, consent, token storage, OBO, and token exchange logic — only the URL/scope derivation changes.
I have a working implementation of this pattern validated against a real B2C tenant. Happy to open a PR if this approach aligns with the framework's direction.
Problem
MCP servers that sit behind Azure AD B2C user flows cannot use
AzureProvidertoday.B2C uses a fundamentally different set of endpoints compared to standard Microsoft Entra ID:
login.microsoftonline.com{tenant}.b2clogin.comapi://{client_id}/{scope}https://{tenant}.onmicrosoft.com/{client_id}/{scope}/{tenant_id}/oauth2/v2.0/.../{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/...Passing a B2C tenant to
AzureProviderproduces wrong authorization URLs, wrong token endpoints, and wrong scope URIs, causing authentication to fail entirely.Use Case
Azure AD B2C is Microsoft's consumer identity service, widely used by companies to authenticate external users (customers, partners) with social logins, custom user flows, and multi-factor auth. Organizations running MCP servers for end-users — rather than internal corporate users — commonly rely on B2C.
A concrete example of what users have to do today (which doesn't work correctly):
Proposed Approach
Add
AzureB2CProvideras a subclass ofAzureProviderinsrc/fastmcp/server/auth/providers/azure_b2c.py.It would accept the three B2C-specific parameters and compute the correct endpoints internally:
The subclass would:
base_authority→{tenant_name}.b2clogin.comidentifier_uri→https://{tenant_name}.onmicrosoft.com/{client_id}tenant_id→{tenant_name}.onmicrosoft.com/{policy_name}(for URL path construction)_prefix_scopes_for_azure()to use thehttps://scheme identifier URINone(B2C issuers carry a tenant GUID — not the.onmicrosoft.comname — and vary by policy; audience validation still enforces correct app targeting)custom_domainparameter for B2C tenants using custom domainsThis reuses all of
AzureProvider's OAuth proxy, PKCE, consent, token storage, OBO, and token exchange logic — only the URL/scope derivation changes.I have a working implementation of this pattern validated against a real B2C tenant. Happy to open a PR if this approach aligns with the framework's direction.