1818
1919
2020# The __init__.py will import this. Not the other way around.
21- __version__ = "0.3.0 "
21+ __version__ = "0.3.1 "
2222
2323logger = logging .getLogger (__name__ )
2424
@@ -104,17 +104,11 @@ def __init__(
104104 # Here the self.authority is not the same type as authority in input
105105 self .token_cache = token_cache or TokenCache ()
106106 self .client = self ._build_client (client_credential , self .authority )
107- self .authority_groups = self ._get_authority_aliases ()
108-
109- def _get_authority_aliases (self ):
110- resp = requests .get (
111- "https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https://login.microsoftonline.com/common/oauth2/authorize" ,
112- headers = {'Accept' : 'application/json' })
113- resp .raise_for_status ()
114- return [set (group ['aliases' ]) for group in resp .json ()['metadata' ]]
107+ self .authority_groups = None
115108
116109 def _build_client (self , client_credential , authority ):
117110 client_assertion = None
111+ client_assertion_type = None
118112 default_body = {"client_info" : 1 }
119113 if isinstance (client_credential , dict ):
120114 assert ("private_key" in client_credential
@@ -124,6 +118,7 @@ def _build_client(self, client_credential, authority):
124118 sha1_thumbprint = client_credential .get ("thumbprint" ))
125119 client_assertion = signer .sign_assertion (
126120 audience = authority .token_endpoint , issuer = self .client_id )
121+ client_assertion_type = Client .CLIENT_ASSERTION_TYPE_JWT
127122 else :
128123 default_body ['client_secret' ] = client_credential
129124 server_configuration = {
@@ -142,6 +137,7 @@ def _build_client(self, client_credential, authority):
142137 },
143138 default_body = default_body ,
144139 client_assertion = client_assertion ,
140+ client_assertion_type = client_assertion_type ,
145141 on_obtaining_tokens = self .token_cache .add ,
146142 on_removing_rt = self .token_cache .remove_rt ,
147143 on_updating_rt = self .token_cache .update_rt ,
@@ -249,13 +245,10 @@ def get_accounts(self, username=None):
249245 """
250246 accounts = self ._find_msal_accounts (environment = self .authority .instance )
251247 if not accounts : # Now try other aliases of this authority instance
252- for group in self .authority_groups :
253- if self .authority .instance in group :
254- for alias in group :
255- if alias != self .authority .instance :
256- accounts = self ._find_msal_accounts (environment = alias )
257- if accounts :
258- break
248+ for alias in self ._get_authority_aliases (self .authority .instance ):
249+ accounts = self ._find_msal_accounts (environment = alias )
250+ if accounts :
251+ break
259252 if username :
260253 # Federated account["username"] from AAD could contain mixed case
261254 lowercase_username = username .lower ()
@@ -274,6 +267,19 @@ def _find_msal_accounts(self, environment):
274267 if a ["authority_type" ] in (
275268 TokenCache .AuthorityType .ADFS , TokenCache .AuthorityType .MSSTS )]
276269
270+ def _get_authority_aliases (self , instance ):
271+ if not self .authority_groups :
272+ resp = requests .get (
273+ "https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https://login.microsoftonline.com/common/oauth2/authorize" ,
274+ headers = {'Accept' : 'application/json' })
275+ resp .raise_for_status ()
276+ self .authority_groups = [
277+ set (group ['aliases' ]) for group in resp .json ()['metadata' ]]
278+ for group in self .authority_groups :
279+ if instance in group :
280+ return [alias for alias in group if alias != instance ]
281+ return []
282+
277283 def acquire_token_silent (
278284 self ,
279285 scopes , # type: List[str]
@@ -309,19 +315,15 @@ def acquire_token_silent(
309315 result = self ._acquire_token_silent (scopes , account , self .authority , ** kwargs )
310316 if result :
311317 return result
312- for group in self .authority_groups :
313- if self .authority .instance in group :
314- for alias in group :
315- if alias != self .authority .instance :
316- the_authority = Authority (
317- "https://" + alias + "/" + self .authority .tenant ,
318- validate_authority = False ,
319- verify = self .verify , proxies = self .proxies ,
320- timeout = self .timeout ,)
321- result = self ._acquire_token_silent (
322- scopes , account , the_authority , ** kwargs )
323- if result :
324- return result
318+ for alias in self ._get_authority_aliases (self .authority .instance ):
319+ the_authority = Authority (
320+ "https://" + alias + "/" + self .authority .tenant ,
321+ validate_authority = False ,
322+ verify = self .verify , proxies = self .proxies , timeout = self .timeout )
323+ result = self ._acquire_token_silent (
324+ scopes , account , the_authority , ** kwargs )
325+ if result :
326+ return result
325327
326328 def _acquire_token_silent (
327329 self ,
@@ -466,6 +468,9 @@ def acquire_token_by_username_password(
466468 self , username , password , scopes = None , ** kwargs ):
467469 """Gets a token for a given resource via user credentails.
468470
471+ See this page for constraints of Username Password Flow.
472+ https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication
473+
469474 :param str username: Typically a UPN in the form of an email address.
470475 :param str password: The password.
471476 :param list[str] scopes:
@@ -494,6 +499,11 @@ def _acquire_token_by_username_password_federated(
494499 wstrust_endpoint = mex_send_request (
495500 user_realm_result ["federation_metadata_url" ],
496501 verify = verify , proxies = proxies )
502+ if wstrust_endpoint is None :
503+ raise ValueError ("Unable to find wstrust endpoint from MEX. "
504+ "This typically happens when attempting MSA accounts. "
505+ "More details available here. "
506+ "https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication" )
497507 logger .debug ("wstrust_endpoint = %s" , wstrust_endpoint )
498508 wstrust_result = wst_send_request (
499509 username , password , user_realm_result .get ("cloud_audience_urn" ),
0 commit comments