2121
2222
2323# The __init__.py will import this. Not the other way around.
24- __version__ = "1.3 .0"
24+ __version__ = "1.4 .0"
2525
2626logger = logging .getLogger (__name__ )
2727
@@ -198,8 +198,9 @@ def __init__(
198198 authority or "https://login.microsoftonline.com/common/" ,
199199 self .http_client , validate_authority = validate_authority )
200200 # Here the self.authority is not the same type as authority in input
201+ self .client = None
201202 self .token_cache = token_cache or TokenCache ()
202- self .client = self . _build_client ( client_credential , self . authority )
203+ self ._client_credential = client_credential
203204 self .authority_groups = None
204205
205206 def _build_client (self , client_credential , authority ):
@@ -248,6 +249,12 @@ def _build_client(self, client_credential, authority):
248249 on_removing_rt = self .token_cache .remove_rt ,
249250 on_updating_rt = self .token_cache .update_rt )
250251
252+ def _get_client (self ):
253+ if not self .client :
254+ self .authority .initialize ()
255+ self .client = self ._build_client (self ._client_credential , self .authority )
256+ return self .client
257+
251258 def get_authorization_request_url (
252259 self ,
253260 scopes , # type: list[str]
@@ -284,8 +291,9 @@ def get_authorization_request_url(
284291 Can be one of "consumers" or "organizations" or your tenant domain "contoso.com".
285292 If included, it will skip the email-based discovery process that user goes
286293 through on the sign-in page, leading to a slightly more streamlined user experience.
287- https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
288- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8
294+ More information on possible values
295+ `here <https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code>`_ and
296+ `here <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oapx/86fb452d-e34a-494e-ac61-e526e263b6d8>`_.
289297 :return: The authorization url as a string.
290298 """
291299 """ # TBD: this would only be meaningful in a new acquire_token_interactive()
@@ -306,6 +314,7 @@ def get_authorization_request_url(
306314 authority ,
307315 self .http_client
308316 ) if authority else self .authority
317+ the_authority .initialize ()
309318
310319 client = Client (
311320 {"authorization_endpoint" : the_authority .authorization_endpoint },
@@ -366,7 +375,7 @@ def acquire_token_by_authorization_code(
366375 # really empty.
367376 assert isinstance (scopes , list ), "Invalid parameter type"
368377 self ._validate_ssh_cert_input_data (kwargs .get ("data" , {}))
369- return self .client .obtain_token_by_authorization_code (
378+ return self ._get_client () .obtain_token_by_authorization_code (
370379 code , redirect_uri = redirect_uri ,
371380 scope = decorate_scope (scopes , self .client_id ),
372381 headers = {
@@ -390,6 +399,7 @@ def get_accounts(self, username=None):
390399 Your app can choose to display those information to end user,
391400 and allow user to choose one of his/her accounts to proceed.
392401 """
402+ self .authority .initialize ()
393403 accounts = self ._find_msal_accounts (environment = self .authority .instance )
394404 if not accounts : # Now try other aliases of this authority instance
395405 for alias in self ._get_authority_aliases (self .authority .instance ):
@@ -542,6 +552,7 @@ def acquire_token_silent_with_error(
542552 # authority,
543553 # self.http_client,
544554 # ) if authority else self.authority
555+ self .authority .initialize ()
545556 result = self ._acquire_token_silent_from_cache_and_possibly_refresh_it (
546557 scopes , account , self .authority , force_refresh = force_refresh ,
547558 correlation_id = correlation_id ,
@@ -554,6 +565,7 @@ def acquire_token_silent_with_error(
554565 "https://" + alias + "/" + self .authority .tenant ,
555566 self .http_client ,
556567 validate_authority = False )
568+ the_authority .initialize ()
557569 result = self ._acquire_token_silent_from_cache_and_possibly_refresh_it (
558570 scopes , account , the_authority , force_refresh = force_refresh ,
559571 correlation_id = correlation_id ,
@@ -723,11 +735,12 @@ def acquire_token_by_refresh_token(self, refresh_token, scopes):
723735 * A dict contains "error" and some other keys, when error happened.
724736 * A dict contains no "error" key means migration was successful.
725737 """
726- return self .client .obtain_token_by_refresh_token (
738+ return self ._get_client () .obtain_token_by_refresh_token (
727739 refresh_token ,
728- decorate_scope (scopes , self .client_id ),
740+ scope = decorate_scope (scopes , self .client_id ),
729741 rt_getter = lambda rt : rt ,
730742 on_updating_rt = False ,
743+ on_removing_rt = lambda rt_item : None , # No OP
731744 )
732745
733746
@@ -753,7 +766,7 @@ def initiate_device_flow(self, scopes=None, **kwargs):
753766 - an error response would contain some other readable key/value pairs.
754767 """
755768 correlation_id = _get_new_correlation_id ()
756- flow = self .client .initiate_device_flow (
769+ flow = self ._get_client () .initiate_device_flow (
757770 scope = decorate_scope (scopes or [], self .client_id ),
758771 headers = {
759772 CLIENT_REQUEST_ID : correlation_id ,
@@ -777,7 +790,7 @@ def acquire_token_by_device_flow(self, flow, **kwargs):
777790 - A successful response would contain "access_token" key,
778791 - an error response would contain "error" and usually "error_description".
779792 """
780- return self .client .obtain_token_by_device_flow (
793+ return self ._get_client () .obtain_token_by_device_flow (
781794 flow ,
782795 data = dict (kwargs .pop ("data" , {}), code = flow ["device_code" ]),
783796 # 2018-10-4 Hack:
@@ -814,14 +827,15 @@ def acquire_token_by_username_password(
814827 CLIENT_CURRENT_TELEMETRY : _build_current_telemetry_request_header (
815828 self .ACQUIRE_TOKEN_BY_USERNAME_PASSWORD_ID ),
816829 }
830+ self .authority .initialize ()
817831 if not self .authority .is_adfs :
818832 user_realm_result = self .authority .user_realm_discovery (
819833 username , correlation_id = headers [CLIENT_REQUEST_ID ])
820834 if user_realm_result .get ("account_type" ) == "Federated" :
821835 return self ._acquire_token_by_username_password_federated (
822836 user_realm_result , username , password , scopes = scopes ,
823837 headers = headers , ** kwargs )
824- return self .client .obtain_token_by_username_password (
838+ return self ._get_client () .obtain_token_by_username_password (
825839 username , password , scope = scopes ,
826840 headers = headers ,
827841 ** kwargs )
@@ -850,16 +864,16 @@ def _acquire_token_by_username_password_federated(
850864 GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
851865 grant_type = {
852866 SAML_TOKEN_TYPE_V1 : GRANT_TYPE_SAML1_1 ,
853- SAML_TOKEN_TYPE_V2 : self . client .GRANT_TYPE_SAML2 ,
867+ SAML_TOKEN_TYPE_V2 : Client .GRANT_TYPE_SAML2 ,
854868 WSS_SAML_TOKEN_PROFILE_V1_1 : GRANT_TYPE_SAML1_1 ,
855- WSS_SAML_TOKEN_PROFILE_V2 : self . client .GRANT_TYPE_SAML2
869+ WSS_SAML_TOKEN_PROFILE_V2 : Client .GRANT_TYPE_SAML2
856870 }.get (wstrust_result .get ("type" ))
857871 if not grant_type :
858872 raise RuntimeError (
859873 "RSTR returned unknown token type: %s" , wstrust_result .get ("type" ))
860- self . client .grant_assertion_encoders .setdefault ( # Register a non-standard type
861- grant_type , self . client .encode_saml_assertion )
862- return self .client .obtain_token_by_assertion (
874+ Client .grant_assertion_encoders .setdefault ( # Register a non-standard type
875+ grant_type , Client .encode_saml_assertion )
876+ return self ._get_client () .obtain_token_by_assertion (
863877 wstrust_result ["token" ], grant_type , scope = scopes , ** kwargs )
864878
865879
@@ -877,7 +891,7 @@ def acquire_token_for_client(self, scopes, **kwargs):
877891 - an error response would contain "error" and usually "error_description".
878892 """
879893 # TBD: force_refresh behavior
880- return self .client .obtain_token_for_client (
894+ return self ._get_client () .obtain_token_for_client (
881895 scope = scopes , # This grant flow requires no scope decoration
882896 headers = {
883897 CLIENT_REQUEST_ID : _get_new_correlation_id (),
@@ -909,9 +923,9 @@ def acquire_token_on_behalf_of(self, user_assertion, scopes, **kwargs):
909923 """
910924 # The implementation is NOT based on Token Exchange
911925 # https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16
912- return self .client .obtain_token_by_assertion ( # bases on assertion RFC 7521
926+ return self ._get_client () .obtain_token_by_assertion ( # bases on assertion RFC 7521
913927 user_assertion ,
914- self . client .GRANT_TYPE_JWT , # IDTs and AAD ATs are all JWTs
928+ Client .GRANT_TYPE_JWT , # IDTs and AAD ATs are all JWTs
915929 scope = decorate_scope (scopes , self .client_id ), # Decoration is used for:
916930 # 1. Explicitly requesting an RT, without relying on AAD default
917931 # behavior, even though it currently still issues an RT.
0 commit comments