{"record":{"id":"95f895e83dd470e0","repo":"PrefectHQ/fastmcp","slug":"azure-ad-b2c-does-not-support-the-on-behalf-of-ob","errorCode":null,"errorMessage":"Azure AD B2C does not support the On-Behalf-Of (OBO) flow. Use AzureProvider with standard Entra ID for OBO scenarios.","messagePattern":"Azure AD B2C does not support the On-Behalf-Of \\(OBO\\) flow\\. Use AzureProvider with standard Entra ID for OBO scenarios\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/providers/azure.py","lineNumber":647,"sourceCode":"    async def get_obo_credential(self, user_assertion: str) -> OnBehalfOfCredential:\n        \"\"\"Get a cached or new OnBehalfOfCredential for OBO token exchange.\n\n        Credentials are cached by user assertion so the Azure SDK's internal\n        token cache can avoid redundant OBO exchanges when the same user\n        calls multiple tools with the same scopes.\n\n        Args:\n            user_assertion: The user's access token to exchange via OBO.\n\n        Returns:\n            A configured OnBehalfOfCredential ready for get_token() calls.\n\n        Raises:\n            NotImplementedError: If OBO is not supported (e.g. Azure AD B2C).\n            ImportError: If azure-identity is not installed (requires fastmcp[azure]).\n        \"\"\"\n        if not self._obo_supported:\n            raise NotImplementedError(\n                \"Azure AD B2C does not support the On-Behalf-Of (OBO) flow. \"\n                \"Use AzureProvider with standard Entra ID for OBO scenarios.\"\n            )\n        _require_azure_identity(\"OBO token exchange\")\n        from azure.identity.aio import OnBehalfOfCredential\n\n        key = hashlib.sha256(user_assertion.encode()).hexdigest()\n\n        if key in self._obo_credentials:\n            self._obo_credentials.move_to_end(key)\n            return self._obo_credentials[key]\n\n        obo_kwargs: dict[str, Any] = {\n            \"tenant_id\": self._tenant_id,\n            \"client_id\": self._upstream_client_id,\n            \"user_assertion\": user_assertion,\n            \"authority\": f\"https://{self._base_authority}\",\n        }","sourceCodeStart":629,"sourceCodeEnd":665,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/providers/azure.py#L629-L665","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntoken = await provider.get_obo_credential(user_assertion=user_token)\n// after\ntry:\n    token = await provider.get_obo_credential(user_assertion=user_token)\nexcept NotImplementedError:\n    token = client_credentials_token_for_downstream()  # B2C has no OBO","handlingStrategy":"try-catch","validationCode":"def obo_supported(provider) -> bool:\n    return bool(getattr(provider, \"_obo_supported\", True))","typeGuard":"def is_b2c_provider(provider) -> bool:\n    return isinstance(provider, AzureProvider) and not getattr(provider, \"_obo_supported\", True)","tryCatchPattern":"try:\n    cred = await provider.get_obo_credential(user_assertion=token)\nexcept NotImplementedError:\n    cred = await get_client_credentials_token(scopes)  # fallback path","preventionTips":["Gate OBO code paths on provider capability before deploying to B2C tenants","Add an integration test that asserts B2C providers skip OBO flows"],"tags":["python","azure","azure-ad-b2c","obo","unsupported-feature"],"backgroundTag":"obo-flow-unsupported","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}