2121 ConversationAccount ,
2222 ConversationReference ,
2323 Credentials ,
24- JsonWebToken ,
2524 MessageActivityInput ,
2625 TokenCredentials ,
2726)
3231from .app_oauth import OauthHandlers
3332from .app_plugins import PluginProcessor
3433from .app_process import ActivityProcessor
35- from .app_tokens import AppTokens
3634from .auth import TokenValidator
3735from .auth .remote_function_jwt_middleware import remote_function_jwt_validation
3836from .container import Container
4543 get_event_type_from_signature ,
4644 is_registered_event ,
4745)
48- from .graph_token_manager import GraphTokenManager
4946from .http_plugin import HttpPlugin
5047from .options import AppOptions , InternalAppOptions
5148from .plugins import PluginBase , PluginStartEvent , get_metadata
5249from .routing import ActivityHandlerMixin , ActivityRouter
5350from .routing .activity_context import ActivityContext
51+ from .token_manager import TokenManager
5452
5553version = importlib .metadata .version ("microsoft-teams-apps" )
5654
@@ -83,22 +81,26 @@ def __init__(self, **options: Unpack[AppOptions]):
8381 self ._events = EventEmitter [EventType ]()
8482 self ._router = ActivityRouter ()
8583
86- self ._tokens = AppTokens ()
8784 self .credentials = self ._init_credentials ()
8885
86+ self ._token_manager = TokenManager (
87+ http_client = self .http_client ,
88+ credentials = self .credentials ,
89+ logger = self .log ,
90+ default_connection_name = self .options .default_connection_name ,
91+ )
92+
8993 self .container = Container ()
9094 self .container .set_provider ("id" , providers .Object (self .id ))
91- self .container .set_provider ("name" , providers .Object (self .name ))
9295 self .container .set_provider ("credentials" , providers .Object (self .credentials ))
93- self .container .set_provider ("bot_token" , providers .Callable (lambda : self .tokens .bot ))
94- self .container .set_provider ("graph_token" , providers .Callable (lambda : self .tokens .graph ))
96+ self .container .set_provider ("bot_token" , providers .Factory (lambda : self ._get_or_get_bot_token ))
9597 self .container .set_provider ("logger" , providers .Object (self .log ))
9698 self .container .set_provider ("storage" , providers .Object (self .storage ))
9799 self .container .set_provider (self .http_client .__class__ .__name__ , providers .Factory (lambda : self .http_client ))
98100
99101 self .api = ApiClient (
100102 "https://smba.trafficmanager.net/teams" ,
101- self .http_client .clone (ClientOptions (token = lambda : self .tokens . bot )),
103+ self .http_client .clone (ClientOptions (token = self ._get_or_get_bot_token )),
102104 )
103105
104106 plugins : List [PluginBase ] = list (self .options .plugins )
@@ -125,20 +127,14 @@ def __init__(self, **options: Unpack[AppOptions]):
125127 self ._running = False
126128
127129 # initialize all event, activity, and plugin processors
128- self .graph_token_manager = GraphTokenManager (
129- api_client = self .api ,
130- credentials = self .credentials ,
131- logger = self .log ,
132- )
133130 self .activity_processor = ActivityProcessor (
134131 self ._router ,
135132 self .log ,
136133 self .id ,
137134 self .storage ,
138135 self .options .default_connection_name ,
139136 self .http_client ,
140- self .tokens ,
141- self .graph_token_manager ,
137+ self ._token_manager ,
142138 )
143139 self .event_manager = EventManager (self ._events , self .activity_processor )
144140 self .activity_processor .event_manager = self .event_manager
@@ -169,11 +165,6 @@ def is_running(self) -> bool:
169165 """Whether the app is currently running."""
170166 return self ._running
171167
172- @property
173- def tokens (self ) -> AppTokens :
174- """Current authentication tokens."""
175- return self ._tokens
176-
177168 @property
178169 def logger (self ) -> Logger :
179170 """The logger instance used by the app."""
@@ -191,17 +182,10 @@ def router(self) -> ActivityRouter:
191182
192183 @property
193184 def id (self ) -> Optional [str ]:
194- """The app's ID from tokens."""
195- return (
196- self ._tokens .bot .app_id if self ._tokens .bot else self ._tokens .graph .app_id if self ._tokens .graph else None
197- )
198-
199- @property
200- def name (self ) -> Optional [str ]:
201- """The app's name from tokens."""
202- return getattr (self ._tokens .bot , "app_display_name" , None ) or getattr (
203- self ._tokens .graph , "app_display_name" , None
204- )
185+ """The app's ID from credentials."""
186+ if not self .credentials :
187+ return None
188+ return self .credentials .client_id
205189
206190 async def start (self , port : Optional [int ] = None ) -> None :
207191 """
@@ -220,9 +204,6 @@ async def start(self, port: Optional[int] = None) -> None:
220204 self ._port = port or int (os .getenv ("PORT" , "3978" ))
221205
222206 try :
223- await self ._refresh_tokens (force = True )
224- self ._running = True
225-
226207 for plugin in self .plugins :
227208 # Inject the dependencies
228209 self ._plugin_processor .inject (plugin )
@@ -234,6 +215,7 @@ async def on_http_ready() -> None:
234215 self .log .info ("Teams app started successfully" )
235216 assert self ._port is not None , "Port must be set before emitting start event"
236217 self ._events .emit ("start" , StartEvent (port = self ._port ))
218+ self ._running = True
237219
238220 self .http .on_ready_callback = on_http_ready
239221
@@ -280,13 +262,13 @@ async def on_http_stopped() -> None:
280262 async def send (self , conversation_id : str , activity : str | ActivityParams | AdaptiveCard ):
281263 """Send an activity proactively."""
282264
283- if self .id is None or self . name is None :
265+ if self .id is None :
284266 raise ValueError ("app not started" )
285267
286268 conversation_ref = ConversationReference (
287269 channel_id = "msteams" ,
288270 service_url = self .api .service_url ,
289- bot = Account (id = self .id , name = self . name , role = "bot" ),
271+ bot = Account (id = self .id , role = "bot" ),
290272 conversation = ConversationAccount (id = conversation_id , conversation_type = "personal" ),
291273 )
292274
@@ -326,65 +308,6 @@ def _init_credentials(self) -> Optional[Credentials]:
326308
327309 return None
328310
329- async def _refresh_tokens (self , force : bool = False ) -> None :
330- """Refresh bot and graph tokens."""
331- await asyncio .gather (self ._refresh_bot_token (force ), self ._refresh_graph_token (force ), return_exceptions = True )
332-
333- async def _refresh_bot_token (self , force : bool = False ) -> None :
334- """Refresh the bot authentication token."""
335- if not self .credentials :
336- self .log .warning ("No credentials provided, skipping bot token refresh" )
337- return
338-
339- if not force and self ._tokens .bot and not self ._tokens .bot .is_expired ():
340- return
341-
342- if self ._tokens .bot :
343- self .log .debug ("Refreshing bot token" )
344-
345- try :
346- token_response = await self .api .bots .token .get (self .credentials )
347- self ._tokens .bot = JsonWebToken (token_response .access_token )
348- self .log .debug ("Bot token refreshed successfully" )
349-
350- except Exception as error :
351- self .log .error (f"Failed to refresh bot token: { error } " )
352-
353- self ._events .emit ("error" , ErrorEvent (error , context = {"method" : "_refresh_bot_token" }))
354- raise
355-
356- async def _refresh_graph_token (self , force : bool = False ) -> None :
357- """Refresh the Graph API token."""
358- if not self .credentials :
359- self .log .warning ("No credentials provided, skipping graph token refresh" )
360- return
361-
362- if not force and self ._tokens .graph and not self ._tokens .graph .is_expired ():
363- return
364-
365- if self ._tokens .graph :
366- self .log .debug ("Refreshing graph token" )
367-
368- try :
369- # Use GraphTokenManager for tenant-aware token management
370- tenant_id = self .credentials .tenant_id if self .credentials else None
371- token = await self .graph_token_manager .get_token (tenant_id )
372-
373- if token :
374- self ._tokens .graph = token
375- self .log .debug ("Graph token refreshed successfully" )
376-
377- # Emit token acquired event
378- self ._events .emit ("token" , {"type" : "graph" , "token" : self ._tokens .graph })
379- else :
380- self .log .debug ("Failed to get graph token from GraphTokenManager" )
381-
382- except Exception as error :
383- self .log .error (f"Failed to refresh graph token: { error } " )
384-
385- self ._events .emit ("error" , ErrorEvent (error , context = {"method" : "_refresh_graph_token" }))
386- raise
387-
388311 @overload
389312 def event (self , func_or_event_type : F ) -> F :
390313 """Register event handler with auto-detected type from function signature."""
@@ -521,7 +444,6 @@ async def endpoint(req: Request):
521444 async def call_next (r : Request ) -> Any :
522445 ctx = FunctionContext (
523446 id = self .id ,
524- name = self .name ,
525447 api = self .api ,
526448 http = self .http ,
527449 log = self .log ,
@@ -541,3 +463,6 @@ async def call_next(r: Request) -> Any:
541463
542464 # Named decoration: @app.func("name")
543465 return decorator
466+
467+ async def _get_or_get_bot_token (self ):
468+ return await self ._token_manager .get_bot_token ()
0 commit comments