-
Notifications
You must be signed in to change notification settings - Fork 205
acquire_token_silent() shall not invoke broker if the account was not established by broker #569
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ | |
from .mex import send_request as mex_send_request | ||
from .wstrust_request import send_request as wst_send_request | ||
from .wstrust_response import * | ||
from .token_cache import TokenCache, _get_username | ||
from .token_cache import TokenCache, _get_username, _GRANT_TYPE_BROKER | ||
import msal.telemetry | ||
from .region import _detect_region | ||
from .throttled_http_client import ThrottledHttpClient | ||
|
@@ -1104,6 +1104,7 @@ def _find_msal_accounts(self, environment): | |
"home_account_id": a.get("home_account_id"), | ||
"environment": a.get("environment"), | ||
"username": a.get("username"), | ||
"account_source": a.get("account_source"), | ||
|
||
# The following fields for backward compatibility, for now | ||
"authority_type": a.get("authority_type"), | ||
|
@@ -1398,7 +1399,10 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it( | |
if account and account.get("authority_type") == _AUTHORITY_TYPE_CLOUDSHELL: | ||
return self._acquire_token_by_cloud_shell(scopes, data=data) | ||
|
||
if self._enable_broker and account is not None: | ||
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. | ||
): | ||
from .broker import _acquire_token_silently | ||
response = _acquire_token_silently( | ||
"https://{}/{}".format(self.authority.instance, self.authority.tenant), | ||
|
@@ -1409,8 +1413,12 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it( | |
self._client_capabilities, claims_challenge), | ||
correlation_id=correlation_id, | ||
**data) | ||
if response: # The broker provided a decisive outcome, so we use it | ||
return self._process_broker_response(response, scopes, data) | ||
if response: # Broker provides a decisive outcome | ||
account_was_established_by_broker = account.get( | ||
"account_source") == _GRANT_TYPE_BROKER | ||
broker_attempt_succeeded_just_now = "error" not in response | ||
if account_was_established_by_broker or broker_attempt_succeeded_just_now: | ||
return self._process_broker_response(response, scopes, data) | ||
|
||
if account: | ||
result = self._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family( | ||
|
@@ -1441,6 +1449,8 @@ def _process_broker_response(self, response, scopes, data): | |
response=response, | ||
data=data, | ||
_account_id=response["_account_id"], | ||
environment=self.authority.instance, # Be consistent with non-broker flows | ||
grant_type=_GRANT_TYPE_BROKER, # A pseudo grant type for TokenCache to mark account_source as broker | ||
)) | ||
response[self._TOKEN_SOURCE] = self._TOKEN_SOURCE_BROKER | ||
return _clean_up(response) | ||
|
@@ -1628,7 +1638,7 @@ def acquire_token_by_username_password( | |
""" | ||
claims = _merge_claims_challenge_and_capabilities( | ||
self._client_capabilities, claims_challenge) | ||
if self._enable_broker: | ||
if False: # Disabled, for now. It was if self._enable_broker: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI. In v1, we also expose broker for InteractiveBrowserCred, there is no support for username password for broker in Python identity too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI v2: We reverted course again, back to the original. CC: @jiasli Note: New comments, if any, shall be posted into that new feature request 702. |
||
from .broker import _signin_silently | ||
response = _signin_silently( | ||
"https://{}/{}".format(self.authority.instance, self.authority.tenant), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import json | ||
try: | ||
from unittest.mock import patch | ||
except: | ||
from mock import patch | ||
try: | ||
import pymsalruntime | ||
broker_available = True | ||
except ImportError: | ||
broker_available = False | ||
import msal | ||
from tests import unittest | ||
from tests.test_token_cache import build_response | ||
from tests.http_client import MinimalResponse | ||
|
||
|
||
SCOPE = "scope_foo" | ||
TOKEN_RESPONSE = build_response( | ||
access_token="at", | ||
uid="uid", utid="utid", # So that it will create an account | ||
scope=SCOPE, refresh_token="rt", # So that non-broker's acquire_token_silent() would work | ||
) | ||
|
||
def _mock_post(url, headers=None, *args, **kwargs): | ||
return MinimalResponse(status_code=200, text=json.dumps(TOKEN_RESPONSE)) | ||
|
||
@unittest.skipUnless(broker_available, "These test cases need pip install msal[broker]") | ||
@patch("msal.broker._acquire_token_silently", return_value=dict( | ||
TOKEN_RESPONSE, _account_id="placeholder")) | ||
@patch.object(msal.authority, "tenant_discovery", return_value={ | ||
"authorization_endpoint": "https://contoso.com/placeholder", | ||
"token_endpoint": "https://contoso.com/placeholder", | ||
}) # Otherwise it would fail on OIDC discovery | ||
class TestAccountSourceBehavior(unittest.TestCase): | ||
|
||
def test_device_flow_and_its_silent_call_should_bypass_broker(self, _, mocked_broker_ats): | ||
app = msal.PublicClientApplication("client_id", enable_broker_on_windows=True) | ||
result = app.acquire_token_by_device_flow({"device_code": "123"}, post=_mock_post) | ||
self.assertEqual(result["token_source"], "identity_provider") | ||
|
||
account = app.get_accounts()[0] | ||
self.assertEqual(account["account_source"], "urn:ietf:params:oauth:grant-type:device_code") | ||
|
||
result = app.acquire_token_silent_with_error( | ||
[SCOPE], account, force_refresh=True, post=_mock_post) | ||
mocked_broker_ats.assert_not_called() | ||
self.assertEqual(result["token_source"], "identity_provider") | ||
|
||
def test_ropc_flow_and_its_silent_call_should_bypass_broker(self, _, mocked_broker_ats): | ||
app = msal.PublicClientApplication("client_id", enable_broker_on_windows=True) | ||
with patch.object(app.authority, "user_realm_discovery", return_value={}): | ||
result = app.acquire_token_by_username_password( | ||
"username", "placeholder", [SCOPE], post=_mock_post) | ||
self.assertEqual(result["token_source"], "identity_provider") | ||
|
||
account = app.get_accounts()[0] | ||
self.assertEqual(account["account_source"], "password") | ||
|
||
result = app.acquire_token_silent_with_error( | ||
[SCOPE], account, force_refresh=True, post=_mock_post) | ||
mocked_broker_ats.assert_not_called() | ||
self.assertEqual(result["token_source"], "identity_provider") | ||
|
||
def test_interactive_flow_and_its_silent_call_should_invoke_broker(self, _, mocked_broker_ats): | ||
app = msal.PublicClientApplication("client_id", enable_broker_on_windows=True) | ||
with patch.object(app, "_acquire_token_interactive_via_broker", return_value=dict( | ||
TOKEN_RESPONSE, _account_id="placeholder")): | ||
result = app.acquire_token_interactive( | ||
[SCOPE], parent_window_handle=app.CONSOLE_WINDOW_HANDLE) | ||
self.assertEqual(result["token_source"], "broker") | ||
|
||
account = app.get_accounts()[0] | ||
self.assertEqual(account["account_source"], "broker") | ||
|
||
result = app.acquire_token_silent_with_error( | ||
[SCOPE], account, force_refresh=True, post=_mock_post) | ||
mocked_broker_ats.assert_called_once() | ||
self.assertEqual(result["token_source"], "broker") | ||
|
Uh oh!
There was an error while loading. Please reload this page.