1+ import functools
2+ import json
13import time
24try : # Python 2
35 from urlparse import urljoin
@@ -54,11 +56,11 @@ def decorate_scope(
5456CLIENT_CURRENT_TELEMETRY = 'x-client-current-telemetry'
5557
5658def _get_new_correlation_id ():
57- return str (uuid .uuid4 ())
59+ return str (uuid .uuid4 ())
5860
5961
6062def _build_current_telemetry_request_header (public_api_id , force_refresh = False ):
61- return "1|{},{}|" .format (public_api_id , "1" if force_refresh else "0" )
63+ return "1|{},{}|" .format (public_api_id , "1" if force_refresh else "0" )
6264
6365
6466def extract_certs (public_cert_content ):
@@ -92,6 +94,7 @@ def __init__(
9294 self , client_id ,
9395 client_credential = None , authority = None , validate_authority = True ,
9496 token_cache = None ,
97+ http_client = None ,
9598 verify = True , proxies = None , timeout = None ,
9699 client_claims = None , app_name = None , app_version = None ):
97100 """Create an instance of application.
@@ -151,18 +154,24 @@ def __init__(
151154 :param TokenCache cache:
152155 Sets the token cache used by this ClientApplication instance.
153156 By default, an in-memory cache will be created and used.
157+ :param http_client: (optional)
158+ Your implementation of abstract class HttpClient <msal.oauth2cli.http.http_client>
159+ Defaults to a requests session instance
154160 :param verify: (optional)
155161 It will be passed to the
156162 `verify parameter in the underlying requests library
157163 <http://docs.python-requests.org/en/v2.9.1/user/advanced/#ssl-cert-verification>`_
164+ This does not apply if you have chosen to pass your own Http client
158165 :param proxies: (optional)
159166 It will be passed to the
160167 `proxies parameter in the underlying requests library
161168 <http://docs.python-requests.org/en/v2.9.1/user/advanced/#proxies>`_
169+ This does not apply if you have chosen to pass your own Http client
162170 :param timeout: (optional)
163171 It will be passed to the
164172 `timeout parameter in the underlying requests library
165173 <http://docs.python-requests.org/en/v2.9.1/user/advanced/#timeouts>`_
174+ This does not apply if you have chosen to pass your own Http client
166175 :param app_name: (optional)
167176 You can provide your application name for Microsoft telemetry purposes.
168177 Default value is None, means it will not be passed to Microsoft.
@@ -173,14 +182,21 @@ def __init__(
173182 self .client_id = client_id
174183 self .client_credential = client_credential
175184 self .client_claims = client_claims
176- self .verify = verify
177- self .proxies = proxies
178- self .timeout = timeout
185+ if http_client :
186+ self .http_client = http_client
187+ else :
188+ self .http_client = requests .Session ()
189+ self .http_client .verify = verify
190+ self .http_client .proxies = proxies
191+ # Requests, does not support session - wide timeout
192+ # But you can patch that (https://github.com/psf/requests/issues/3341):
193+ self .http_client .request = functools .partial (
194+ self .http_client .request , timeout = timeout )
179195 self .app_name = app_name
180196 self .app_version = app_version
181197 self .authority = Authority (
182198 authority or "https://login.microsoftonline.com/common/" ,
183- validate_authority , verify = verify , proxies = proxies , timeout = timeout )
199+ self . http_client , validate_authority = validate_authority )
184200 # Here the self.authority is not the same type as authority in input
185201 self .token_cache = token_cache or TokenCache ()
186202 self .client = self ._build_client (client_credential , self .authority )
@@ -223,14 +239,14 @@ def _build_client(self, client_credential, authority):
223239 return Client (
224240 server_configuration ,
225241 self .client_id ,
242+ http_client = self .http_client ,
226243 default_headers = default_headers ,
227244 default_body = default_body ,
228245 client_assertion = client_assertion ,
229246 client_assertion_type = client_assertion_type ,
230247 on_obtaining_tokens = self .token_cache .add ,
231248 on_removing_rt = self .token_cache .remove_rt ,
232- on_updating_rt = self .token_cache .update_rt ,
233- verify = self .verify , proxies = self .proxies , timeout = self .timeout )
249+ on_updating_rt = self .token_cache .update_rt )
234250
235251 def get_authorization_request_url (
236252 self ,
@@ -288,12 +304,13 @@ def get_authorization_request_url(
288304 # Multi-tenant app can use new authority on demand
289305 the_authority = Authority (
290306 authority ,
291- verify = self .verify , proxies = self . proxies , timeout = self . timeout ,
307+ self .http_client
292308 ) if authority else self .authority
293309
294310 client = Client (
295311 {"authorization_endpoint" : the_authority .authorization_endpoint },
296- self .client_id )
312+ self .client_id ,
313+ http_client = self .http_client )
297314 return client .build_auth_request_uri (
298315 response_type = response_type ,
299316 redirect_uri = redirect_uri , state = state , login_hint = login_hint ,
@@ -399,13 +416,12 @@ def _find_msal_accounts(self, environment):
399416
400417 def _get_authority_aliases (self , instance ):
401418 if not self .authority_groups :
402- resp = requests .get (
419+ resp = self . http_client .get (
403420 "https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https://login.microsoftonline.com/common/oauth2/authorize" ,
404- headers = {'Accept' : 'application/json' },
405- verify = self .verify , proxies = self .proxies , timeout = self .timeout )
421+ headers = {'Accept' : 'application/json' })
406422 resp .raise_for_status ()
407423 self .authority_groups = [
408- set (group ['aliases' ]) for group in resp . json ( )['metadata' ]]
424+ set (group ['aliases' ]) for group in json . loads ( resp . text )['metadata' ]]
409425 for group in self .authority_groups :
410426 if instance in group :
411427 return [alias for alias in group if alias != instance ]
@@ -524,7 +540,7 @@ def acquire_token_silent_with_error(
524540 warnings .warn ("We haven't decided how/if this method will accept authority parameter" )
525541 # the_authority = Authority(
526542 # authority,
527- # verify= self.verify, proxies=self.proxies, timeout=self.timeout ,
543+ # self.http_client ,
528544 # ) if authority else self.authority
529545 result = self ._acquire_token_silent_from_cache_and_possibly_refresh_it (
530546 scopes , account , self .authority , force_refresh = force_refresh ,
@@ -536,8 +552,8 @@ def acquire_token_silent_with_error(
536552 for alias in self ._get_authority_aliases (self .authority .instance ):
537553 the_authority = Authority (
538554 "https://" + alias + "/" + self .authority .tenant ,
539- validate_authority = False ,
540- verify = self . verify , proxies = self . proxies , timeout = self . timeout )
555+ self . http_client ,
556+ validate_authority = False )
541557 result = self ._acquire_token_silent_from_cache_and_possibly_refresh_it (
542558 scopes , account , the_authority , force_refresh = force_refresh ,
543559 correlation_id = correlation_id ,
@@ -780,13 +796,11 @@ def acquire_token_by_username_password(
780796
781797 def _acquire_token_by_username_password_federated (
782798 self , user_realm_result , username , password , scopes = None , ** kwargs ):
783- verify = kwargs .pop ("verify" , self .verify )
784- proxies = kwargs .pop ("proxies" , self .proxies )
785799 wstrust_endpoint = {}
786800 if user_realm_result .get ("federation_metadata_url" ):
787801 wstrust_endpoint = mex_send_request (
788802 user_realm_result ["federation_metadata_url" ],
789- verify = verify , proxies = proxies )
803+ self . http_client )
790804 if wstrust_endpoint is None :
791805 raise ValueError ("Unable to find wstrust endpoint from MEX. "
792806 "This typically happens when attempting MSA accounts. "
@@ -798,7 +812,7 @@ def _acquire_token_by_username_password_federated(
798812 wstrust_endpoint .get ("address" ,
799813 # Fallback to an AAD supplied endpoint
800814 user_realm_result .get ("federation_active_auth_url" )),
801- wstrust_endpoint .get ("action" ), verify = verify , proxies = proxies )
815+ wstrust_endpoint .get ("action" ), self . http_client )
802816 if not ("token" in wstrust_result and "type" in wstrust_result ):
803817 raise RuntimeError ("Unsuccessful RSTR. %s" % wstrust_result )
804818 GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
0 commit comments