Skip to content

Authorization AsyncTokenCredential-returning variants - #568

Open
Rodrigo Brandão (rodrigobr-msft) wants to merge 8 commits into
mainfrom
users/robrandao/authorization-cred
Open

Authorization AsyncTokenCredential-returning variants#568
Rodrigo Brandão (rodrigobr-msft) wants to merge 8 commits into
mainfrom
users/robrandao/authorization-cred

Conversation

@rodrigobr-msft

Copy link
Copy Markdown
Contributor

This pull request introduces significant improvements to the authentication and authorization infrastructure, focusing on enhanced interoperability with Azure SDKs and improved type safety. The main changes include the addition of new AsyncTokenCredential wrappers for token retrieval, refactoring of method signatures for clarity, and the introduction of utility helpers to bridge internal token types with Azure's credential interfaces.

Azure SDK Interoperability and Token Credential Support:

  • Added new get_token_credential methods to both SidecarAuth and MsalAuth classes, allowing them to return AsyncTokenCredential objects compatible with Azure SDKs. Corresponding SidecarTokenCredential and MsalTokenCredential classes were implemented to encapsulate the token retrieval logic. [1] [2] [3] [4]
  • Introduced a utility module _helpers.py with the _CallableTokenCredential class, which wraps an async callable to provide an AsyncTokenCredential interface, and helper functions to convert internal TokenResponse objects to Azure's AccessToken.

Authorization API Enhancements:

  • The Authorization class now provides get_token_as_token_credential and exchange_token_as_token_credential methods, enabling consumers to obtain Azure-compatible token credentials directly from authorization flows. [1] [2]
  • Refactored method signatures throughout authorization.py to use | None syntax instead of Optional, improving code clarity and aligning with modern Python typing conventions. [1] [2] [3] [4] [5] [6] [7] [8]

Type Safety and Consistency:

  • Updated various type annotations for handler callbacks and method parameters to use str | None instead of Optional[str], and similar changes for other types, enhancing type safety and readability. [1] [2] [3] [4] [5] [6] [7] [8]

Dependency Imports and Code Organization:

  • Added necessary imports for AsyncTokenCredential and related Azure SDK types in relevant modules to support the new credential features. [1] [2] [3] [4] [5]

These updates collectively make the authentication system more robust, extensible, and compatible with Azure's ecosystem, while also improving code maintainability and developer experience.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the Microsoft 365 Agents SDK for Python authentication/authorization surface to interoperate with Azure SDKs by introducing azure.core.credentials_async.AsyncTokenCredential-returning APIs and wrappers, while modernizing type annotations in the OAuth Authorization flow.

Changes:

  • Added Azure AsyncTokenCredential wrappers for MSAL and Sidecar auth providers (MsalTokenCredential, SidecarTokenCredential) and provider entrypoints (get_token_credential).
  • Introduced _CallableTokenCredential + helpers to bridge internal TokenResponse to Azure AccessToken.
  • Extended Authorization with credential-returning convenience methods and updated type hints to | None.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
