diff --git a/msal/application.py b/msal/application.py index 98a14601..1aa14c28 100644 --- a/msal/application.py +++ b/msal/application.py @@ -2,6 +2,7 @@ import json import time import logging +import platform import sys import warnings from threading import Lock @@ -815,6 +816,21 @@ def _decide_broker(self, allow_broker, enable_pii_log): and not self.authority.is_adfs and not self.authority._is_b2c ) + if ( + self._enable_broker + and sys.platform == "darwin" + and platform.machine() != "arm64" + ): + # Broker on macOS is supported only on Apple Silicon (arm64). + # Anything else on darwin -- Intel Macs, and an x86_64 Python + # running under Rosetta -- is excluded by product policy, + # regardless of whether a broker is installed on the device. + # This is an allowlist so that an unrecognized architecture + # errs on the side of not using the broker. + self._enable_broker = False + logger.warning( + "Broker on macOS is supported only on Apple Silicon (arm64). " + "We will fallback to non-broker.") if self._enable_broker: try: _init_broker(enable_pii_log) @@ -2167,17 +2183,17 @@ def __init__( 1. You can set any combination of the following opt-in parameters to true: - +--------------------------+-----------------------------------+------------------------------------------------------------------------------------+ - | Opt-in flag | If app will run on | App has registered this as a Desktop platform redirect URI in Azure Portal | - +==========================+===================================+====================================================================================+ - | enable_broker_on_windows | Windows 10+ | ms-appx-web://Microsoft.AAD.BrokerPlugin/your_client_id | - +--------------------------+-----------------------------------+------------------------------------------------------------------------------------+ - | enable_broker_on_wsl | WSL | ms-appx-web://Microsoft.AAD.BrokerPlugin/your_client_id | - +--------------------------+-----------------------------------+------------------------------------------------------------------------------------+ - | enable_broker_on_mac | Mac with Company Portal installed | msauth.com.msauth.unsignedapp://auth | - +--------------------------+-----------------------------------+------------------------------------------------------------------------------------+ - | enable_broker_on_linux | Linux with Intune installed | ``https://login.microsoftonline.com/common/oauth2/nativeclient`` (MUST be enabled) | - +--------------------------+-----------------------------------+------------------------------------------------------------------------------------+ + +--------------------------+-------------------------------------------------+------------------------------------------------------------------------------------+ + | Opt-in flag | If app will run on | App has registered this as a Desktop platform redirect URI in Azure Portal | + +==========================+=================================================+====================================================================================+ + | enable_broker_on_windows | Windows 10+ | ms-appx-web://Microsoft.AAD.BrokerPlugin/your_client_id | + +--------------------------+-------------------------------------------------+------------------------------------------------------------------------------------+ + | enable_broker_on_wsl | WSL | ms-appx-web://Microsoft.AAD.BrokerPlugin/your_client_id | + +--------------------------+-------------------------------------------------+------------------------------------------------------------------------------------+ + | enable_broker_on_mac | Apple Silicon Mac with Company Portal installed | msauth.com.msauth.unsignedapp://auth | + +--------------------------+-------------------------------------------------+------------------------------------------------------------------------------------+ + | enable_broker_on_linux | Linux with Intune installed | ``https://login.microsoftonline.com/common/oauth2/nativeclient`` (MUST be enabled) | + +--------------------------+-------------------------------------------------+------------------------------------------------------------------------------------+ 2. Install broker dependency, e.g. ``pip install msal[broker]>=1.33,<2``. @@ -2214,7 +2230,10 @@ def __init__( New in MSAL Python 1.25.0. :param boolean enable_broker_on_mac: - This setting is only effective if your app is running on Mac. + This setting is only effective if your app is running on + an Apple Silicon (arm64) Mac. + Broker is not supported on Intel-based Macs, where this setting + is ignored and MSAL will fall back to non-broker. This parameter defaults to None, which means MSAL will not utilize a broker. New in MSAL Python 1.31.0. diff --git a/tests/intel_mac_broker_smoke_test.py b/tests/intel_mac_broker_smoke_test.py new file mode 100644 index 00000000..b2560eac --- /dev/null +++ b/tests/intel_mac_broker_smoke_test.py @@ -0,0 +1,72 @@ +"""Manual smoke test for the Intel-Mac broker-disable gate. + +This script is intentionally credential-free and runs in a few seconds. +It exercises the production decision logic in ``ClientApplication._decide_broker`` +on real hardware (no mocks), which is the one thing the unit tests in +``test_application.py::TestBrokerDisabledOnIntelMac`` cannot do — CI runs on +``ubuntu-latest`` only, so ``sys.platform`` is patched there. + +How to run:: + + pip install --force-reinstall "msal[broker]" # or your local checkout + python tests/intel_mac_broker_smoke_test.py + +Expected outcomes: + +* Apple Silicon Mac (``arm64``) with ``pymsalruntime`` installed: + ``_enable_broker`` is ``True``. +* Intel Mac (``x86_64`` / ``i386``), or any other non-``arm64`` machine + (including an ``x86_64`` Python running under Rosetta): + ``_enable_broker`` is ``False`` even though the opt-in was passed and even + if a broker is installed on the device. +* Non-Mac (Windows, Linux): + ``enable_broker_on_mac`` is ignored — ``_enable_broker`` is ``False``. + +The script asserts the expected outcome for the host it runs on and exits +non-zero if the gate misbehaves. +""" +import platform +import sys + +import msal + + +_CLIENT_ID = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" # Azure CLI, public +_AUTHORITY = "https://login.microsoftonline.com/organizations" + + +def _expected_broker_state(): + if sys.platform != "darwin": + return False, "non-Mac platform — enable_broker_on_mac is a no-op" + if platform.machine() != "arm64": + return False, "not Apple Silicon — broker disabled by product policy" + return True, "Apple Silicon Mac — broker should be enabled" + + +def main(): + print(f"sys.platform = {sys.platform!r}") + print(f"platform.machine() = {platform.machine()!r}") + + expected, why = _expected_broker_state() + print(f"Expected _enable_broker = {expected} ({why})") + + app = msal.PublicClientApplication( + _CLIENT_ID, + authority=_AUTHORITY, + enable_broker_on_mac=True, # Only opt in on Mac; this script validates the Mac gate. + ) + actual = bool(app._enable_broker) + print(f"Actual _enable_broker = {actual}") + + if actual != expected: + print( + "FAIL: Intel-Mac gate misbehaved. " + "See ClientApplication._decide_broker in msal/application.py.", + file=sys.stderr, + ) + sys.exit(1) + print("PASS") + + +if __name__ == "__main__": + main() diff --git a/tests/test_application.py b/tests/test_application.py index 6163c5ac..7e5a64dd 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1621,6 +1621,7 @@ def test_client_id_should_be_a_valid_scope(self): @patch("sys.platform", new="darwin") # Pretend running on Mac. +@patch("msal.application.platform.machine", new=Mock(return_value="arm64")) # Pretend Apple Silicon, because broker is not supported on Intel-based Macs. @patch("msal.authority.tenant_discovery", new=Mock(return_value={ "authorization_endpoint": "https://contoso.com/placeholder", "token_endpoint": "https://contoso.com/placeholder", @@ -1663,6 +1664,7 @@ def test_should_fallback_when_pymsalruntime_failed_to_initialize_broker(self): @patch("sys.platform", new="darwin") # Pretend running on Mac. +@patch("msal.application.platform.machine", new=Mock(return_value="arm64")) # Pretend Apple Silicon, because broker is not supported on Intel-based Macs. @patch("msal.authority.tenant_discovery", new=Mock(return_value={ "authorization_endpoint": "https://contoso.com/placeholder", "token_endpoint": "https://contoso.com/placeholder", @@ -1748,6 +1750,54 @@ def test_app_did_not_register_redirect_uri_should_error_out(self): self.assertEqual(result.get("error"), "broker_error") +@patch("sys.platform", new="darwin") # Pretend running on Mac. +@patch("msal.authority.tenant_discovery", new=Mock(return_value={ + "authorization_endpoint": "https://contoso.com/placeholder", + "token_endpoint": "https://contoso.com/placeholder", + "issuer": "https://contoso.com/placeholder", + })) +@patch("msal.application._init_broker", new=Mock()) # Pretend pymsalruntime installed and working +class TestBrokerDisabledOnIntelMac(unittest.TestCase): + """Broker is disabled on Intel-based Macs regardless of opt-in.""" + + @patch("msal.application.platform.machine", new=Mock(return_value="arm64")) + def test_broker_should_be_enabled_on_apple_silicon_mac(self): + app = msal.PublicClientApplication( + "client_id", + authority="https://login.microsoftonline.com/common", + enable_broker_on_mac=True, + ) + self.assertTrue(app._enable_broker) + + @patch("msal.application.platform.machine", new=Mock(return_value="x86_64")) + def test_broker_should_be_disabled_on_x86_64_mac(self): + app = msal.PublicClientApplication( + "client_id", + authority="https://login.microsoftonline.com/common", + enable_broker_on_mac=True, + ) + self.assertFalse(app._enable_broker) + + @patch("msal.application.platform.machine", new=Mock(return_value="i386")) + def test_broker_should_be_disabled_on_i386_mac(self): + app = msal.PublicClientApplication( + "client_id", + authority="https://login.microsoftonline.com/common", + enable_broker_on_mac=True, + ) + self.assertFalse(app._enable_broker) + + @patch("msal.application.platform.machine", new=Mock(return_value="unexpected")) + def test_broker_should_be_disabled_on_unrecognized_machine(self): + """The gate is an allowlist, so an unknown architecture stays broker-free.""" + app = msal.PublicClientApplication( + "client_id", + authority="https://login.microsoftonline.com/common", + enable_broker_on_mac=True, + ) + self.assertFalse(app._enable_broker) + + class MismatchingScopeTestCase(unittest.TestCase): """Test cache behavior when HTTP response scope differs from requested scope"""