Skip to content

Commit 097a1ac

Browse files
committed
test: add e2e tests for iwa flow
1 parent f2ad4d1 commit 097a1ac

File tree

5 files changed

+39
-10
lines changed

5 files changed

+39
-10
lines changed

msal/application.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ class ClientApplication(object):
239239
"You can enable broker by following these instructions. "
240240
"https://msal-python.readthedocs.io/en/latest/#publicclientapplication")
241241

242+
242243
def __init__(
243244
self, client_id,
244245
client_credential=None, authority=None, validate_authority=True,
@@ -1889,11 +1890,10 @@ def _acquire_token_by_username_password_federated(
18891890
wstrust_endpoint.get("action"), self.http_client)
18901891
if not ("token" in wstrust_result and "type" in wstrust_result):
18911892
raise RuntimeError("Unsuccessful RSTR. %s" % wstrust_result)
1892-
GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
18931893
grant_type = {
1894-
SAML_TOKEN_TYPE_V1: GRANT_TYPE_SAML1_1,
1894+
SAML_TOKEN_TYPE_V1: self.client.GRANT_TYPE_SAML1_1,
18951895
SAML_TOKEN_TYPE_V2: self.client.GRANT_TYPE_SAML2,
1896-
WSS_SAML_TOKEN_PROFILE_V1_1: GRANT_TYPE_SAML1_1,
1896+
WSS_SAML_TOKEN_PROFILE_V1_1: self.client.GRANT_TYPE_SAML1_1,
18971897
WSS_SAML_TOKEN_PROFILE_V2: self.client.GRANT_TYPE_SAML2
18981898
}.get(wstrust_result.get("type"))
18991899
if not grant_type:
@@ -2387,11 +2387,10 @@ def _acquire_token_by_iwa_federated(
23872387
wstrust_endpoint.get("action"), self.http_client)
23882388
if not ("token" in wstrust_result and "type" in wstrust_result):
23892389
raise RuntimeError("Unsuccessful RSTR. %s" % wstrust_result)
2390-
GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
23912390
grant_type = {
2392-
SAML_TOKEN_TYPE_V1: GRANT_TYPE_SAML1_1,
2391+
SAML_TOKEN_TYPE_V1: self.client.GRANT_TYPE_SAML1_1,
23932392
SAML_TOKEN_TYPE_V2: self.client.GRANT_TYPE_SAML2,
2394-
WSS_SAML_TOKEN_PROFILE_V1_1: GRANT_TYPE_SAML1_1,
2393+
WSS_SAML_TOKEN_PROFILE_V1_1: self.client.GRANT_TYPE_SAML1_1,
23952394
WSS_SAML_TOKEN_PROFILE_V2: self.client.GRANT_TYPE_SAML2
23962395
}.get(wstrust_result.get("type"))
23972396
if not grant_type:
@@ -2405,6 +2404,7 @@ def _acquire_token_by_iwa_federated(
24052404
event,
24062405
environment=self.authority.instance,
24072406
username=username, # Useful in case IDT contains no such info
2407+
iwa=True
24082408
)),
24092409
**kwargs)
24102410

msal/oauth2cli/oauth2.py

+1
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ class Client(BaseClient): # We choose to implement all 4 grants in 1 class
300300
"DEVICE_CODE": "device_code",
301301
}
302302
DEVICE_FLOW_RETRIABLE_ERRORS = ("authorization_pending", "slow_down")
303+
GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
303304
GRANT_TYPE_SAML2 = "urn:ietf:params:oauth:grant-type:saml2-bearer" # RFC7522
304305
GRANT_TYPE_JWT = "urn:ietf:params:oauth:grant-type:jwt-bearer" # RFC7523
305306
grant_assertion_encoders = {GRANT_TYPE_SAML2: BaseClient.encode_saml_assertion}

msal/token_cache.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ def __add(self, event, now=None):
240240
# Only use decode_id_token() when necessary, it contains time-sensitive validation
241241
decode_id_token(id_token, client_id=event["client_id"]) if id_token else {})
242242
client_info, home_account_id = self.__parse_account(response, id_token_claims)
243-
244243
target = ' '.join(sorted(event.get("scope") or [])) # Schema should have required sorting
244+
iwa = event.get("iwa", False) # Integrated Windows Authentication
245245

246246
with self._lock:
247247
now = int(time.time() if now is None else now)
@@ -277,7 +277,7 @@ def __add(self, event, now=None):
277277
at["refresh_on"] = str(now + refresh_in) # Schema wants a string
278278
self.modify(self.CredentialType.ACCESS_TOKEN, at, at)
279279

280-
if client_info and not event.get("skip_account_creation"):
280+
if (client_info or iwa) and not event.get("skip_account_creation"):
281281
account = {
282282
"home_account_id": home_account_id,
283283
"environment": environment,

sample/integrated_windows_authentication_sample.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import requests
2727
import msal
28+
from msal.token_cache import TokenCache
2829

2930

3031
# Optional logging
@@ -54,11 +55,11 @@ def acquire_and_use_token():
5455
# Firstly, check the cache to see if this end user has signed in before
5556
accounts = global_app.get_accounts(username=config["username"])
5657
if accounts:
57-
logging.info("Account(s) exists in cache, probably with token too. Let's try.")
58+
print("Account(s) exists in cache, probably with token too. Let's try.")
5859
result = global_app.acquire_token_silent(config["scope"], account=accounts[0])
5960

6061
if not result:
61-
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
62+
print("No suitable token exists in cache. Let's get a new one from AAD.")
6263
# See this page for constraints of Username Password Flow.
6364
# https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication
6465
result = global_app.acquire_token_integrated_windows_auth(

tests/test_e2e.py

+27
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,29 @@ def _test_username_password(self,
230230
)
231231
return result
232232

233+
def _test_iwa(self,
234+
authority=None, client_id=None, username=None, scope=None,
235+
client_secret=None,
236+
azure_region=None,
237+
http_client=None,
238+
**ignored):
239+
assert authority and client_id and username and scope
240+
self.app = self._build_app(
241+
client_id, authority=authority,
242+
http_client=requests,
243+
azure_region=azure_region, client_credential=client_secret)
244+
self.assertEqual(
245+
self.app.get_accounts(username=username), [], "Cache starts empty")
246+
result = self.app.acquire_token_integrated_windows_auth(
247+
username, scopes=scope)
248+
self.assertLoosely(result)
249+
self.assertCacheWorksForUser(
250+
result, scope,
251+
username=username
252+
)
253+
return result
254+
255+
233256
@unittest.skipIf(
234257
os.getenv("TRAVIS"), # It is set when running on TravisCI or Github Actions
235258
"Although it is doable, we still choose to skip device flow to save time")
@@ -348,6 +371,10 @@ def test_username_password(self):
348371
self.skipUnlessWithConfig(["client_id", "username", "password", "scope"])
349372
self._test_username_password(**self.config)
350373

374+
def test_iwa(self):
375+
self.skipUnlessWithConfig(["client_id","username","scope"])
376+
return self._test_iwa(**self.config)
377+
351378
def _get_app_and_auth_code(self, scopes=None, **kwargs):
352379
return _get_app_and_auth_code(
353380
self.config["client_id"],

0 commit comments

Comments
 (0)