-
Notifications
You must be signed in to change notification settings - Fork 3.3k
{Auth} Support get_token_info protocol
#30928
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 | ||
|---|---|---|---|---|
|
|
@@ -24,15 +24,24 @@ def __init__(self, credential, auxiliary_credentials=None): | |||
| self._auxiliary_credentials = auxiliary_credentials | ||||
|
|
||||
| def get_token(self, *scopes, **kwargs): | ||||
| """Get an access token from the main credential.""" | ||||
| """Implement the old SDK token protocol azure.core.credentials.TokenCredential | ||||
| Return azure.core.credentials.AccessToken | ||||
| """ | ||||
| logger.debug("CredentialAdaptor.get_token: scopes=%r, kwargs=%r", scopes, kwargs) | ||||
|
|
||||
| # Discard unsupported kwargs: tenant_id, enable_cae | ||||
| filtered_kwargs = {} | ||||
| if 'data' in kwargs: | ||||
| filtered_kwargs['data'] = kwargs['data'] | ||||
| msal_kwargs = _prepare_msal_kwargs(kwargs) | ||||
| msal_result = self._credential.acquire_token(list(scopes), **msal_kwargs) | ||||
| return build_sdk_access_token(msal_result) | ||||
|
|
||||
| def get_token_info(self, *scopes, options=None): | ||||
| """Implement the new SDK token protocol azure.core.credentials.SupportsTokenInfo | ||||
| Return azure.core.credentials.AccessTokenInfo | ||||
| """ | ||||
| logger.debug("CredentialAdaptor.get_token_info: scopes=%r, options=%r", scopes, options) | ||||
|
|
||||
| return build_sdk_access_token(self._credential.acquire_token(list(scopes), **filtered_kwargs)) | ||||
| msal_kwargs = _prepare_msal_kwargs(options) | ||||
| msal_result = self._credential.acquire_token(list(scopes), **msal_kwargs) | ||||
| return _build_sdk_access_token_info(msal_result) | ||||
|
|
||||
| def get_auxiliary_tokens(self, *scopes, **kwargs): | ||||
| """Get access tokens from auxiliary credentials.""" | ||||
|
|
@@ -41,3 +50,33 @@ def get_auxiliary_tokens(self, *scopes, **kwargs): | |||
| return [build_sdk_access_token(cred.acquire_token(list(scopes), **kwargs)) | ||||
| for cred in self._auxiliary_credentials] | ||||
| return None | ||||
|
|
||||
|
|
||||
| def _prepare_msal_kwargs(options=None): | ||||
| # Preserve supported options and discard unsupported options (tenant_id, enable_cae). | ||||
| # Both get_token's kwargs and get_token_info's options are accepted as their schema is the same (at least for now). | ||||
| msal_kwargs = {} | ||||
| if options: | ||||
| # For VM SSH. 'data' support is a CLI-specific extension. | ||||
| # SDK doesn't support 'data': https://github.com/Azure/azure-sdk-for-python/pull/16397 | ||||
| if 'data' in options: | ||||
| msal_kwargs['data'] = options['data'] | ||||
|
Comment on lines
+60
to
+63
Member
Author
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.
credential, _, _ = profile.get_login_credentials(subscription_id=profile.get_subscription()["id"])
certificatedata = credential.get_token(*scopes, data=data) |
||||
| # For CAE | ||||
| if 'claims' in options: | ||||
| msal_kwargs['claims_challenge'] = options['claims'] | ||||
| return msal_kwargs | ||||
|
|
||||
|
|
||||
| def _build_sdk_access_token_info(token_entry): | ||||
|
Member
Author
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.
so it can't be moved to |
||||
| # MSAL token entry sample: | ||||
| # { | ||||
| # 'access_token': 'eyJ0eXAiOiJKV...', | ||||
| # 'token_type': 'Bearer', | ||||
| # 'expires_in': 1618, | ||||
| # 'token_source': 'cache' | ||||
| # } | ||||
| from .constants import ACCESS_TOKEN, EXPIRES_IN | ||||
| from .util import _now_timestamp | ||||
| from azure.core.credentials import AccessTokenInfo | ||||
|
|
||||
| return AccessTokenInfo(token_entry[ACCESS_TOKEN], _now_timestamp() + token_entry[EXPIRES_IN]) | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # -------------------------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for license information. | ||
| # -------------------------------------------------------------------------------------------- | ||
|
|
||
|
|
||
| import unittest | ||
| from unittest import mock | ||
|
|
||
| from ..credential_adaptor import CredentialAdaptor | ||
|
|
||
|
|
||
| MOCK_ACCESS_TOKEN = "mock_access_token" | ||
| MOCK_DATA = { | ||
| 'key_id': 'test', | ||
| 'req_cnf': 'test', | ||
| 'token_type': 'ssh-cert' | ||
| } | ||
| MOCK_CLAIMS = {"test_claims": "value2"} | ||
|
|
||
| class MsalCredentialStub: | ||
|
|
||
| def __init__(self, *args, **kwargs): | ||
| self.acquire_token_scopes = None | ||
| self.acquire_token_claims_challenge = None | ||
| self.acquire_token_kwargs = None | ||
| super().__init__() | ||
|
|
||
| def acquire_token(self, scopes, claims_challenge=None, **kwargs): | ||
| self.acquire_token_scopes = scopes | ||
| self.acquire_token_claims_challenge = claims_challenge | ||
| self.acquire_token_kwargs = kwargs | ||
| return { | ||
| 'access_token': MOCK_ACCESS_TOKEN, | ||
| 'token_type': 'Bearer', | ||
| 'expires_in': 1800, | ||
| 'token_source': 'cache' | ||
| } | ||
|
|
||
| def _now_timestamp_mock(): | ||
| # 2021-09-06 08:55:23 | ||
| return 1630918523 | ||
|
|
||
|
|
||
| class TestCredentialAdaptor(unittest.TestCase): | ||
|
|
||
| @mock.patch('azure.cli.core.auth.util._now_timestamp', new=_now_timestamp_mock) | ||
| def test_get_token(self): | ||
| msal_cred = MsalCredentialStub() | ||
| sdk_cred = CredentialAdaptor(msal_cred) | ||
| access_token = sdk_cred.get_token('https://management.core.windows.net//.default') | ||
| assert msal_cred.acquire_token_scopes == ['https://management.core.windows.net//.default'] | ||
|
|
||
| from ..util import AccessToken | ||
| assert isinstance(access_token, AccessToken) | ||
| assert access_token.token == MOCK_ACCESS_TOKEN | ||
| assert access_token.expires_on == 1630920323 | ||
|
|
||
| # Note that SDK doesn't support 'data'. This is a CLI-specific extension. | ||
| sdk_cred.get_token('https://management.core.windows.net//.default', data=MOCK_DATA) | ||
| assert msal_cred.acquire_token_kwargs['data'] == MOCK_DATA | ||
|
|
||
| sdk_cred.get_token('https://management.core.windows.net//.default', claims=MOCK_CLAIMS) | ||
| assert msal_cred.acquire_token_claims_challenge == MOCK_CLAIMS | ||
|
|
||
|
|
||
| @mock.patch('azure.cli.core.auth.util._now_timestamp', new=_now_timestamp_mock) | ||
| def test_get_token_info(self): | ||
| msal_cred = MsalCredentialStub() | ||
| sdk_cred = CredentialAdaptor(msal_cred) | ||
| access_token_info = sdk_cred.get_token_info('https://management.core.windows.net//.default') | ||
|
|
||
| from azure.core.credentials import AccessTokenInfo | ||
| assert isinstance(access_token_info, AccessTokenInfo) | ||
| assert access_token_info.token == MOCK_ACCESS_TOKEN | ||
| assert access_token_info.expires_on == 1630920323 | ||
| assert access_token_info.token_type == 'Bearer' | ||
|
|
||
| assert msal_cred.acquire_token_scopes == ['https://management.core.windows.net//.default'] | ||
|
|
||
| # Note that SDK doesn't support 'data'. If 'data' were supported, it should be tested with: | ||
| sdk_cred.get_token_info('https://management.core.windows.net//.default', options={'data': MOCK_DATA}) | ||
| assert msal_cred.acquire_token_kwargs['data'] == MOCK_DATA | ||
|
|
||
| sdk_cred.get_token_info('https://management.core.windows.net//.default', options={'claims': MOCK_CLAIMS}) | ||
| assert msal_cred.acquire_token_claims_challenge == MOCK_CLAIMS | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
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.
get_tokenis kept assshextension still direclty uses it to get an SSH certificate:https://github.com/Azure/azure-cli-extensions/blob/695bd02037a7a8abd6b0ac76ae1ac1559ae46c41/src/ssh/azext_ssh/custom.py#L232