11# Copyright (c) Microsoft Corporation. All rights reserved.
22# Licensed under the MIT License.
33
4+ # pylint: disable=too-many-lines
5+
46import asyncio
57import base64
68import json
2224 JwtTokenValidation ,
2325 SimpleCredentialProvider ,
2426 SkillValidation ,
27+ CertificateAppCredentials ,
2528)
2629from botframework .connector .token_api import TokenApiClient
2730from botframework .connector .token_api .models import TokenStatus
@@ -76,13 +79,15 @@ class BotFrameworkAdapterSettings:
7679 def __init__ (
7780 self ,
7881 app_id : str ,
79- app_password : str ,
82+ app_password : str = None ,
8083 channel_auth_tenant : str = None ,
8184 oauth_endpoint : str = None ,
8285 open_id_metadata : str = None ,
8386 channel_service : str = None ,
8487 channel_provider : ChannelProvider = None ,
8588 auth_configuration : AuthenticationConfiguration = None ,
89+ certificate_thumbprint : str = None ,
90+ certificate_private_key : str = None ,
8691 ):
8792 """
8893 Contains the settings used to initialize a :class:`BotFrameworkAdapter` instance.
@@ -104,6 +109,15 @@ def __init__(
104109 :type channel_provider: :class:`botframework.connector.auth.ChannelProvider`
105110 :param auth_configuration:
106111 :type auth_configuration: :class:`botframework.connector.auth.AuthenticationConfiguration`
112+ :param certificate_thumbprint: X509 thumbprint
113+ :type certificate_thumbprint: str
114+ :param certificate_private_key: X509 private key
115+ :type certificate_private_key: str
116+
117+ .. remarks::
118+ For credentials authorization, both app_id and app_password are required.
119+ For certificate authorization, app_id, certificate_thumbprint, and certificate_private_key are required.
120+
107121 """
108122 self .app_id = app_id
109123 self .app_password = app_password
@@ -113,6 +127,8 @@ def __init__(
113127 self .channel_service = channel_service
114128 self .channel_provider = channel_provider
115129 self .auth_configuration = auth_configuration or AuthenticationConfiguration ()
130+ self .certificate_thumbprint = certificate_thumbprint
131+ self .certificate_private_key = certificate_private_key
116132
117133
118134class BotFrameworkAdapter (BotAdapter , UserTokenProvider ):
@@ -141,23 +157,42 @@ def __init__(self, settings: BotFrameworkAdapterSettings):
141157 """
142158 super (BotFrameworkAdapter , self ).__init__ ()
143159 self .settings = settings or BotFrameworkAdapterSettings ("" , "" )
160+
161+ # If settings.certificate_thumbprint & settings.certificate_private_key are provided,
162+ # use CertificateAppCredentials.
163+ if self .settings .certificate_thumbprint and settings .certificate_private_key :
164+ self ._credentials = CertificateAppCredentials (
165+ self .settings .app_id ,
166+ self .settings .certificate_thumbprint ,
167+ self .settings .certificate_private_key ,
168+ self .settings .channel_auth_tenant ,
169+ )
170+ self ._credential_provider = SimpleCredentialProvider (
171+ self .settings .app_id , ""
172+ )
173+ else :
174+ self ._credentials = MicrosoftAppCredentials (
175+ self .settings .app_id ,
176+ self .settings .app_password ,
177+ self .settings .channel_auth_tenant ,
178+ )
179+ self ._credential_provider = SimpleCredentialProvider (
180+ self .settings .app_id , self .settings .app_password
181+ )
182+
183+ self ._is_emulating_oauth_cards = False
184+
185+ # If no channel_service or open_id_metadata values were passed in the settings, check the
186+ # process' Environment Variables for values.
187+ # These values may be set when a bot is provisioned on Azure and if so are required for
188+ # the bot to properly work in Public Azure or a National Cloud.
144189 self .settings .channel_service = self .settings .channel_service or os .environ .get (
145190 AuthenticationConstants .CHANNEL_SERVICE
146191 )
147-
148192 self .settings .open_id_metadata = (
149193 self .settings .open_id_metadata
150194 or os .environ .get (AuthenticationConstants .BOT_OPEN_ID_METADATA_KEY )
151195 )
152- self ._credentials = MicrosoftAppCredentials (
153- self .settings .app_id ,
154- self .settings .app_password ,
155- self .settings .channel_auth_tenant ,
156- )
157- self ._credential_provider = SimpleCredentialProvider (
158- self .settings .app_id , self .settings .app_password
159- )
160- self ._is_emulating_oauth_cards = False
161196
162197 if self .settings .open_id_metadata :
163198 ChannelValidation .open_id_metadata_endpoint = self .settings .open_id_metadata
@@ -878,35 +913,39 @@ async def create_connector_client(
878913
879914 :return: An instance of the :class:`ConnectorClient` class
880915 """
916+
917+ # Anonymous claims and non-skill claims should fall through without modifying the scope.
918+ credentials = self ._credentials
919+
881920 if identity :
882921 bot_app_id_claim = identity .claims .get (
883922 AuthenticationConstants .AUDIENCE_CLAIM
884923 ) or identity .claims .get (AuthenticationConstants .APP_ID_CLAIM )
885924
886- credentials = None
887925 if bot_app_id_claim and SkillValidation .is_skill_claim (identity .claims ):
888926 scope = JwtTokenValidation .get_app_id_from_claims (identity .claims )
889927
890- password = await self ._credential_provider .get_app_password (
891- bot_app_id_claim
892- )
893- credentials = MicrosoftAppCredentials (
894- bot_app_id_claim , password , oauth_scope = scope
895- )
896- if (
897- self .settings .channel_provider
898- and self .settings .channel_provider .is_government ()
899- ):
900- credentials .oauth_endpoint = (
901- GovernmentConstants .TO_CHANNEL_FROM_BOT_LOGIN_URL
928+ # Do nothing, if the current credentials and its scope are valid for the skill.
929+ # i.e. the adapter instance is pre-configured to talk with one skill.
930+ # Otherwise we will create a new instance of the AppCredentials
931+ # so self._credentials.oauth_scope isn't overridden.
932+ if self ._credentials .oauth_scope != scope :
933+ password = await self ._credential_provider .get_app_password (
934+ bot_app_id_claim
902935 )
903- credentials . oauth_scope = (
904- GovernmentConstants . TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
936+ credentials = MicrosoftAppCredentials (
937+ bot_app_id_claim , password , oauth_scope = scope
905938 )
906- else :
907- credentials = self ._credentials
908- else :
909- credentials = self ._credentials
939+ if (
940+ self .settings .channel_provider
941+ and self .settings .channel_provider .is_government ()
942+ ):
943+ credentials .oauth_endpoint = (
944+ GovernmentConstants .TO_CHANNEL_FROM_BOT_LOGIN_URL
945+ )
946+ credentials .oauth_scope = (
947+ GovernmentConstants .TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
948+ )
910949
911950 client_key = (
912951 f"{ service_url } { credentials .microsoft_app_id if credentials else '' } "
0 commit comments