Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broker on mac support #38274

Merged
merged 8 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@
"mktime",
"mlindex",
"msal",
"msauth",
"msrest",
"msrestazure",
"MSSQL",
Expand Down
8 changes: 2 additions & 6 deletions sdk/identity/azure-identity-broker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
# Release History

## 1.2.1 (Unreleased)
## 1.3.0b1 (2024-11-05)

### Features Added

### Breaking Changes

### Bugs Fixed

### Other Changes
- Added broker on macOS support.

## 1.2.0 (2024-10-08)

Expand Down
15 changes: 9 additions & 6 deletions sdk/identity/azure-identity-broker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

This package extends the [Azure Identity][azure_identity] library by providing supplemental credentials for authenticating via an authentication broker.

An authentication broker is an application that runs on a user’s machine that manages the authentication handshakes and token maintenance for connected accounts. Currently, only the Windows authentication broker, Web Account Manager (WAM), is supported.
An authentication broker is an application that runs on a user’s machine that manages the authentication handshakes and token maintenance for connected accounts. Currently, only the following brokers are supported:
- Web Account Manager (WAM) on Windows
- Company Portal on macOS

[Source code][source_code] | [Package (PyPI)][azure_identity_broker] | [API reference documentation][ref_docs] | [Microsoft Entra ID documentation][entra_id]

Expand All @@ -28,15 +30,16 @@ When authenticating interactively via `InteractiveBrowserBrokerCredential`, a pa

## Microsoft account (MSA) passthrough

Microsoft accounts (MSA) are personal accounts created by users to access Microsoft services. MSA passthrough is a legacy configuration which enables users to get tokens to resources which normally don't accept MSA logins. This feature is only available to first-party applications. Users authenticating with an application that is configured to use MSA passthrough can set `enable_msa_passthrough` to `True` inside `InteractiveBrowserBrokerCredential` to allow these personal accounts to be listed by WAM.
Microsoft accounts (MSA) are personal accounts created by users to access Microsoft services. MSA passthrough is a legacy configuration which enables users to get tokens to resources which normally don't accept MSA logins. This feature is only available to first-party applications. Users authenticating with an application that is configured to use MSA passthrough can set `enable_msa_passthrough` to `True` inside `InteractiveBrowserBrokerCredential` to allow these personal accounts to be listed by broker.

## Redirect URIs

Microsoft Entra applications rely on redirect URIs to determine where to send the authentication response after a user has logged in. To enable brokered authentication through WAM, a redirect URI matching the following pattern should be registered to the application:
Microsoft Entra applications rely on redirect URIs to determine where to send the authentication response after a user has logged in. To enable brokered authentication through broker, a redirect URI matching the following pattern should be registered to the application:

```
ms-appx-web://Microsoft.AAD.BrokerPlugin/{client_id}
```
* ``ms-appx-web://Microsoft.AAD.BrokerPlugin/your_client_id``
if your app is expected to run on Windows 10+
* ``msauth.com.msauth.unsignedapp://auth``
if your app is expected to run on Mac

## Examples

Expand Down
132 changes: 101 additions & 31 deletions sdk/identity/azure-identity-broker/azure/identity/broker/_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Licensed under the MIT License.
# ------------------------------------
import socket
import sys
from typing import Dict, Any, Mapping, Union
import msal

