def test_login_hint(): expected_username = "******" auth_code_response = {"code": "authorization-code", "state": ["..."]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) msal_acquire_token_result = dict( build_aad_response(access_token="**", id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) mock_msal_app = Mock( acquire_token_by_auth_code_flow=Mock( return_value=msal_acquire_token_result), initiate_auth_code_flow=Mock( return_value={"auth_uri": "http://localhost"}), ) credential = InteractiveBrowserCredential(_server_class=server_class, transport=transport, login_hint=expected_username) with patch("msal.PublicClientApplication", Mock(return_value=mock_msal_app)): with patch(WEBBROWSER_OPEN, lambda _: True): credential.authenticate(scopes=["scope"]) assert mock_msal_app.initiate_auth_code_flow.call_count == 1 _, kwargs = mock_msal_app.initiate_auth_code_flow.call_args assert kwargs["login_hint"] == expected_username
def test_adfs(): """the credential should be able to construct an AuthenticationRecord from an ADFS response returned by MSAL""" authority = "localhost" subject = "subject" tenant = "adfs" username = "******" msal_response = build_aad_response(access_token="***", refresh_token="**") msal_response["id_token_claims"] = id_token_claims( aud="client-id", iss="https://{}/{}".format(authority, tenant), sub=subject, tenant_id=tenant, object_id="object-id", upn=username, ) class TestCredential(InteractiveCredential): def __init__(self, **kwargs): super(TestCredential, self).__init__(client_id="...", **kwargs) def _request_token(self, *_, **__): return msal_response record = TestCredential().authenticate() assert record.authority == authority assert record.home_account_id == subject assert record.tenant_id == tenant assert record.username == username
def test_claims_challenge(): """get_token should pass any claims challenge to MSAL token acquisition APIs""" msal_acquire_token_result = dict( build_aad_response(access_token="**", id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) expected_claims = '{"access_token": {"essential": "true"}' transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) credential = DeviceCodeCredential(transport=transport) with patch.object(DeviceCodeCredential, "_get_app") as get_mock_app: msal_app = get_mock_app() msal_app.initiate_device_flow.return_value = {"message": "it worked"} msal_app.acquire_token_by_device_flow.return_value = msal_acquire_token_result credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_by_device_flow.call_count == 1 args, kwargs = msal_app.acquire_token_by_device_flow.call_args assert kwargs["claims_challenge"] == expected_claims msal_app.get_accounts.return_value = [{ "home_account_id": credential._auth_record.home_account_id }] msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_silent_with_error.call_count == 1 args, kwargs = msal_app.acquire_token_silent_with_error.call_args assert kwargs["claims_challenge"] == expected_claims
def test_claims_challenge(): """get_token should and authenticate pass any claims challenge to MSAL token acquisition APIs""" msal_acquire_token_result = dict( build_aad_response(access_token="**", id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) expected_claims = '{"access_token": {"essential": "true"}' transport = Mock(send=Mock(side_effect=Exception("this test mocks MSAL, so no request should be sent"))) credential = UsernamePasswordCredential("client-id", "username", "password", transport=transport) with patch.object(UsernamePasswordCredential, "_get_app") as get_mock_app: msal_app = get_mock_app() msal_app.acquire_token_by_username_password.return_value = msal_acquire_token_result credential.authenticate(scopes=["scope"], claims=expected_claims) assert msal_app.acquire_token_by_username_password.call_count == 1 args, kwargs = msal_app.acquire_token_by_username_password.call_args assert kwargs["claims_challenge"] == expected_claims credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_by_username_password.call_count == 2 args, kwargs = msal_app.acquire_token_by_username_password.call_args assert kwargs["claims_challenge"] == expected_claims msal_app.get_accounts.return_value = [{"home_account_id": credential._auth_record.home_account_id}] msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_silent_with_error.call_count == 1 args, kwargs = msal_app.acquire_token_silent_with_error.call_args assert kwargs["claims_challenge"] == expected_claims
async def send(request, **_): parsed = urlparse(request.url) tenant_id = parsed.path.split("/")[1] assert tenant_id == default_tenant return mock_response(json_payload=build_aad_response( access_token=expected_token, id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), ))
async def send(request, **_): parsed = urlparse(request.url) tenant_id = parsed.path.split("/")[1] return mock_response(json_payload=build_aad_response( access_token=second_token if tenant_id == second_tenant else first_token, id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), ))
def send(request, **_): parsed = urlparse(request.url) tenant_id = parsed.path.split("/")[1] assert tenant_id in (default_tenant, second_tenant), 'unexpected tenant "{}"'.format(tenant_id) return mock_response( json_payload=build_aad_response( access_token=second_token if tenant_id == second_tenant else first_token, id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), ) )
def request_token(*_, **__): return build_aad_response( access_token=expected_token, id_token_claims=id_token_claims( aud="...", iss="http://localhost/tenant", sub="subject", preferred_username="******", tenant_id="...", object_id="...", ), )
def test_claims_challenge(): """get_token and authenticate should pass any claims challenge to MSAL token acquisition APIs""" expected_claims = '{"access_token": {"essential": "true"}' auth_code_response = {"code": "authorization-code", "state": ["..."]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) msal_acquire_token_result = dict( build_aad_response(access_token="**", id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) credential = InteractiveBrowserCredential(_server_class=server_class, transport=transport) with patch.object(InteractiveBrowserCredential, "_get_app") as get_mock_app: msal_app = get_mock_app() msal_app.initiate_auth_code_flow.return_value = { "auth_uri": "http://localhost" } msal_app.acquire_token_by_auth_code_flow.return_value = msal_acquire_token_result with patch(WEBBROWSER_OPEN, lambda _: True): credential.authenticate(scopes=["scope"], claims=expected_claims) assert msal_app.acquire_token_by_auth_code_flow.call_count == 1 args, kwargs = msal_app.acquire_token_by_auth_code_flow.call_args assert kwargs["claims_challenge"] == expected_claims with patch(WEBBROWSER_OPEN, lambda _: True): credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_by_auth_code_flow.call_count == 2 args, kwargs = msal_app.acquire_token_by_auth_code_flow.call_args assert kwargs["claims_challenge"] == expected_claims msal_app.get_accounts.return_value = [{ "home_account_id": credential._auth_record.home_account_id }] msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_silent_with_error.call_count == 1 args, kwargs = msal_app.acquire_token_silent_with_error.call_args assert kwargs["claims_challenge"] == expected_claims
def send(request, **_): parsed = urlparse(request.url) tenant_id = parsed.path.split("/")[1] if "/oauth2/v2.0/token" not in request.url: return get_discovery_response("https://{}/{}".format(parsed.netloc, tenant_id)) assert tenant_id in (default_tenant, second_tenant), 'unexpected tenant "{}"'.format(tenant_id) return mock_response( json_payload=build_aad_response( access_token=second_token if tenant_id == second_tenant else first_token, id_token_claims=id_token_claims(aud="...", iss="...", sub="..."), ) )
def request_token(*args, **kwargs): tenant_id = kwargs.get("tenant_id") return build_aad_response( access_token=second_token if tenant_id == second_tenant else first_token, id_token_claims=id_token_claims( aud="...", iss="http://localhost/tenant", sub="subject", preferred_username="******", tenant_id="...", object_id="...", ), )
def test_wsl_fallback(uname, is_wsl): """the credential should invoke powershell.exe to open a browser in WSL when webbrowser.open fails""" auth_uri = "http://localhost" expected_access_token = "**" msal_acquire_token_result = dict( build_aad_response(access_token=expected_access_token, id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) msal_app = Mock( initiate_auth_code_flow=Mock(return_value={"auth_uri": auth_uri}), acquire_token_by_auth_code_flow=Mock( return_value=msal_acquire_token_result), ) transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) credential = InteractiveBrowserCredential(_server_class=Mock(), transport=transport) with patch(InteractiveBrowserCredential.__module__ + ".subprocess.call") as subprocess_call: subprocess_call.return_value = 0 with patch(InteractiveBrowserCredential.__module__ + ".platform.uname", lambda: uname): with patch.object(InteractiveBrowserCredential, "_get_app", lambda _: msal_app): with patch(WEBBROWSER_OPEN, lambda _: False): try: token = credential.get_token("scope") except CredentialUnavailableError: assert not is_wsl, "credential should invoke powershell.exe in WSL" return assert is_wsl, "credential should raise CredentialUnavailableError when not in WSL" assert token.token == expected_access_token assert subprocess_call.call_count == 1 args, kwargs = subprocess_call.call_args assert args[0][0] == "powershell.exe" assert auth_uri in args[0][-1] if platform.python_version() >= "3.3": assert "timeout" in kwargs
from six.moves.urllib_parse import urlparse try: from unittest.mock import Mock, patch except ImportError: # python < 3.3 from mock import Mock, patch # type: ignore from helpers import build_aad_response, get_discovery_response, id_token_claims # fake object for tests which need to exercise request_token but don't care about its return value REQUEST_TOKEN_RESULT = build_aad_response( access_token="***", id_token_claims=id_token_claims( aud="...", iss="http://localhost/tenant", sub="subject", preferred_username="******", tenant_id="...", object_id="...", ), ) class MockCredential(InteractiveCredential): """Test class to drive InteractiveCredential. Default instances have an empty in-memory cache, and raise rather than send an HTTP request. """ def __init__(self, client_id="...", request_token=None, transport=None,