PrefectHQ/fastmcp · error · NotImplementedError
Azure AD B2C does not support the On-Behalf-Of (OBO) flow. U
Error message
Azure AD B2C does not support the On-Behalf-Of (OBO) flow. Use AzureProvider with standard Entra ID for OBO scenarios.
What it means
get_obo_credential() performs the OAuth On-Behalf-Of flow to exchange a user token for a downstream-service token. Azure AD B2C does not implement the OBO flow at all, so when the provider is a B2C variant (_obo_supported is False) the method raises NotImplementedError instead of attempting a doomed exchange.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/providers/azure.py:647
async def get_obo_credential(self, user_assertion: str) -> OnBehalfOfCredential:
"""Get a cached or new OnBehalfOfCredential for OBO token exchange.
Credentials are cached by user assertion so the Azure SDK's internal
token cache can avoid redundant OBO exchanges when the same user
calls multiple tools with the same scopes.
Args:
user_assertion: The user's access token to exchange via OBO.
Returns:
A configured OnBehalfOfCredential ready for get_token() calls.
Raises:
NotImplementedError: If OBO is not supported (e.g. Azure AD B2C).
ImportError: If azure-identity is not installed (requires fastmcp[azure]).
"""
if not self._obo_supported:
raise NotImplementedError(
"Azure AD B2C does not support the On-Behalf-Of (OBO) flow. "
"Use AzureProvider with standard Entra ID for OBO scenarios."
)
_require_azure_identity("OBO token exchange")
from azure.identity.aio import OnBehalfOfCredential
key = hashlib.sha256(user_assertion.encode()).hexdigest()
if key in self._obo_credentials:
self._obo_credentials.move_to_end(key)
return self._obo_credentials[key]
obo_kwargs: dict[str, Any] = {
"tenant_id": self._tenant_id,
"client_id": self._upstream_client_id,
"user_assertion": user_assertion,
"authority": f"https://{self._base_authority}",
}View on GitHub (pinned to 1f02114297)
Solutions
- Migrate the server to standard Entra ID (plain AzureProvider) if OBO is a requirement.
- Handle NotImplementedError and fall back to client-credentials auth for downstream calls (service identity instead of delegated user identity).
- Alternatively call downstream APIs directly with the user's original access token if the downstream API accepts the same B2C issuer.
Example fix
// before
token = await provider.get_obo_credential(user_assertion=user_token)
// after
try:
token = await provider.get_obo_credential(user_assertion=user_token)
except NotImplementedError:
token = client_credentials_token_for_downstream() # B2C has no OBO Defensive patterns
Strategy: try-catch
Validate before calling
def obo_supported(provider) -> bool:
return bool(getattr(provider, "_obo_supported", True)) Type guard
def is_b2c_provider(provider) -> bool:
return isinstance(provider, AzureProvider) and not getattr(provider, "_obo_supported", True) Try / catch
try:
cred = await provider.get_obo_credential(user_assertion=token)
except NotImplementedError:
cred = await get_client_credentials_token(scopes) # fallback path Prevention
- Gate OBO code paths on provider capability before deploying to B2C tenants
- Add an integration test that asserts B2C providers skip OBO flows
When it happens
Trigger: Calling await b2c_provider.get_obo_credential(user_assertion=...) on an AzureProvider created via from_b2c, or using EntraOBOToken as an async context manager against a B2C-backed provider.
Common situations: Building a multi-service architecture on B2C and trying to call downstream APIs with delegated user identity; porting Entra ID OBO code to a B2C tenant.
Related errors
- tenant_name should be the short name without the .onmicrosof
- OBO token exchange requires either a client_secret or a subc
- No access token available. Cannot perform OBO exchange.
- EntraOBOToken requires an AzureProvider as the auth provider
- AzureProvider requires at least one non-OIDC scope in requir
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/95f895e83dd470e0.
Report an issue: GitHub.