-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
{Core} Refactor code for MSAL authentication #29439
Conversation
️✔️AzureCLI-FullTest
|
Hi @jiasli, |
️✔️AzureCLI-BreakingChangeTest
|
Core |
self.authority.tenant, claims) | ||
result = self.acquire_token_silent_with_error(list(scopes), self._account, claims_challenge=claims, **kwargs) | ||
self._msal_app.authority.tenant, claims) | ||
result = self._msal_app.acquire_token_silent_with_error(list(scopes), self._account, claims_challenge=claims, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By saving msal.PublicClientApplication
instance to self._msal_app
, self._account
is no longer saved as an attribute of msal.PublicClientApplication
. This avoids the possible conflict of msal.PublicClientApplication
introducing an _account
attribute of its own.
match = re.search(r'-+BEGIN CERTIFICATE-+(?P<public>[^-]+)-+END CERTIFICATE-+', cert_file_string, re.I) | ||
public_certificate = match.group().strip() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not test code with the code itself:
azure-cli/src/azure-cli-core/azure/cli/core/auth/identity.py
Lines 290 to 292 in d142b43
match = re.search(r'-----BEGIN CERTIFICATE-----(?P<cert_value>[^-]+)-----END CERTIFICATE-----', | |
self._certificate_string, re.I) | |
self._public_certificate = match.group() |
|
||
class TestIdentity(unittest.TestCase): | ||
|
||
@mock.patch("azure.cli.core.auth.identity.ServicePrincipalStore.save_entry") | ||
@mock.patch("msal.application.ConfidentialClientApplication.acquire_token_for_client") | ||
@mock.patch("msal.application.ConfidentialClientApplication.__init__") | ||
@mock.patch("msal.application.ConfidentialClientApplication.__init__", return_value=None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
msal.application.ConfidentialClientApplication.__init__
is previously called as super().__init__
in azure.cli.core.auth.msal_authentication.ServicePrincipalCredential.__init__
. The returned MagicMock
gets discarded.
Now msal.application.ConfidentialClientApplication.__init__
is directly invoked. Without return_value=None
, patching msal.application.ConfidentialClientApplication.__init__
makes it return a MagicMock
. pytest
fails:
self = <azure.cli.core.auth.identity.Identity object at 0x0000021CEACFC150>
client_id = 'sp_id1', credential = {'client_secret': 'test_secret'}
scopes = 'openid'
def login_with_service_principal(self, client_id, credential, scopes):
"""
`credential` is a dict returned by ServicePrincipalAuth.build_credential
"""
sp_auth = ServicePrincipalAuth.build_from_credential(self.tenant_id, client_id, credential)
client_credential = sp_auth.get_msal_client_credential()
> cca = ConfidentialClientApplication(client_id, client_credential, **self._msal_app_kwargs)
E TypeError: __init__() should return None, not 'MagicMock'
..\identity.py:196: TypeError
client_credential = sp_auth.get_msal_client_credential() | ||
cca = ConfidentialClientApplication(client_id, client_credential, **self._msal_app_kwargs) | ||
result = cca.acquire_token_for_client(scopes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because ServicePrincipalCredential
stops inheriting from msal.ConfidentialClientApplication
, it is no longer possible to call acquire_token_for_client
on the cred
. We prepare client_credential
and directly create a ConfidentialClientApplication
instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is renamed to more accurately reflect its content.
logger = get_logger(__name__) | ||
|
||
|
||
class UserCredential(PublicClientApplication): | ||
class UserCredential: # pylint: disable=too-few-public-methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interestingly, pylint doesn't think this is worth a class. Haha
Used when class has too few public methods, so be sure it's really worth it.
Ref: https://pylint.readthedocs.io/en/latest/user_guide/messages/refactor/too-few-public-methods.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to define such class so that it can be passed to SDK as a credential.
This PR breaks from azure.cli.core.auth.msal_authentication import ServicePrincipalCredential
credential, _, _ = profile.get_login_credentials(subscription_id=profile.get_subscription()["id"],
resource=consts.KAP_1P_Server_App_Scope)
if isinstance(credential._credential, ServicePrincipalCredential):
# This is a workaround to fix the issue where the token is not being refreshed
# https://github.com/AzureAD/microsoft-authentication-library-for-python/pull/692
credential._credential.remove_tokens_for_client() This is considered a bad practice as internal As AzureAD/microsoft-authentication-library-for-python#692 has been resolved, this workaround should no longer be necessary. |
Related command
az login
Description
Change 1: Refactor
UserCredential
andServicePrincipalCredential
In the current code,
UserCredential
inherits frommsal.PublicClientApplication
ServicePrincipalCredential
inherits frommsal.ConfidentialClientApplication
This exposes the internal MSAL application instances to the caller SDK which only requires the
get_token()
callback interface.This PR stops
UserCredential
andServicePrincipalCredential
from inheriting from MSAL, but makes them keep an internal MSAL application instance, in order to greatly reduce MSAL applications' exposure.Change 2: Refactor
ServicePrincipalAuth
All attributes are set at
__init__()
to avoid frequently callinggetattr()
to check the existence of attributes. The logic for generatingclient_credential
is moved toServicePrincipalAuth.get_msal_client_credential()
.This PR paves way for