libraries/microsoft-agents-hosting-core/setup.py Adds Azure dependency for hosting-core (currently uses incorrect distribution name).
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/access_token_provider_base.py Adds get_token_credential() to the provider protocol (new API contract).
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/_helpers.py Adds TokenResponseAccessToken conversion and callable-backed AsyncTokenCredential.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py Adds credential-returning methods on OAuth Authorization and updates typing.
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/authorization/sdk_token_credential.py Present in PR metadata, but currently empty in the checkout.
libraries/microsoft-agents-authentication-msal/microsoft_agents/authentication/msal/msal_auth.py Adds get_token_credential() returning an Azure-compatible credential wrapper.
libraries/microsoft-agents-authentication-msal/microsoft_agents/authentication/msal/msal_token_credential.py Allows injecting an MSAL provider instance and wraps MSAL token acquisition as AsyncTokenCredential.
libraries/microsoft-agents-authentication-entra-auth-sidecar/microsoft_agents/authentication/entra_auth_sidecar/sidecar_auth.py Adds get_token_credential() returning an Azure-compatible credential wrapper.
libraries/microsoft-agents-authentication-entra-auth-sidecar/microsoft_agents/authentication/entra_auth_sidecar/sidecar_token_credential.py New AsyncTokenCredential wrapper around Sidecar token acquisition.
Suppressed comments (3)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py:416

  • get_token_as_token_credential() passes Azure SDK **kwargs through to exchange_token(), but exchange_token() doesn't accept arbitrary kwargs. Azure SDKs commonly pass claims, tenant_id, etc. into get_token(), so this will raise TypeError when used as an AsyncTokenCredential.
        async def func(*scopes: str, **kwargs) -> TokenResponse:
            return await self.exchange_token(
                context, auth_handler_id=auth_handler_id, scopes=list(scopes), **kwargs
            )

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py:506

  • In exchange_token_as_token_credential(), all_scopes = list(set(scopes or [] + list(new_scopes))) has two problems: (1) when scopes is a non-empty list it short-circuits the or and ignores new_scopes, and (2) set() discards order, which can be surprising when scopes are logged/debugged. This can cause credentials to request the wrong scopes.
        async def func(*new_scopes: str, **kwargs) -> TokenResponse:
            all_scopes = list(set(scopes or [] + list(new_scopes)))
            return await self.exchange_token(
                context,
                scopes=all_scopes,
                auth_handler_id=auth_handler_id,
                exchange_connection=exchange_connection,
            )

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py:411

  • Docstring types for get_token_as_token_credential() are inconsistent with the actual signature (e.g. auth_handler_id can be None, and :rtype: points to microsoft_agents...authorization.AsyncTokenCredential which isn't a real type; the return type is azure.core.credentials_async.AsyncTokenCredential). This can mislead users relying on generated docs.
        :param auth_handler_id: The ID of the auth handler to get the token for.
        :type auth_handler_id: str
        :return: An AsyncTokenCredential for the specified auth handler or the default handler.
        :rtype: :class:`microsoft_agents.hosting.core.app.oauth.authorization.AsyncTokenCredential`
        """

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread libraries/microsoft-agents-hosting-core/setup.py Outdated
Copilot AI review requested due to automatic review settings August 31, 2026 18:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread libraries/microsoft-agents-hosting-core/setup.py
Copilot AI review requested due to automatic review settings August 31, 2026 19:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py:410

  • The docstring types don’t match the signature/return type here: auth_handler_id can be None, and the :rtype: should reference azure.core.credentials_async.AsyncTokenCredential (the current module path doesn’t exist). This can break generated docs and mislead SDK consumers.
        :type context: :class:`microsoft_agents.hosting.core.turn_context.TurnContext`
        :param auth_handler_id: The ID of the auth handler to get the token for.
        :type auth_handler_id: str
        :return: An AsyncTokenCredential for the specified auth handler or the default handler.
        :rtype: :class:`microsoft_agents.hosting.core.app.oauth.authorization.AsyncTokenCredential`

Comment thread tests/hosting_core/authorization/test_helpers.py Outdated
Copilot AI review requested due to automatic review settings August 31, 2026 20:44
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) marked this pull request as ready for review August 31, 2026 20:46
@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) marked this pull request as draft August 31, 2026 20:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Copilot AI review requested due to automatic review settings August 31, 2026 20:56
@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) marked this pull request as ready for review August 31, 2026 20:57
@rodrigobr-msft
Rodrigo Brandão (rodrigobr-msft) marked this pull request as draft August 31, 2026 20:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py:7

  • overload and Literal are imported but never used in this module, which adds avoidable lint noise and makes the import list harder to scan. Remove the unused names from the typing import.
from typing import Optional, Callable, Awaitable, cast, overload, Literal

Copilot AI review requested due to automatic review settings August 31, 2026 21:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py:416

  • get_token_as_token_credential() forwards scopes=list(scopes) to exchange_token(). When the Azure SDK (or a consumer) calls credential.get_token() with no scopes, this passes [] rather than None, which prevents exchange_token() from falling back to the handler’s configured default scopes (it only defaults when scopes is None).

This issue also appears on line 500 of the same file.

        async def func(*scopes: str, **_kwargs) -> TokenResponse:
            return await self.exchange_token(
                context, auth_handler_id=auth_handler_id, scopes=list(scopes)
            )

libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py:504

  • exchange_token_as_token_credential() always passes a (possibly empty) list of scopes to exchange_token(). If the wrapper is called with no scopes and the caller didn’t provide initial scopes, this results in [] instead of None, which prevents the downstream handler from using its configured default scopes.
            prev_scopes: list[str] = scopes or []
            all_scopes = list(dict.fromkeys([*prev_scopes, *new_scopes]))
            return await self.exchange_token(
                context,
                scopes=all_scopes,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expand Authorization class to have TokenCredential-returning variants of existing API

2 participants