1
1
# Copyright (c) Microsoft Corporation. All rights reserved.
2
2
# Licensed under the MIT License.
3
3
4
+ # pylint: disable=too-many-lines
5
+
4
6
import asyncio
5
7
import base64
6
8
import json
22
24
JwtTokenValidation ,
23
25
SimpleCredentialProvider ,
24
26
SkillValidation ,
27
+ CertificateAppCredentials ,
25
28
)
26
29
from botframework .connector .token_api import TokenApiClient
27
30
from botframework .connector .token_api .models import TokenStatus
@@ -76,13 +79,15 @@ class BotFrameworkAdapterSettings:
76
79
def __init__ (
77
80
self ,
78
81
app_id : str ,
79
- app_password : str ,
82
+ app_password : str = None ,
80
83
channel_auth_tenant : str = None ,
81
84
oauth_endpoint : str = None ,
82
85
open_id_metadata : str = None ,
83
86
channel_service : str = None ,
84
87
channel_provider : ChannelProvider = None ,
85
88
auth_configuration : AuthenticationConfiguration = None ,
89
+ certificate_thumbprint : str = None ,
90
+ certificate_private_key : str = None ,
86
91
):
87
92
"""
88
93
Contains the settings used to initialize a :class:`BotFrameworkAdapter` instance.
@@ -104,6 +109,15 @@ def __init__(
104
109
:type channel_provider: :class:`botframework.connector.auth.ChannelProvider`
105
110
:param auth_configuration:
106
111
: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
+
107
121
"""
108
122
self .app_id = app_id
109
123
self .app_password = app_password
@@ -113,6 +127,8 @@ def __init__(
113
127
self .channel_service = channel_service
114
128
self .channel_provider = channel_provider
115
129
self .auth_configuration = auth_configuration or AuthenticationConfiguration ()
130
+ self .certificate_thumbprint = certificate_thumbprint
131
+ self .certificate_private_key = certificate_private_key
116
132
117
133
118
134
class BotFrameworkAdapter (BotAdapter , UserTokenProvider ):
@@ -141,23 +157,42 @@ def __init__(self, settings: BotFrameworkAdapterSettings):
141
157
"""
142
158
super (BotFrameworkAdapter , self ).__init__ ()
143
159
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.
144
189
self .settings .channel_service = self .settings .channel_service or os .environ .get (
145
190
AuthenticationConstants .CHANNEL_SERVICE
146
191
)
147
-
148
192
self .settings .open_id_metadata = (
149
193
self .settings .open_id_metadata
150
194
or os .environ .get (AuthenticationConstants .BOT_OPEN_ID_METADATA_KEY )
151
195
)
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
161
196
162
197
if self .settings .open_id_metadata :
163
198
ChannelValidation .open_id_metadata_endpoint = self .settings .open_id_metadata
@@ -878,35 +913,39 @@ async def create_connector_client(
878
913
879
914
:return: An instance of the :class:`ConnectorClient` class
880
915
"""
916
+
917
+ # Anonymous claims and non-skill claims should fall through without modifying the scope.
918
+ credentials = self ._credentials
919
+
881
920
if identity :
882
921
bot_app_id_claim = identity .claims .get (
883
922
AuthenticationConstants .AUDIENCE_CLAIM
884
923
) or identity .claims .get (AuthenticationConstants .APP_ID_CLAIM )
885
924
886
- credentials = None
887
925
if bot_app_id_claim and SkillValidation .is_skill_claim (identity .claims ):
888
926
scope = JwtTokenValidation .get_app_id_from_claims (identity .claims )
889
927
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
-
10000
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
902
935
)
903
- credentials . oauth_scope = (
904
- GovernmentConstants . TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
936
+ credentials = MicrosoftAppCredentials (
937
+ bot_app_id_claim , password , oauth_scope = scope
905
938
)
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
+ )
910
949
911
950
client_key = (
912
951
f"{ service_url } { credentials .microsoft_app_id if credentials else '' } "
0 commit comments