Python: [BREAKING] Unify Azure credential handling across all packages#4088
Python: [BREAKING] Unify Azure credential handling across all packages#4088eavanvalkenburg merged 1 commit intomicrosoft:mainfrom
Conversation
Replace ad_token, ad_token_provider, and get_entra_auth_token with a unified credential parameter across all Azure-related packages. Core changes: - Add AzureCredentialTypes (TokenCredential | AsyncTokenCredential) and AzureTokenProvider (Callable[[], str | Awaitable[str]]) type aliases - Add resolve_credential_to_token_provider() using azure.identity's get_bearer_token_provider for automatic token caching/refresh - Update AzureOpenAIChatClient, AzureOpenAIResponsesClient, and AzureOpenAIAssistantsClient to accept credential: AzureCredentialTypes | AzureTokenProvider - Remove ad_token, ad_token_provider params and get_entra_auth_token helpers Package updates: - azure-ai: Accept AzureCredentialTypes on AzureAIClient, AzureAIAgentClient, AzureAIProjectAgentProvider, AzureAIAgentsProvider - azure-ai-search: Accept AzureCredentialTypes on AzureAISearchContextProvider - purview: Accept AzureCredentialTypes | AzureTokenProvider on PurviewClient, PurviewPolicyMiddleware, PurviewChatPolicyMiddleware Fixes microsoft#3449 Fixes microsoft#3500 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Pull request overview
Unifies Microsoft Entra ID (Azure AD) authentication across the Python Azure-related packages by replacing the previous ad_token/ad_token_provider/get_entra_auth_token* approach with a single credential parameter and a shared credential→token-provider resolver (using azure.identity.get_bearer_token_provider for refresh/caching).
Changes:
- Introduces
AzureCredentialTypes,AzureTokenProvider, andresolve_credential_to_token_provider()to standardize how credentials are handled. - Updates Azure OpenAI clients to accept
credential(credential object or callable token provider) and always configure OpenAI withazure_ad_token_provider. - Updates Purview / Azure AI / Azure AI Search packages to use the unified credential types and refresh-capable token providers; updates tests accordingly.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| python/packages/core/agent_framework/azure/_entra_id_authentication.py | Adds shared credential + token-provider type aliases and resolver helper. |
| python/packages/core/agent_framework/azure/_shared.py | Switches Azure OpenAI base configuration to resolve credential into azure_ad_token_provider. |
| python/packages/core/agent_framework/azure/_chat_client.py | Updates Azure chat client auth surface to use unified credential. |
| python/packages/core/agent_framework/azure/_responses_client.py | Updates Azure responses client auth surface to use unified credential. |
| python/packages/core/agent_framework/azure/_assistants_client.py | Updates Azure assistants client auth surface to use unified credential. |
| python/packages/core/agent_framework/azure/init.py | Updates lazy exports (adds new types, removes old helper export). |
| python/packages/core/agent_framework/azure/init.pyi | Updates public typing exports to reflect new auth types. |
| python/packages/core/tests/azure/test_entra_id_authentication.py | Replaces tests for removed token-fetch helpers with tests for resolver behavior. |
| python/packages/core/tests/azure/test_azure_assistants_client.py | Updates assistants client tests to validate token-provider-based auth flow. |
| python/packages/purview/agent_framework_purview/_middleware.py | Updates Purview middleware to accept unified credential/token provider types. |
| python/packages/purview/agent_framework_purview/_client.py | Updates Purview client to accept callable token providers in addition to credentials. |
| python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py | Updates Azure AI project provider to accept unified credential type. |
| python/packages/azure-ai/agent_framework_azure_ai/_client.py | Updates Azure AI client to accept unified credential type. |
| python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py | Updates Azure AI agent client to accept unified credential type. |
| python/packages/azure-ai/agent_framework_azure_ai/_agent_provider.py | Updates Azure AI agents provider to accept unified credential type. |
| python/packages/azure-ai-search/agent_framework_azure_ai_search/_context_provider.py | Updates Azure AI Search context provider to accept unified credential type. |
Comments suppressed due to low confidence (12)
python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py:121
- The docstring says the
credentialparameter can be a callable token provider, but the type isAzureCredentialTypes | None(TokenCredential | AsyncTokenCredential) and the value is passed directly intoAIProjectClient. Either remove the callable mention from docs or widen the type and explicitly handle token providers.
credential: Azure credential for authentication. Accepts a TokenCredential,
AsyncTokenCredential, or a callable token provider.
Required when project_client is not provided.
python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py:444
- The docstring states
credentialaccepts a callable token provider, but the type isAzureCredentialTypes | Noneand the value is passed directly toAgentsClient. Either update the type/handling to truly support callables or remove that claim from the docs.
credential: Azure credential for authentication. Accepts a TokenCredential,
AsyncTokenCredential, or a callable token provider.
should_cleanup_agent: Whether to cleanup (delete) agents created by this client when
python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py:518
credentialis typed asAzureCredentialTypes(includes syncTokenCredential) but is passed into the asyncAgentsClientwith# type: ignore[arg-type]. If a sync credential is provided, this can fail because async clients generally requireAsyncTokenCredential. Consider narrowing the accepted type or adapting sync credentials for async use.
if not credential:
raise ServiceInitializationError("Azure credential is required when agents_client is not provided.")
agents_client = AgentsClient(
endpoint=resolved_endpoint,
credential=credential, # type: ignore[arg-type]
user_agent=AGENT_FRAMEWORK_USER_AGENT,
python/packages/azure-ai/agent_framework_azure_ai/_agent_provider.py:143
credentialis typed asAzureCredentialTypes(includes syncTokenCredential) but is passed into the asyncAgentsClientwith# type: ignore[arg-type]. If a sync credential is provided, this is likely to break async authentication at runtime. Consider requiringAsyncTokenCredentialhere or adding an async adapter around sync credentials.
if not credential:
raise ServiceInitializationError("Azure credential is required when agents_client is not provided.")
self._agents_client = AgentsClient(
endpoint=resolved_endpoint,
credential=credential, # type: ignore[arg-type]
user_agent=AGENT_FRAMEWORK_USER_AGENT,
)
python/packages/purview/agent_framework_purview/_client.py:73
TokenCredential.get_token(...)is called directly on the event loop. For sync credentials this can block (network I/O), and the docstring states sync credentials are invoked in a thread, but that isn’t happening here. Consider calling syncget_tokenviaasyncio.to_thread(or adjust the docstring/accepted credential types accordingly).
scopes = get_purview_scopes(self._settings)
token = cred.get_token(*scopes, tenant_id=tenant_id) # type: ignore[union-attr]
token = await token if inspect.isawaitable(token) else token
python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py:155
credentialis typed asAzureCredentialTypes(includes syncTokenCredential) but is passed into the asyncAIProjectClientwith# type: ignore[arg-type]. If a syncTokenCredentialis provided, it won’t satisfyAsyncTokenCredentialand can fail at runtime. Consider narrowing this parameter toAsyncTokenCredential(or adding an adapter that wraps sync credentials for async clients).
project_client = AIProjectClient(
endpoint=resolved_endpoint,
credential=credential, # type: ignore[arg-type]
user_agent=AGENT_FRAMEWORK_USER_AGENT,
)
python/packages/azure-ai/agent_framework_azure_ai/_client.py:137
- The docstring says the
credentialparameter can be a callable token provider, but the annotated type isAzureCredentialTypes | Noneand the value is passed directly toAIProjectClient. Please align docs with the actual supported types/behavior.
credential: Azure credential for authentication. Accepts a TokenCredential,
AsyncTokenCredential, or a callable token provider.
use_latest_version: Boolean flag that indicates whether to use latest agent version
python/packages/azure-ai/agent_framework_azure_ai/_client.py:202
credentialis typed asAzureCredentialTypes(includes syncTokenCredential) but is passed into the asyncAIProjectClientwith# type: ignore[arg-type]. If a sync credential is allowed by the public signature, this can produce runtime failures in async auth. Consider requiringAsyncTokenCredentialhere (or wrapping sync credentials into an async adapter).
raise ServiceInitializationError("Azure credential is required when project_client is not provided.")
project_client = AIProjectClient(
endpoint=resolved_endpoint,
credential=credential, # type: ignore[arg-type]
user_agent=AGENT_FRAMEWORK_USER_AGENT,
python/packages/azure-ai/agent_framework_azure_ai/_agent_provider.py:111
- The docstring says
credentialaccepts a callable token provider, but the type isAzureCredentialTypes | Noneand the credential is passed directly toAgentsClient. Please update the docs to match the actual supported credential types.
credential: Azure credential for authentication. Accepts a TokenCredential,
AsyncTokenCredential, or a callable token provider.
Required if agents_client is not provided.
python/packages/azure-ai-search/agent_framework_azure_ai_search/_context_provider.py:181
- The docstring says
credentialcan be a callable token provider, but the type isAzureCredentialTypes | Noneand the async Search clients expectAsyncTokenCredential(not a callable). Please align the docs with what’s actually supported here.
credential: Azure credential for managed identity authentication.
Accepts a TokenCredential, AsyncTokenCredential, or a callable token provider.
mode: Search mode - "semantic" or "agentic". Default: "semantic".
python/packages/azure-ai-search/agent_framework_azure_ai_search/_context_provider.py:229
credentialis declared asAzureCredentialTypes(includes syncTokenCredential), butresolved_credentialis typed asAzureKeyCredential | AsyncTokenCredentialand the assignment requires a# type: ignore. This suggests sync credentials aren’t actually supported for the async Search clients and may fail at runtime. Consider narrowingcredentialtoAsyncTokenCredential(or adding a wrapper for sync credentials).
resolved_credential: AzureKeyCredential | AsyncTokenCredential
if credential:
resolved_credential = credential # type: ignore[assignment]
elif isinstance(api_key, AzureKeyCredential):
python/packages/core/agent_framework/azure/_entra_id_authentication.py:14
loggeris defined but not used in this module, which will trigger Ruff (F841) under the repo lint config. Either remove the unused logger/logging import or add logging where it’s actually needed.
logger: logging.Logger = logging.getLogger(__name__)
Summary
Replace
ad_token,ad_token_provider, andget_entra_auth_tokenwith a unifiedcredentialparameter across all Azure-related Python packages, usingazure.identity.get_bearer_token_providerfor automatic token caching and refresh.Motivation
AzureOpenAIResponsesClientonly accepted syncTokenCredential, failing at runtime in async contexts.ad_token_providerwhen providing Azure credential #3500: Credential handling used one-shot token fetches instead of a token provider with auto-refresh.Changes
New type aliases (
_entra_id_authentication.py)AzureCredentialTypes = TokenCredential | AsyncTokenCredential— credential objectsAzureTokenProvider = Callable[[], str | Awaitable[str]]— callable token providersresolve_credential_to_token_provider()— wraps credentials usingazure.identity.get_bearer_token_providerfor auto-refreshCore package
AzureOpenAIChatClient,AzureOpenAIResponsesClient,AzureOpenAIAssistantsClient: Acceptcredential: AzureCredentialTypes | AzureTokenProviderad_token,ad_token_providerparameters,get_entra_auth_token/get_entra_auth_token_asynchelpersazure-ai package
AzureAIClient,AzureAIAgentClient,AzureAIProjectAgentProvider,AzureAIAgentsProvider: Acceptcredential: AzureCredentialTypes(credential objects only, since the Azure SDK clients handle token refresh internally)azure-ai-search package
AzureAISearchContextProvider: Acceptcredential: AzureCredentialTypespurview package
PurviewClient,PurviewPolicyMiddleware,PurviewChatPolicyMiddleware: Acceptcredential: AzureCredentialTypes | AzureTokenProviderBreaking Changes
ad_tokenandad_token_providerparameters from all Azure OpenAI clientsget_entra_auth_tokenandget_entra_auth_token_asynchelper functionscredential=parameter instead (accepts sync/async credentials and callable token providers)Testing
All existing tests pass across all affected packages (core, azure-ai, azure-ai-search, purview). All fmt, lint, pyright, and mypy checks pass.
Fixes #3449
Fixes #3500