Skip to content

feat: Add Azure AD B2C authentication provider #3994

@carlos-rian

Description

@carlos-rian

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_urihttps://{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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    authRelated to authentication (Bearer, JWT, OAuth, WorkOS) for client or server.enhancementImprovement to existing functionality. For issues and smaller PR improvements.too-longExcessively verbose or unedited LLM output. Condense before triage.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions