Skip to content

Commit 6ead687

Browse files
committed
test: add e2e tests for iwa flow
1 parent f69ddd0 commit 6ead687

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
@@ -188,6 +188,7 @@ class ClientApplication(object):
188188
"You can enable broker by following these instructions. "
189189
"https://msal-python.readthedocs.io/en/latest/#publicclientapplication")
190190

191+
191192
def __init__(
192193
self, client_id,
193194
client_credential=None, authority=None, validate_authority=True,
@@ -1712,11 +1713,10 @@ def _acquire_token_by_username_password_federated(
17121713
wstrust_endpoint.get("action"), self.http_client)
17131714
if not ("token" in wstrust_result and "type" in wstrust_result):
17141715
raise RuntimeError("Unsuccessful RSTR. %s" % wstrust_result)
1715-
GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
17161716
grant_type = {
1717-
SAML_TOKEN_TYPE_V1: GRANT_TYPE_SAML1_1,
1717+
SAML_TOKEN_TYPE_V1: self.client.GRANT_TYPE_SAML1_1,
17181718
SAML_TOKEN_TYPE_V2: self.client.GRANT_TYPE_SAML2,
1719-
WSS_SAML_TOKEN_PROFILE_V1_1: GRANT_TYPE_SAML1_1,
1719+
WSS_SAML_TOKEN_PROFILE_V1_1: self.client.GRANT_TYPE_SAML1_1,
17201720
WSS_SAML_TOKEN_PROFILE_V2: self.client.GRANT_TYPE_SAML2
17211721
}.get(wstrust_result.get("type"))
17221722
if not grant_type:
@@ -2167,11 +2167,10 @@ def _acquire_token_by_iwa_federated(
21672167
wstrust_endpoint.get("action"), self.http_client)
21682168
if not ("token" in wstrust_result and "type" in wstrust_result):
21692169
raise RuntimeError("Unsuccessful RSTR. %s" % wstrust_result)
2170-
GRANT_TYPE_SAML1_1 = 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
21712170
grant_type = {
2172-
SAML_TOKEN_TYPE_V1: GRANT_TYPE_SAML1_1,
2171+
SAML_TOKEN_TYPE_V1: self.client.GRANT_TYPE_SAML1_1,
21732172
SAML_TOKEN_TYPE_V2: self.client.GRANT_TYPE_SAML2,
2174-
WSS_SAML_TOKEN_PROFILE_V1_1: GRANT_TYPE_SAML1_1,
2173+
WSS_SAML_TOKEN_PROFILE_V1_1: self.client.GRANT_TYPE_SAML1_1,
21752174
WSS_SAML_TOKEN_PROFILE_V2: self.client.GRANT_TYPE_SAML2
21762175
}.get(wstrust_result.get("type"))
21772176
if not grant_type:
@@ -2185,6 +2184,7 @@ def _acquire_token_by_iwa_federated(
21852184
event,
21862185
environment=self.authority.instance,
21872186
username=username, # Useful in case IDT contains no such info
2187+
iwa=True
21882188
)),
21892189
**kwargs)
21902190

msal/oauth2cli/oauth2.py

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

msal/token_cache.py

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

215215
with self._lock:
216216
now = int(time.time() if now is None else now)
@@ -243,7 +243,7 @@ def __add(self, event, now=None):
243243
at["refresh_on"] = str(now + refresh_in) # Schema wants a string
244244
self.modify(self.CredentialType.ACCESS_TOKEN, at, at)
245245

246-
if client_info and not event.get("skip_account_creation"):
246+
if (client_info or iwa) and not event.get("skip_account_creation"):
247247
account = {
248248
"home_account_id": home_account_id,
249249
"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
@@ -223,6 +223,29 @@ def _test_username_password(self,
223223
)
224224
return result
225225

226+
def _test_iwa(self,
227+
authority=None, client_id=None, username=None, scope=None,
228+
client_secret=None,
229+
azure_region=None,
230+
http_client=None,
231+
**ignored):
232+
assert authority and client_id and username and scope
233+
self.app = self._build_app(
234+
client_id, authority=authority,
235+
http_client=requests,
236+
azure_region=azure_region, client_credential=client_secret)
237+
self.assertEqual(
238+
self.app.get_accounts(username=username), [], "Cache starts empty")
239+
result = self.app.acquire_token_integrated_windows_auth(
240+
username, scopes=scope)
241+
self.assertLoosely(result)
242+
self.assertCacheWorksForUser(
243+
result, scope,
244+
username=username
245+
)
246+
return result
247+
248+
226249
@unittest.skipIf(
227250
os.getenv("TRAVIS"), # It is set when running on TravisCI or Github Actions
228251
"Although it is doable, we still choose to skip device flow to save time")
@@ -334,6 +357,10 @@ def test_username_password(self):
334357
self.skipUnlessWithConfig(["client_id", "username", "password", "scope"])
335358
self._test_username_password(**self.config)
336359

360+
def test_iwa(self):
361+
self.skipUnlessWithConfig(["client_id","username","scope"])
362+
return self._test_iwa(**self.config)
363+
337364
def _get_app_and_auth_code(self, scopes=None, **kwargs):
338365
return _get_app_and_auth_code(
339366
self.config["client_id"],

0 commit comments

Comments
 (0)