Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# Microsoft 365 Agents SDK for Python - Release Notes v1.10.0 (Unreleased)

**Previous Version:** 1.9.0 (Released 2026-10-19)

- **Auth Handler Configuration**: Added `AuthHandlerSettings` and a unified `settings` parameter accepting a settings object or dictionary across authorization handlers (#633).
- **Deprecation**: Deprecated `AuthHandler` in favor of `AuthHandlerSettings`, and handler constructor parameters `auth_handler` and `auth_handler_settings` in favor of `settings`, while retaining backward compatibility (#633).

---

# Microsoft 365 Agents SDK for Python - Release Notes v1.9.0

**Release Date:** 2026-10-19
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
Authorization,
AuthHandler,
AgenticUserAuthorization,
AuthHandlerSettings,
)

# App State
Expand Down Expand Up @@ -151,6 +152,7 @@
"TempState",
"Authorization",
"AuthHandler",
"AuthHandlerSettings",
"AccessTokenProviderBase",
"AuthenticationConstants",
"AnonymousTokenProvider",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .oauth import (
Authorization,
AuthHandler,
AuthHandlerSettings,
AgenticUserAuthorization,
)

Expand Down Expand Up @@ -59,6 +60,7 @@
"TempState",
"Authorization",
"AuthHandler",
"AuthHandlerSettings",
"AgenticUserAuthorization",
# Proactive
"Conversation",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from dataclasses import dataclass, field
from typing import Callable, Optional

from microsoft_agents.hosting.core.app.oauth import AuthHandler
from microsoft_agents.hosting.core.app.oauth import AuthHandlerSettings
from microsoft_agents.hosting.core.authorization import Connections
from microsoft_agents.hosting.core.storage import Storage

Expand Down Expand Up @@ -81,7 +81,7 @@ class ApplicationOptions:
If not provided, the default `TurnState` will be used.
"""

authorization_handlers: Optional[dict[str, AuthHandler]] = None
authorization_handlers: Optional[dict[str, AuthHandlerSettings]] = None
"""
Optional. Authorization handler for OAuth flows.
If not provided, no OAuth flows will be supported.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


from .authorization import Authorization
from .auth_handler import AuthHandler
from .auth_handler import AuthHandler, AuthHandlerSettings
from ._sign_in_state import _SignInState
from ._sign_in_response import _SignInResponse
from ._handlers import (
Expand All @@ -16,6 +16,7 @@
__all__ = [
"Authorization",
"AuthHandler",
"AuthHandlerSettings",
"_AuthorizationHandler",
"_SignInState",
"_SignInResponse",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
Licensed under the MIT License.
"""

from abc import ABC
from typing import Optional
import logging
import warnings

from abc import ABC

from microsoft_agents.activity import TokenResponse

from ....turn_context import TurnContext
from ....storage import Storage
from ....authorization import Connections
from ..auth_handler import AuthHandler
from ..auth_handler import AuthHandlerSettings
from .._sign_in_response import _SignInResponse

logger = logging.getLogger(__name__)
Expand All @@ -23,16 +24,17 @@ class _AuthorizationHandler(ABC):

_storage: Storage
_connection_manager: Connections
_handler: AuthHandler
_handler: AuthHandlerSettings

def __init__(
self,
storage: Storage,
connection_manager: Connections,
auth_handler: Optional[AuthHandler] = None,
settings: dict | AuthHandlerSettings | None = None,
*,
auth_handler_id: Optional[str] = None,
auth_handler_settings: Optional[dict] = None,
auth_handler_id: str | None = None,
auth_handler: AuthHandlerSettings | None = None,
auth_handler_settings: dict | None = None,
**kwargs,
) -> None:
"""
Expand All @@ -42,28 +44,45 @@ def __init__(
:type storage: Storage
:param connection_manager: The connection manager for OAuth providers.
:type connection_manager: Connections
:param auth_handlers: Configuration for OAuth providers.
:type auth_handlers: dict[str, AuthHandler], Optional
:param settings: Optional settings for the auth handler.
:type settings: dict | AuthHandlerSettings | None
:param auth_handler: (Deprecated) Configuration for OAuth provider.
:type auth_handler: AuthHandlerSettings | None
:param auth_handler_id: Optional ID of the auth handler.
:type auth_handler_id: str | None
:param auth_handler_settings: (Deprecated) Optional settings dict for the auth handler.
:type auth_handler_settings: dict | None
:raises ValueError: When storage is None or no auth handlers provided.
"""
if not storage:
raise ValueError("Storage is required for Authorization")
if not auth_handler and not auth_handler_settings:
if not settings and not auth_handler and not auth_handler_settings:
raise ValueError(
"At least one of auth_handler or auth_handler_settings is required."
"At least one of settings or auth_handler or auth_handler_settings is required."
)
if auth_handler or auth_handler_settings:
warnings.warn(
"auth_handler and auth_handler_settings are deprecated. Please use settings instead.",
DeprecationWarning,
stacklevel=2,
)

self._storage = storage
self._connection_manager = connection_manager

if auth_handler:
if settings:
if isinstance(settings, dict):
self._handler = AuthHandlerSettings(**settings)
else:
self._handler = settings
Comment thread
rodrigobr-msft marked this conversation as resolved.
Resolved
elif auth_handler: # deprecated
self._handler = auth_handler
else:
else: # deprecated
if not auth_handler_settings:
raise ValueError(
"auth_handler_settings must be provided if auth_handler is None."
)
self._handler = AuthHandler._from_settings(auth_handler_settings)
self._handler = AuthHandlerSettings(**auth_handler_settings)

self._id = auth_handler_id or self._handler.name
if not self._id:
Expand All @@ -72,14 +91,14 @@ def __init__(
)

async def _sign_in(
self, context: TurnContext, scopes: Optional[list[str]] = None
self, context: TurnContext, scopes: list[str] | None = None
) -> _SignInResponse:
"""Initiate or continue the sign-in process for the user with the given auth handler.

:param context: The turn context for the current turn of conversation.
:type context: TurnContext
:param scopes: Optional list of scopes to request during sign-in. If None, default scopes will be used.
:type scopes: Optional[list[str]], Optional
:type scopes: list[str] | None
:return: A SignInResponse indicating the result of the sign-in attempt.
:rtype: SignInResponse
"""
Expand All @@ -88,17 +107,17 @@ async def _sign_in(
async def get_refreshed_token(
self,
context: TurnContext,
exchange_connection: Optional[str] = None,
exchange_scopes: Optional[list[str]] = None,
exchange_connection: str | None = None,
exchange_scopes: list[str] | None = None,
) -> TokenResponse:
"""Attempts to get a refreshed token for the user with the given scopes

:param context: The turn context for the current turn of conversation.
:type context: TurnContext
:param exchange_connection: Optional name of the connection to use for token exchange. If None, default connection will be used.
:type exchange_connection: Optional[str], Optional
:type exchange_connection: str | None
:param exchange_scopes: Optional list of scopes to request during token exchange. If None, default scopes will be used.
:type exchange_scopes: Optional[list[str]], Optional
:type exchange_scopes: list[str] | None
"""
raise NotImplementedError()

Expand All @@ -108,6 +127,6 @@ async def _sign_out(self, context: TurnContext) -> None:
:param context: The turn context for the current turn of conversation.
:type context: TurnContext
:param auth_handler_id: The ID of the auth handler to sign out from. If None, sign out from all handlers.
:type auth_handler_id: Optional[str]
:type auth_handler_id: str | None
"""
raise NotImplementedError()
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

import logging

from typing import Optional

from microsoft_agents.activity import TokenResponse

from ....turn_context import TurnContext
Expand All @@ -15,7 +13,7 @@
from ._authorization_handler import _AuthorizationHandler
from ....storage import Storage
from ....authorization import Connections
from ..auth_handler import AuthHandler
from ..auth_handler import AuthHandlerSettings
from ..telemetry import spans

logger = logging.getLogger(__name__)
Expand All @@ -28,10 +26,11 @@ def __init__(
self,
storage: Storage,
connection_manager: Connections,
auth_handler: Optional[AuthHandler] = None,
settings: dict | AuthHandlerSettings | None = None,
*,
auth_handler_id: Optional[str] = None,
auth_handler_settings: Optional[dict] = None,
auth_handler_id: str | None = None,
auth_handler: AuthHandlerSettings | None = None,
auth_handler_settings: dict | None = None,
**kwargs,
) -> None:
"""
Expand All @@ -41,29 +40,34 @@ def __init__(
:type storage: Storage
:param connection_manager: The connection manager for OAuth providers.
:type connection_manager: Connections
:param auth_handlers: Configuration for OAuth providers.
:type auth_handlers: dict[str, AuthHandler], Optional
:param settings: Optional settings for the auth handler.
:type settings: dict | AuthHandlerSettings | None
:param auth_handler: (Deprecated) Configuration for OAuth provider.
:type auth_handler: AuthHandlerSettings | None
:param auth_handler_id: Optional ID of the auth handler.
:type auth_handler_id: str | None
:param auth_handler_settings: (Deprecated) Optional settings dict for the auth handler.
:type auth_handler_settings: dict | None
:raises ValueError: When storage is None or no auth handlers provided.
"""
super().__init__(
storage,
connection_manager,
auth_handler,
settings,
auth_handler=auth_handler,
auth_handler_id=auth_handler_id,
auth_handler_settings=auth_handler_settings,
**kwargs,
)
self._alt_blueprint_name = (
auth_handler._alt_blueprint_name if auth_handler else None
)
self._alt_blueprint_name = self._handler._alt_blueprint_name

async def get_agentic_instance_token(self, context: TurnContext) -> TokenResponse:
"""Gets the agentic instance token for the current agent instance.

:param context: The context object for the current turn.
:type context: TurnContext
:return: The agentic instance token, or None if not an agentic request.
:rtype: Optional[str]
:rtype: TokenResponse
"""

if not context.activity.is_agentic_request():
Expand Down Expand Up @@ -103,7 +107,7 @@ async def get_agentic_user_token(
:param scopes: The scopes to request for the token.
:type scopes: list[str]
:return: The agentic user token, or None if not an agentic request or no user.
:rtype: Optional[str]
:rtype: TokenResponse
"""
logger.info("Retrieving agentic user token for scopes: %s", scopes)

Expand Down Expand Up @@ -163,8 +167,8 @@ async def get_agentic_user_token(
async def _sign_in(
self,
context: TurnContext,
exchange_connection: Optional[str] = None,
exchange_scopes: Optional[list[str]] = None,
exchange_connection: str | None = None,
exchange_scopes: list[str] | None = None,
) -> _SignInResponse:
"""Retrieves the agentic user token if available.

Expand All @@ -173,7 +177,7 @@ async def _sign_in(
:param connection_name: The name of the connection to use for sign-in.
:type connection_name: str
:param scopes: The scopes to request for the token.
:type scopes: Optional[list[str]]
:type scopes: list[str] | None
:return: A _SignInResponse containing the token response and flow state tag.
:rtype: _SignInResponse
"""
Expand All @@ -189,17 +193,17 @@ async def _sign_in(
async def get_refreshed_token(
self,
context: TurnContext,
exchange_connection: Optional[str] = None,
exchange_scopes: Optional[list[str]] = None,
exchange_connection: str | None = None,
exchange_scopes: list[str] | None = None,
) -> TokenResponse:
"""Attempts to get a refreshed token for the user with the given scopes

:param context: The turn context for the current turn of conversation.
:type context: TurnContext
:param exchange_connection: Optional name of the connection to use for token exchange. If None, default connection will be used.
:type exchange_connection: Optional[str], Optional
:type exchange_connection: str | None
:param exchange_scopes: Optional list of scopes to request during token exchange. If None, default scopes will be used.
:type exchange_scopes: Optional[list[str]], Optional
:type exchange_scopes: list[str] | None
"""
with spans.AgenticToken(
auth_handler_id=self._id,
Expand All @@ -211,6 +215,6 @@ async def get_refreshed_token(
return await self.get_agentic_user_token(context, exchange_scopes)

async def sign_out(
self, context: TurnContext, auth_handler_id: Optional[str] = None
self, context: TurnContext, auth_handler_id: str | None = None
) -> None:
"""Nothing to do for agentic sign out."""
Loading
Loading