diff --git a/msal/__main__.py b/msal/__main__.py index 0c6c59f7..4107fd89 100644 --- a/msal/__main__.py +++ b/msal/__main__.py @@ -300,6 +300,8 @@ def _main(): instance_discovery=instance_discovery, enable_broker_on_windows=enable_broker, enable_broker_on_mac=enable_broker, + enable_broker_on_linux=enable_broker, + enable_broker_on_wsl=enable_broker, enable_pii_log=enable_pii_log, token_cache=global_cache, ) if not is_cca else msal.ConfidentialClientApplication( diff --git a/msal/application.py b/msal/application.py index 31e1105d..a335e3f2 100644 --- a/msal/application.py +++ b/msal/application.py @@ -21,7 +21,7 @@ from .throttled_http_client import ThrottledHttpClient from .cloudshell import _is_running_in_cloud_shell from .sku import SKU, __version__ - +from .oauth2cli.authcode import is_wsl logger = logging.getLogger(__name__) @@ -164,6 +164,8 @@ def _preferred_browser(): pass # We may still proceed return None +def _is_ssh_cert_or_pop_request(token_type, auth_scheme) -> bool: + return token_type == "ssh-cert" or token_type == "pop" or isinstance(auth_scheme, msal.auth_scheme.PopAuthScheme) class _ClientWithCcsRoutingInfo(Client): @@ -710,7 +712,7 @@ def _decide_broker(self, allow_broker, enable_pii_log): def is_pop_supported(self): """Returns True if this client supports Proof-of-Possession Access Token.""" - return self._enable_broker + return self._enable_broker and sys.platform in ("win32", "darwin") def _decorate_scope( self, scopes, @@ -1582,10 +1584,12 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it( raise ValueError("auth_scheme is not supported in Cloud Shell") return self._acquire_token_by_cloud_shell(scopes, data=data) + is_ssh_cert_or_pop_request = _is_ssh_cert_or_pop_request(data.get("token_type"), auth_scheme) + if self._enable_broker and account and account.get("account_source") in ( _GRANT_TYPE_BROKER, # Broker successfully established this account previously. None, # Unknown data from older MSAL. Broker might still work. - ): + ) and (sys.platform in ("win32", "darwin") or not is_ssh_cert_or_pop_request): from .broker import _acquire_token_silently response = _acquire_token_silently( "https://{}/{}".format(self.authority.instance, self.authority.tenant), @@ -1832,7 +1836,7 @@ def acquire_token_by_username_password( """ claims = _merge_claims_challenge_and_capabilities( self._client_capabilities, claims_challenge) - if self._enable_broker: + if self._enable_broker and sys.platform in ("win32", "darwin"): from .broker import _signin_silently response = _signin_silently( "https://{}/{}".format(self.authority.instance, self.authority.tenant), @@ -1929,13 +1933,15 @@ def __init__( *, enable_broker_on_windows=None, enable_broker_on_mac=None, + enable_broker_on_linux=None, + enable_broker_on_wsl=None, **kwargs): """Same as :func:`ClientApplication.__init__`, except that ``client_credential`` parameter shall remain ``None``. .. note:: - You may set enable_broker_on_windows and/or enable_broker_on_mac to True. + You may set enable_broker_on_windows and/or enable_broker_on_mac and/or enable_broker_on_linux and/or enable_broker_on_wsl to True. **What is a broker, and why use it?** @@ -1963,9 +1969,11 @@ def __init__( if your app is expected to run on Windows 10+ * ``msauth.com.msauth.unsignedapp://auth`` if your app is expected to run on Mac + * ``ms-appx-web://Microsoft.AAD.BrokerPlugin/your_client_id`` + if your app is expected to run on Linux, especially WSL 2. installed broker dependency, - e.g. ``pip install msal[broker]>=1.31,<2``. + e.g. ``pip install msal[broker]>=1.33,<2``. 3. tested with ``acquire_token_interactive()`` and ``acquire_token_silent()``. @@ -2003,12 +2011,29 @@ def __init__( This parameter defaults to None, which means MSAL will not utilize a broker. New in MSAL Python 1.31.0. + + :param boolean enable_broker_on_linux: + This setting is only effective if your app is running on Linux, including WSL. + This parameter defaults to None, which means MSAL will not utilize a broker. + + New in MSAL Python 1.33.0. + + :param boolean enable_broker_on_wsl: + This setting is only effective if your app is running on WSL. + This parameter defaults to None, which means MSAL will not utilize a broker. + + New in MSAL Python 1.33.0. """ if client_credential is not None: raise ValueError("Public Client should not possess credentials") + self._enable_broker = bool( enable_broker_on_windows and sys.platform == "win32" - or enable_broker_on_mac and sys.platform == "darwin") + or enable_broker_on_mac and sys.platform == "darwin" + or enable_broker_on_linux and sys.platform == "linux" + or enable_broker_on_wsl and is_wsl() + ) + super(PublicClientApplication, self).__init__( client_id, client_credential=None, **kwargs) @@ -2137,6 +2162,8 @@ def acquire_token_interactive( False ) and data.get("token_type") != "ssh-cert" # Work around a known issue as of PyMsalRuntime 0.8 self._validate_ssh_cert_input_data(data) + is_ssh_cert_or_pop_request = _is_ssh_cert_or_pop_request(data.get("token_type"), auth_scheme) + if not on_before_launching_ui: on_before_launching_ui = lambda **kwargs: None if _is_running_in_cloud_shell() and prompt == "none": @@ -2145,7 +2172,7 @@ def acquire_token_interactive( return self._acquire_token_by_cloud_shell(scopes, data=data) claims = _merge_claims_challenge_and_capabilities( self._client_capabilities, claims_challenge) - if self._enable_broker: + if self._enable_broker and (sys.platform in ("win32", "darwin") or not is_ssh_cert_or_pop_request): if parent_window_handle is None: raise ValueError( "parent_window_handle is required when you opted into using broker. " @@ -2170,7 +2197,9 @@ def acquire_token_interactive( ) return self._process_broker_response(response, scopes, data) - if auth_scheme: + if isinstance(auth_scheme, msal.auth_scheme.PopAuthScheme) and sys.platform == "linux": + raise ValueError("POP is not supported on Linux") + elif auth_scheme: raise ValueError(self._AUTH_SCHEME_UNSUPPORTED) on_before_launching_ui(ui="browser") telemetry_context = self._build_telemetry_context( diff --git a/msal/broker.py b/msal/broker.py index f4d71e11..1e794b36 100644 --- a/msal/broker.py +++ b/msal/broker.py @@ -27,6 +27,7 @@ min_ver = { "win32": "1.20", "darwin": "1.31", + "linux": "1.33", }.get(sys.platform) if min_ver: raise ImportError( diff --git a/sample/interactive_sample.py b/sample/interactive_sample.py index 8c3f2df9..11f823f0 100644 --- a/sample/interactive_sample.py +++ b/sample/interactive_sample.py @@ -47,7 +47,8 @@ oidc_authority=os.getenv('OIDC_AUTHORITY'), # For External ID with custom domain #enable_broker_on_windows=True, # Opted in. You will be guided to meet the prerequisites, if your app hasn't already #enable_broker_on_mac=True, # Opted in. You will be guided to meet the prerequisites, if your app hasn't already - + #enable_broker_on_linux=True, # Opted in. You will be guided to meet the prerequisites, if your app hasn't already + #enable_broker_on_wsl=True, # Opted in. You will be guided to meet the prerequisites, if your app hasn't already token_cache=global_token_cache, # Let this app (re)use an existing token cache. # If absent, ClientApplication will create its own empty token cache ) diff --git a/setup.cfg b/setup.cfg index 6dfcfc7b..16f7cca3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -62,9 +62,11 @@ broker = # most existing MSAL Python apps do not have the redirect_uri needed by broker. # # We need pymsalruntime.CallbackData introduced in PyMsalRuntime 0.14 - pymsalruntime>=0.14,<0.18; python_version>='3.6' and platform_system=='Windows' + pymsalruntime>=0.14,<0.19; python_version>='3.6' and platform_system=='Windows' # On Mac, PyMsalRuntime 0.17+ is expected to support SSH cert and ROPC - pymsalruntime>=0.17,<0.18; python_version>='3.8' and platform_system=='Darwin' + pymsalruntime>=0.17,<0.19; python_version>='3.8' and platform_system=='Darwin' + # PyMsalRuntime 0.18+ is expected to support broker on Linux + pymsalruntime>=0.18,<0.19; python_version>='3.8' and platform_system=='Linux' [options.packages.find] exclude = diff --git a/tests/broker-test.py b/tests/broker-test.py index cdcc4817..fa4d916c 100644 --- a/tests/broker-test.py +++ b/tests/broker-test.py @@ -39,7 +39,10 @@ _AZURE_CLI, authority="https://login.microsoftonline.com/organizations", enable_broker_on_mac=True, - enable_broker_on_windows=True) + enable_broker_on_windows=True, + enable_broker_on_linux=True, + enable_broker_on_wsl=True, + ) def interactive_and_silent(scopes, auth_scheme, data, expected_token_type): print("An account picker shall be pop up, possibly behind this console. Continue from there.") diff --git a/tests/test_e2e.py b/tests/test_e2e.py index 28c73abc..dde26f0d 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -193,6 +193,8 @@ def _build_app(cls, http_client=http_client or MinimalHttpClient(), enable_broker_on_windows=_PYMSALRUNTIME_INSTALLED, enable_broker_on_mac=_PYMSALRUNTIME_INSTALLED, + enable_broker_on_linux=_PYMSALRUNTIME_INSTALLED, + enable_broker_on_wsl=_PYMSALRUNTIME_INSTALLED, ) def _test_username_password(self,