@@ -30,6 +30,10 @@ async def __call__(
3030 enterprise_id : Optional [str ],
3131 team_id : Optional [str ], # can be None for org-wide installed apps
3232 user_id : Optional [str ],
33+ # actor_* can be used only when user_token_resolution: "actor" is set
34+ actor_enterprise_id : Optional [str ] = None ,
35+ actor_team_id : Optional [str ] = None ,
36+ actor_user_id : Optional [str ] = None ,
3337 ) -> Optional [AuthorizeResult ]:
3438 raise NotImplementedError ()
3539
@@ -51,6 +55,10 @@ async def __call__(
5155 enterprise_id : Optional [str ],
5256 team_id : Optional [str ], # can be None for org-wide installed apps
5357 user_id : Optional [str ],
58+ # actor_* can be used only when user_token_resolution: "actor" is set
59+ actor_enterprise_id : Optional [str ] = None ,
60+ actor_team_id : Optional [str ] = None ,
61+ actor_user_id : Optional [str ] = None ,
5462 ) -> Optional [AuthorizeResult ]:
5563 try :
5664 all_available_args = {
@@ -66,6 +74,9 @@ async def __call__(
6674 "enterprise_id" : enterprise_id ,
6775 "team_id" : team_id ,
6876 "user_id" : user_id ,
77+ "actor_enterprise_id" : actor_enterprise_id ,
78+ "actor_team_id" : actor_team_id ,
79+ "actor_user_id" : actor_user_id ,
6980 }
7081 for k , v in context .items ():
7182 if k not in all_available_args :
@@ -103,6 +114,8 @@ class AsyncInstallationStoreAuthorize(AsyncAuthorize):
103114 """
104115
105116 authorize_result_cache : Dict [str , AuthorizeResult ]
117+ bot_only : bool
118+ user_token_resolution : str
106119 find_installation_available : Optional [bool ]
107120 find_bot_available : Optional [bool ]
108121 token_rotator : Optional [AsyncTokenRotator ]
@@ -122,10 +135,13 @@ def __init__(
122135 bot_only : bool = False ,
123136 cache_enabled : bool = False ,
124137 client : Optional [AsyncWebClient ] = None ,
138+ # Since v1.27, user token resolution can be actor ID based when the mode is enabled
139+ user_token_resolution : str = "authed_user" ,
125140 ):
126141 self .logger = logger
127142 self .installation_store = installation_store
128143 self .bot_only = bot_only
144+ self .user_token_resolution = user_token_resolution
129145 self .cache_enabled = cache_enabled
130146 self .authorize_result_cache = {}
131147 self .find_installation_available = None
@@ -147,6 +163,10 @@ async def __call__(
147163 enterprise_id : Optional [str ],
148164 team_id : Optional [str ], # can be None for org-wide installed apps
149165 user_id : Optional [str ],
166+ # actor_* can be used only when user_token_resolution: "actor" is set
167+ actor_enterprise_id : Optional [str ] = None ,
168+ actor_team_id : Optional [str ] = None ,
169+ actor_user_id : Optional [str ] = None ,
150170 ) -> Optional [AuthorizeResult ]:
151171
152172 if self .find_installation_available is None :
@@ -194,16 +214,34 @@ async def __call__(
194214
195215 # try to fetch the request user's installation
196216 # to reflect the user's access token if exists
197- this_user_installation = await self .installation_store .async_find_installation (
198- enterprise_id = enterprise_id ,
199- team_id = team_id ,
200- user_id = user_id ,
201- is_enterprise_install = context .is_enterprise_install ,
202- )
217+ # try to fetch the request user's installation
218+ # to reflect the user's access token if exists
219+ if self .user_token_resolution == "actor" :
220+ if actor_enterprise_id is not None or actor_team_id is not None :
221+ # Note that actor_team_id can be absent for app_mention events
222+ this_user_installation = await self .installation_store .async_find_installation (
223+ enterprise_id = actor_enterprise_id ,
224+ team_id = actor_team_id ,
225+ user_id = actor_user_id ,
226+ is_enterprise_install = None ,
227+ )
228+ else :
229+ this_user_installation = await self .installation_store .async_find_installation (
230+ enterprise_id = enterprise_id ,
231+ team_id = team_id ,
232+ user_id = user_id ,
233+ is_enterprise_install = context .is_enterprise_install ,
234+ )
203235 if this_user_installation is not None :
204236 user_token = this_user_installation .user_token
205237 user_scopes = this_user_installation .user_scopes
206- if latest_bot_installation .bot_token is None :
238+ if (
239+ latest_bot_installation .bot_token is None
240+ # enterprise_id/team_id can be different for Slack Connect channel events
241+ # when enabling user_token_resolution: "actor"
242+ and latest_bot_installation .enterprise_id == this_user_installation .enterprise_id
243+ and latest_bot_installation .team_id == this_user_installation .team_id
244+ ):
207245 # If latest_installation has a bot token, we never overwrite the value
208246 bot_token = this_user_installation .bot_token
209247 bot_scopes = this_user_installation .bot_scopes
@@ -213,7 +251,13 @@ async def __call__(
213251 if refreshed is not None :
214252 user_token = refreshed .user_token
215253 user_scopes = refreshed .user_scopes
216- if latest_bot_installation .bot_token is None :
254+ if (
255+ latest_bot_installation .bot_token is None
256+ # enterprise_id/team_id can be different for Slack Connect channel events
257+ # when enabling user_token_resolution: "actor"
258+ and latest_bot_installation .enterprise_id == this_user_installation .enterprise_id
259+ and latest_bot_installation .team_id == this_user_installation .team_id
260+ ):
217261 # If latest_installation has a bot token, we never overwrite the value
218262 bot_token = refreshed .bot_token
219263 bot_scopes = refreshed .bot_scopes
0 commit comments