21
21
22
22
23
23
# The __init__.py will import this. Not the other way around.
24
- __version__ = "1.3 .0"
24
+ __version__ = "1.4 .0"
25
25
26
26
logger = logging .getLogger (__name__ )
27
27
@@ -198,8 +198,9 @@ def __init__(
198
198
authority or "https://login.microsoftonline.com/common/" ,
199
199
self .http_client , validate_authority = validate_authority )
200
200
# Here the self.authority is not the same type as authority in input
201
+ self .client = None
201
202
self .token_cache = token_cache or TokenCache ()
202
- self .client = self . _build_client ( client_credential , self . authority )
203
+ self ._client_credential = client_credential
203
204
self .authority_groups = None
204
205
205
206
def _build_client (self , client_credential , authority ):
@@ -248,6 +249,12 @@ def _build_client(self, client_credential, authority):
248
249
on_removing_rt = self .token_cache .remove_rt ,
249
250
on_updating_rt = self .token_cache .update_rt )
250
251
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
+
251
258
def get_authorization_request_url (
252
259
self ,
253
260
scopes , # type: list[str]
@@ -284,8 +291,9 @@ def get_authorization_request_url(
284
291
Can be one of "consumers" or "organizations" or your tenant domain "contoso.com".
285
292
If included, it will skip the email-based discovery process that user goes
286
293
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>`_.
289
297
:return: The authorization url as a string.
290
298
"""
291
299
""" # TBD: this would only be meaningful in a new acquire_token_interactive()
@@ -306,6 +314,7 @@ def get_authorization_request_url(
306
314
authority ,
307
315
self .http_client
308
316
) if authority else self .authority
317
+ the_authority .initialize ()
309
318
310
319
client = Client (
311
320
{"authorization_endpoint" : the_authority .authorization_endpoint },
@@ -366,7 +375,7 @@ def acquire_token_by_authorization_code(
366
375
# really empty.
367
376
assert isinstance (scopes , list ), "Invalid parameter type"
368
377
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 (
370
379
code , redirect_uri = redirect_uri ,
371
380
scope = decorate_scope (scopes , self .client_id ),
372
381
headers = {
@@ -390,6 +399,7 @@ def get_accounts(self, username=None):
390
399
Your app can choose to display those information to end user,
391
400
and allow user to choose one of his/her accounts to proceed.
392
401
"""
402
+ self .authority .initialize ()
393
403
accounts = self ._find_msal_accounts (environment = self .authority .instance )
394
404
if not accounts : # Now try other aliases of this authority instance
395
405
for alias in self ._get_authority_aliases (self .authority .instance ):
@@ -542,6 +552,7 @@ def acquire_token_silent_with_error(
542
552
# authority,
543
553
# self.http_client,
544
554
# ) if authority else self.authority
555
+ self .authority .initialize ()
545
556
result = self ._acquire_token_silent_from_cache_and_possibly_refresh_it (
546
557
scopes , account , self .authority , force_refresh = force_refresh ,
547
558
correlation_id = correlation_id ,
@@ -554,6 +565,7 @@ def acquire_token_silent_with_error(
554
565
"https://" + alias + "/" + self .authority .tenant ,
555
566
self .http_client ,
556
567
validate_authority = False )
568
+ the_authority .initialize ()
557
569
result = self ._acquire_token_silent_from_cache_and_possibly_refresh_it (
558
570
scopes , account , the_authority , force_refresh = force_refresh ,
559
571
correlation_id = correlation_id ,
@@ -723,11 +735,12 @@ def acquire_token_by_refresh_token(self, refresh_token, scopes):
723
735
* A dict contains "error" and some other keys, when error happened.
724
736
* A dict contains no "error" key means migration was successful.
725
737
"""
726
- return self .client .obtain_token_by_refresh_token (
738
+ return self ._get_client () .obtain_token_by_refresh_token (
727
739
refresh_token ,
728
- decorate_scope (scopes , self .client_id ),
740
+ scope = decorate_scope (scopes , self .client_id ),
729
741
rt_getter = lambda rt : rt ,
730
742
on_updating_rt = False ,
743
+ on_removing_rt = lambda rt_item : None , # No OP
731
744
)
732
745
733
746
@@ -753,7 +766,7 @@ def initiate_device_flow(self, scopes=None, **kwargs):
753
766
- an error response would contain some other readable key/value pairs.
754
767
"""
755
768
correlation_id = _get_new_correlation_id ()
756
- flow = self .client .initiate_device_flow (
769
+ flow = self ._get_client () .initiate_device_flow (
757
770
scope = decorate_scope (scopes or [], self .client_id ),
758
771
headers = {
759
772
CLIENT_REQUEST_ID : correlation_id ,
@@ -777,7 +790,7 @@ def acquire_token_by_device_flow(self, flow, **kwargs):
777
790
- A successful response would contain "access_token" key,
778
791
- an error response would contain "error" and usually "error_description".
779
792
"""
780
- return self .client .obtain_token_by_device_flow (
793
+ return self ._get_client () .obtain_token_by_device_flow (
781
794
flow ,
782
795
data = dict (kwargs .pop ("data" , {}), code = flow ["device_code" ]),
783
796
# 2018-10-4 Hack:
@@ -814,14 +827,15 @@ def acquire_token_by_username_password(
814
827
CLIENT_CURRENT_TELEMETRY : _build_current_telemetry_request_header (
815
828
self .ACQUIRE_TOKEN_BY_USERNAME_PASSWORD_ID ),
816
829
}
830
+ self .authority .initialize ()
817
831
if not self .authority .is_adfs :
818
832
user_realm_result = self .authority .user_realm_discovery (
819
833
username , correlation_id = headers [CLIENT_REQUEST_ID ])
820
834
if user_realm_result .get ("account_type" ) == "Federated" :
821
835
return self ._acquire_token_by_username_password_federated (
822
836
user_realm_result , username , password , scopes = scopes ,
823
837
headers = headers , ** kwargs )
824
- return self .client .obtain_token_by_username_password (
838
+ return self ._get_client () .obtain_token_by_username_password (
825
839
username , password , scope = scopes ,
826
840
headers = headers ,
827
841
** kwargs )
@@ -850,16 +864,16 @@ def _acquire_token_by_username_password_federated(
850
864
GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
851
865
grant_type = {
852
866
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 ,
854
868
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
856
870
}.get (wstrust_result .get ("type" ))
857
871
if not grant_type :
858
872
raise RuntimeError (
859
873
"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 (
863
877
wstrust_result ["token" ], grant_type , scope = scopes , ** kwargs )
864
878
865
879
@@ -877,7 +891,7 @@ def acquire_token_for_client(self, scopes, **kwargs):
877
891
- an error response would contain "error" and usually "error_description".
878
892
"""
879
893
# TBD: force_refresh behavior
880
- return self .client .obtain_token_for_client (
894
+ return self ._get_client () .obtain_token_for_client (
881
895
scope = scopes , # This grant flow requires no scope decoration
882
896
headers = {
883
897
CLIENT_REQUEST_ID : _get_new_correlation_id (),
@@ -909,9 +923,9 @@ def acquire_token_on_behalf_of(self, user_assertion, scopes, **kwargs):
909
923
"""
910
924
# The implementation is NOT based on Token Exchange
911
925
# 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
913
927
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
915
929
scope = decorate_scope (scopes , self .client_id ), # Decoration is used for:
916
930
# 1. Explicitly requesting an RT, without relying on AAD default
917
931
# behavior, even though it currently still issues an RT.
0 commit comments