From 520a7701c75b9a2cbd04091a95915c027a63b85d Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Wed, 30 Jul 2025 16:09:21 -0700 Subject: [PATCH 1/3] Teams sample updates --- test_samples/teams_agent/app.py | 40 +++++++-------------------- test_samples/teams_agent/config.py | 20 ++++++++++---- test_samples/teams_agent/env.TEMPLATE | 12 ++++---- test_samples/teams_agent/teams_sso.py | 3 +- 4 files changed, 31 insertions(+), 44 deletions(-) diff --git a/test_samples/teams_agent/app.py b/test_samples/teams_agent/app.py index 7aa09c4b..099cff43 100644 --- a/test_samples/teams_agent/app.py +++ b/test_samples/teams_agent/app.py @@ -2,51 +2,31 @@ # Licensed under the MIT License. import pathlib +from os import environ, path from dotenv import load_dotenv from aiohttp.web import Application, Request, Response, run_app -from microsoft.agents.hosting.core import RestChannelServiceClientFactory -from microsoft.agents.hosting.core.state import UserState +from microsoft.agents.activity import load_configuration_from_env +from microsoft.agents.authentication.msal import MsalConnectionManager from microsoft.agents.hosting.aiohttp import CloudAdapter, jwt_authorization_decorator -from microsoft.agents.hosting.core.authorization import ( - Connections, - AccessTokenProviderBase, - ClaimsIdentity, -) -from microsoft.agents.authentication.msal import MsalAuth -from microsoft.agents.hosting.core.storage import MemoryStorage +from microsoft.agents.hosting.core import Authorization, MemoryStorage, UserState from teams_handler import TeamsHandler from teams_sso import TeamsSso from teams_multi_feature import TeamsMultiFeature from config import DefaultConfig -load_dotenv() +load_dotenv(path.join(path.dirname(__file__), ".env")) CONFIG = DefaultConfig() -AUTH_PROVIDER = MsalAuth(DefaultConfig()) +agents_sdk_config = load_configuration_from_env(environ) -class DefaultConnection(Connections): - def get_default_connection(self) -> AccessTokenProviderBase: - pass - - def get_token_provider( - self, claims_identity: ClaimsIdentity, service_url: str - ) -> AccessTokenProviderBase: - return AUTH_PROVIDER - - def get_connection(self, connection_name: str) -> AccessTokenProviderBase: - return AUTH_PROVIDER - - -CHANNEL_CLIENT_FACTORY = RestChannelServiceClientFactory(CONFIG, DefaultConnection()) - -# Create adapter. -ADAPTER = CloudAdapter(CHANNEL_CLIENT_FACTORY) - -# Create the storage and user state (for SSO agent) STORAGE = MemoryStorage() +CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config) +ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER) +AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config) + USER_STATE = UserState(STORAGE) diff --git a/test_samples/teams_agent/config.py b/test_samples/teams_agent/config.py index ee9c3299..f0496df1 100644 --- a/test_samples/teams_agent/config.py +++ b/test_samples/teams_agent/config.py @@ -1,16 +1,24 @@ from os import environ -from microsoft.agents.authentication.msal import AuthTypes, MsalAuthConfiguration +from microsoft.agents.hosting.core import AuthTypes, AgentAuthConfiguration -class DefaultConfig(MsalAuthConfiguration): +class DefaultConfig(AgentAuthConfiguration): """Teams Agent Configuration""" def __init__(self) -> None: self.AUTH_TYPE = AuthTypes.client_secret - self.TENANT_ID = "" or environ.get("TENANT_ID") - self.CLIENT_ID = "" or environ.get("CLIENT_ID") - self.CLIENT_SECRET = "" or environ.get("CLIENT_SECRET") - self.CONNECTION_NAME = "" or environ.get("CONNECTION_NAME") + self.TENANT_ID = "" or environ.get( + "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID" + ) + self.CLIENT_ID = "" or environ.get( + "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID" + ) + self.CLIENT_SECRET = "" or environ.get( + "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET" + ) + self.CONNECTION_NAME = "" or environ.get( + "AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME" + ) self.AGENT_TYPE = environ.get( "AGENT_TYPE", "TeamsHandler" ) # Default to TeamsHandler diff --git a/test_samples/teams_agent/env.TEMPLATE b/test_samples/teams_agent/env.TEMPLATE index d147897d..5c857b7c 100644 --- a/test_samples/teams_agent/env.TEMPLATE +++ b/test_samples/teams_agent/env.TEMPLATE @@ -1,7 +1,7 @@ # Rename to .env -TENANT_ID= -CLIENT_ID= -CLIENT_SECRET= -AGENT_TYPE=TeamsSso -BASE_URL= -CONNECTION_NAME= \ No newline at end of file +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=client-id +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=client-secret +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=tenant-id + +AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=connection-name +AGENT_TYPE=TeamsSso \ No newline at end of file diff --git a/test_samples/teams_agent/teams_sso.py b/test_samples/teams_agent/teams_sso.py index 24ad0319..4fb4f2e3 100644 --- a/test_samples/teams_agent/teams_sso.py +++ b/test_samples/teams_agent/teams_sso.py @@ -1,12 +1,11 @@ from microsoft.agents.hosting.core import ( - ActivityHandler, OAuthFlow, MessageFactory, TurnContext, ) from microsoft.agents.hosting.core.state import UserState from microsoft.agents.activity import ChannelAccount -from microsoft.agents.hosting.teams import TeamsActivityHandler, TeamsInfo +from microsoft.agents.hosting.teams import TeamsActivityHandler from graph_client import GraphClient From 900df40d798eb7bc0eed2d642247e9ee44371f13 Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Thu, 31 Jul 2025 16:54:42 -0700 Subject: [PATCH 2/3] Teams sample updates --- test_samples/teams_agent/app.py | 2 +- test_samples/teams_agent/teams_sso.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/test_samples/teams_agent/app.py b/test_samples/teams_agent/app.py index 099cff43..a3129611 100644 --- a/test_samples/teams_agent/app.py +++ b/test_samples/teams_agent/app.py @@ -35,7 +35,7 @@ def create_agent(agent_type: str): Create the appropriate agent based on configuration. """ if agent_type == "TeamsSso": - return TeamsSso(USER_STATE, CONFIG.CONNECTION_NAME, CONFIG.CLIENT_ID) + return TeamsSso(STORAGE, USER_STATE, CONFIG.CONNECTION_NAME, CONFIG.CLIENT_ID) elif agent_type == "TeamsMultiFeature": return TeamsMultiFeature() else: # Default to TeamsHandler diff --git a/test_samples/teams_agent/teams_sso.py b/test_samples/teams_agent/teams_sso.py index 4fb4f2e3..6c0c5c9f 100644 --- a/test_samples/teams_agent/teams_sso.py +++ b/test_samples/teams_agent/teams_sso.py @@ -2,8 +2,9 @@ OAuthFlow, MessageFactory, TurnContext, + UserState, + Storage, ) -from microsoft.agents.hosting.core.state import UserState from microsoft.agents.activity import ChannelAccount from microsoft.agents.hosting.teams import TeamsActivityHandler @@ -12,7 +13,11 @@ class TeamsSso(TeamsActivityHandler): def __init__( - self, user_state: UserState, connection_name: str = None, app_id: str = None + self, + storage: Storage, + user_state: UserState, + connection_name: str = None, + app_id: str = None, ): """ Initializes a new instance of the TeamsSso class. @@ -21,7 +26,7 @@ def __init__( :param app_id: AgentApplication ID. """ self.user_state = user_state - self.oauth_flow = OAuthFlow(user_state, connection_name) + self.oauth_flow = OAuthFlow(storage, connection_name) async def on_sign_in_invoke(self, turn_context): # Log Event trigggered From 11a02199e206e07580c572674445393048753335 Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Mon, 4 Aug 2025 10:09:41 -0700 Subject: [PATCH 3/3] Teams sample updates --- .../agents/authentication/msal/msal_auth.py | 13 +++++++++++-- test_samples/app_style/authorization_agent.py | 5 ++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/libraries/microsoft-agents-authentication-msal/microsoft/agents/authentication/msal/msal_auth.py b/libraries/microsoft-agents-authentication-msal/microsoft/agents/authentication/msal/msal_auth.py index 985d0644..93c1eaf3 100644 --- a/libraries/microsoft-agents-authentication-msal/microsoft/agents/authentication/msal/msal_auth.py +++ b/libraries/microsoft-agents-authentication-msal/microsoft/agents/authentication/msal/msal_auth.py @@ -80,9 +80,18 @@ async def aquire_token_on_behalf_of( ) elif isinstance(msal_auth_client, ConfidentialClientApplication): # TODO: Handling token error / acquisition failed - return msal_auth_client.acquire_token_on_behalf_of( + + token = msal_auth_client.acquire_token_on_behalf_of( user_assertion=user_assertion, scopes=scopes - )["access_token"] + ) + + if "access_token" not in token: + logger.error( + f"Failed to acquire token on behalf of user: {user_assertion}" + ) + raise ValueError(f"Failed to acquire token. {str(token)}") + + return token["access_token"] logger.error( f"On-behalf-of flow is not supported with the current authentication type: {msal_auth_client.__class__.__name__}" diff --git a/test_samples/app_style/authorization_agent.py b/test_samples/app_style/authorization_agent.py index 77dd41f7..5fe111ed 100644 --- a/test_samples/app_style/authorization_agent.py +++ b/test_samples/app_style/authorization_agent.py @@ -117,7 +117,10 @@ async def profile_request(context: TurnContext, state: TurnState) -> dict: return None try: - token_response = await AGENT_APP.auth.get_token(context, "GRAPH") + # token_to_exchange = await AGENT_APP.auth.get_token(context, "GRAPH") + token_response = await AGENT_APP.auth.exchange_token( + context, scopes=["User.Read", "email"], auth_handler_id="GRAPH" + ) if not token_response or not token_response.token: await context.send_activity( MessageFactory.text(