-
Notifications
You must be signed in to change notification settings - Fork 3.3k
{Core} Refactor code for MSAL authentication #29439
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
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() |
| @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.
|
|
||
|
|
||
| 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 loginDescription
Change 1: Refactor
UserCredentialandServicePrincipalCredentialIn the current code,
UserCredentialinherits frommsal.PublicClientApplicationServicePrincipalCredentialinherits frommsal.ConfidentialClientApplicationThis exposes the internal MSAL application instances to the caller SDK which only requires the
get_token()callback interface.This PR stops
UserCredentialandServicePrincipalCredentialfrom inheriting from MSAL, but makes them keep an internal MSAL application instance, in order to greatly reduce MSAL applications' exposure.Change 2: Refactor
ServicePrincipalAuthAll attributes are set at
__init__()to avoid frequently callinggetattr()to check the existence of attributes. The logic for generatingclient_credentialis moved toServicePrincipalAuth.get_msal_client_credential().This PR paves way for