Expand Down Expand Up @@ -32,8 +33,10 @@ class PopTokenRequestOptions(TokenRequestOptions):
class InteractiveBrowserBrokerCredential(_InteractiveBrowserCredential):
"""Uses an authentication broker to interactively sign in a user.

Currently, only the Windows authentication broker, Web Account Manager (WAM), is supported. Users on macOS and Linux
will be authenticated through a browser.
Currently, only the following brokers are supported:
- Web Account Manager (WAM) on Windows
- Company Portal on macOS
xiangyan99 marked this conversation as resolved.
Show resolved Hide resolved
Users on Linux will be authenticated through the browser.

:func:`~get_token` opens a browser to a login URL provided by Microsoft Entra ID and authenticates a user
there with the authorization code flow, using PKCE (Proof Key for Code Exchange) internally to protect the code.
Expand Down Expand Up @@ -86,48 +89,79 @@ def _request_token(self, *scopes: str, **kwargs: Any) -> Dict:
auth_scheme = msal.PopAuthScheme(
http_method=pop["resource_request_method"], url=pop["resource_request_url"], nonce=pop["nonce"]
)

if self._use_default_broker_account:
if sys.platform.startswith("win"):
if self._use_default_broker_account:
try:
result = app.acquire_token_interactive(
scopes=scopes,
login_hint=self._login_hint,
claims_challenge=claims,
timeout=self._timeout,
prompt=msal.Prompt.NONE,
port=port,
parent_window_handle=self._parent_window_handle,
enable_msa_passthrough=self._enable_msa_passthrough,
auth_scheme=auth_scheme,
)
if "access_token" in result:
return result
except socket.error:
pass
try:
result = app.acquire_token_interactive(
scopes=scopes,
login_hint=self._login_hint,
claims_challenge=claims,
timeout=self._timeout,
prompt="select_account",
port=port,
parent_window_handle=self._parent_window_handle,
enable_msa_passthrough=self._enable_msa_passthrough,
auth_scheme=auth_scheme,
)
except socket.error as ex:
raise CredentialUnavailableError(message="Couldn't start an HTTP server.") from ex
if "access_token" not in result and "error_description" in result:
if within_dac.get():
raise CredentialUnavailableError(message=result["error_description"])
raise ClientAuthenticationError(message=result.get("error_description"))
if "access_token" not in result:
if within_dac.get():
raise CredentialUnavailableError(message="Failed to authenticate user")
raise ClientAuthenticationError(message="Failed to authenticate user")
else:
try:
result = app.acquire_token_interactive(
scopes=scopes,
login_hint=self._login_hint,
claims_challenge=claims,
timeout=self._timeout,
prompt=msal.Prompt.NONE,
prompt="select_account",
port=port,
parent_window_handle=self._parent_window_handle,
enable_msa_passthrough=self._enable_msa_passthrough,
auth_scheme=auth_scheme,
)
except Exception: # pylint: disable=broad-except
app = self._disable_broker_on_app(**kwargs)
result = app.acquire_token_interactive(
scopes=scopes,
login_hint=self._login_hint,
claims_challenge=claims,
timeout=self._timeout,
prompt="select_account",
port=port,
parent_window_handle=self._parent_window_handle,
enable_msa_passthrough=self._enable_msa_passthrough,
)
if "access_token" in result:
return result
except socket.error:
pass
try:
result = app.acquire_token_interactive(
scopes=scopes,
login_hint=self._login_hint,
claims_challenge=claims,
timeout=self._timeout,
prompt="select_account",
port=port,
parent_window_handle=self._parent_window_handle,
enable_msa_passthrough=self._enable_msa_passthrough,
auth_scheme=auth_scheme,
)
except socket.error as ex:
raise CredentialUnavailableError(message="Couldn't start an HTTP server.") from ex
if "access_token" not in result and "error_description" in result:
if within_dac.get():
raise CredentialUnavailableError(message=result["error_description"])
raise ClientAuthenticationError(message=result.get("error_description"))
if "access_token" not in result:
if within_dac.get():
raise CredentialUnavailableError(message="Failed to authenticate user")
raise ClientAuthenticationError(message="Failed to authenticate user")

# base class will raise for other errors
if "error_description" in result:
if within_dac.get():
# pylint: disable=raise-missing-from
raise CredentialUnavailableError(message=result["error_description"])
# pylint: disable=raise-missing-from
raise ClientAuthenticationError(message=result.get("error_description"))
return result

def _get_app(self, **kwargs: Any) -> msal.ClientApplication:
Expand Down Expand Up @@ -160,7 +194,43 @@ def _get_app(self, **kwargs: Any) -> msal.ClientApplication:
http_client=self._client,
instance_discovery=self._instance_discovery,
enable_broker_on_windows=True,
enable_broker_on_mac=True,
enable_pii_log=self._enable_support_logging,
)

return client_applications_map[tenant_id]

def _disable_broker_on_app(self, **kwargs: Any) -> msal.ClientApplication:
tenant_id = resolve_tenant(
self._tenant_id, additionally_allowed_tenants=self._additionally_allowed_tenants, **kwargs
)

client_applications_map = self._client_applications
capabilities = None
token_cache = self._cache

app_class = msal.PublicClientApplication

if kwargs.get("enable_cae"):
client_applications_map = self._cae_client_applications
capabilities = ["CP1"]
token_cache = self._cae_cache

if not token_cache:
token_cache = self._initialize_cache(is_cae=bool(kwargs.get("enable_cae")))

client_applications_map[tenant_id] = app_class(
client_id=self._client_id,
client_credential=self._client_credential,
client_capabilities=capabilities,
authority="{}/{}".format(self._authority, tenant_id),
azure_region=self._regional_authority,
token_cache=token_cache,
http_client=self._client,
instance_discovery=self._instance_discovery,
enable_broker_on_windows=False,
enable_broker_on_mac=False,
enable_pii_log=self._enable_support_logging,
)

return client_applications_map[tenant_id]
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
VERSION = "1.2.1"
VERSION = "1.3.0b1"