Skip to content

Comments

Python: [BREAKING] Unify Azure credential handling across all packages#4088

Merged
eavanvalkenburg merged 1 commit intomicrosoft:mainfrom
eavanvalkenburg:ad_auth
Feb 19, 2026
Merged

Python: [BREAKING] Unify Azure credential handling across all packages#4088
eavanvalkenburg merged 1 commit intomicrosoft:mainfrom
eavanvalkenburg:ad_auth

Conversation

@eavanvalkenburg
Copy link
Member

Summary

Replace ad_token, ad_token_provider, and get_entra_auth_token with a unified credential parameter across all Azure-related Python packages, using azure.identity.get_bearer_token_provider for automatic token caching and refresh.

Motivation

Changes

New type aliases (_entra_id_authentication.py)

  • AzureCredentialTypes = TokenCredential | AsyncTokenCredential — credential objects
  • AzureTokenProvider = Callable[[], str | Awaitable[str]] — callable token providers
  • resolve_credential_to_token_provider() — wraps credentials using azure.identity.get_bearer_token_provider for auto-refresh

Core package

  • AzureOpenAIChatClient, AzureOpenAIResponsesClient, AzureOpenAIAssistantsClient: Accept credential: AzureCredentialTypes | AzureTokenProvider
  • Removed: ad_token, ad_token_provider parameters, get_entra_auth_token/get_entra_auth_token_async helpers

azure-ai package

  • AzureAIClient, AzureAIAgentClient, AzureAIProjectAgentProvider, AzureAIAgentsProvider: Accept credential: AzureCredentialTypes (credential objects only, since the Azure SDK clients handle token refresh internally)

azure-ai-search package

  • AzureAISearchContextProvider: Accept credential: AzureCredentialTypes

purview package

  • PurviewClient, PurviewPolicyMiddleware, PurviewChatPolicyMiddleware: Accept credential: AzureCredentialTypes | AzureTokenProvider

Breaking Changes

  • Removed ad_token and ad_token_provider parameters from all Azure OpenAI clients
  • Removed get_entra_auth_token and get_entra_auth_token_async helper functions
  • Use credential= 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

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>
Copilot AI review requested due to automatic review settings February 19, 2026 15:44
@eavanvalkenburg eavanvalkenburg changed the title Python: Unify Azure credential handling across all packages Python: [BREAKING] Unify Azure credential handling across all packages Feb 19, 2026
@eavanvalkenburg eavanvalkenburg added the breaking change Introduces changes that are not backward compatible and may require updates to dependent code. label Feb 19, 2026
@markwallace-microsoft
Copy link
Member

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/azure-ai-search/agent_framework_azure_ai_search
   _context_provider.py267398%104–105, 274
packages/azure-ai/agent_framework_azure_ai
   _agent_provider.py110199%250
   _chat_client.py4787584%388–389, 391, 575, 580–581, 583–584, 587, 590, 592, 597, 858–859, 861, 864, 867, 870–875, 878, 880, 888, 900–902, 906, 909–910, 918–921, 931, 939–942, 944–945, 947–948, 955, 963–964, 972–973, 978–979, 983–990, 995, 998, 1006, 1012, 1020–1022, 1025, 1047–1048, 1181, 1209, 1224, 1340, 1462
   _client.py2753786%380, 382, 425, 433–445, 458, 518, 533–538, 581, 616, 618, 651, 859, 862, 865–866, 868–871, 914
   _project_provider.py117496%210, 304, 344, 377
packages/core/agent_framework/azure
   _assistants_client.py380100% 
   _chat_client.py78494%299, 301, 314–315
   _entra_id_authentication.py220100% 
   _responses_client.py49687%185, 217, 281–284
   _shared.py72494%175, 178, 189, 199
packages/purview/agent_framework_purview
   _client.py124298%69–70
   _middleware.py1110100% 
TOTAL21241330484% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
4183 239 💤 0 ❌ 0 🔥 1m 10s ⏱️

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, and resolve_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 with azure_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 credential parameter can be a callable token provider, but the type is AzureCredentialTypes | None (TokenCredential | AsyncTokenCredential) and the value is passed directly into AIProjectClient. 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 credential accepts a callable token provider, but the type is AzureCredentialTypes | None and the value is passed directly to AgentsClient. 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

  • credential is typed as AzureCredentialTypes (includes sync TokenCredential) but is passed into the async AgentsClient with # type: ignore[arg-type]. If a sync credential is provided, this can fail because async clients generally require AsyncTokenCredential. 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

  • credential is typed as AzureCredentialTypes (includes sync TokenCredential) but is passed into the async AgentsClient with # type: ignore[arg-type]. If a sync credential is provided, this is likely to break async authentication at runtime. Consider requiring AsyncTokenCredential here 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 sync get_token via asyncio.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

  • credential is typed as AzureCredentialTypes (includes sync TokenCredential) but is passed into the async AIProjectClient with # type: ignore[arg-type]. If a sync TokenCredential is provided, it won’t satisfy AsyncTokenCredential and can fail at runtime. Consider narrowing this parameter to AsyncTokenCredential (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 credential parameter can be a callable token provider, but the annotated type is AzureCredentialTypes | None and the value is passed directly to AIProjectClient. 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

  • credential is typed as AzureCredentialTypes (includes sync TokenCredential) but is passed into the async AIProjectClient with # type: ignore[arg-type]. If a sync credential is allowed by the public signature, this can produce runtime failures in async auth. Consider requiring AsyncTokenCredential here (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 credential accepts a callable token provider, but the type is AzureCredentialTypes | None and the credential is passed directly to AgentsClient. 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 credential can be a callable token provider, but the type is AzureCredentialTypes | None and the async Search clients expect AsyncTokenCredential (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

  • credential is declared as AzureCredentialTypes (includes sync TokenCredential), but resolved_credential is typed as AzureKeyCredential | AsyncTokenCredential and 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 narrowing credential to AsyncTokenCredential (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

  • logger is 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__)

@eavanvalkenburg eavanvalkenburg added this pull request to the merge queue Feb 19, 2026
Merged via the queue into microsoft:main with commit fd4e6e8 Feb 19, 2026
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Introduces changes that are not backward compatible and may require updates to dependent code. python

Projects

None yet

4 participants