Skip to content

Commit 2e65e8a

Browse files
committed
get_token_info
1 parent 3b70655 commit 2e65e8a

File tree

2 files changed

+122
-6
lines changed

2 files changed

+122
-6
lines changed

src/azure-cli-core/azure/cli/core/auth/credential_adaptor.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,18 @@ def __init__(self, credential, auxiliary_credentials=None):
2424
self._auxiliary_credentials = auxiliary_credentials
2525

2626
def get_token(self, *scopes, **kwargs):
27-
"""Get an access token from the main credential."""
27+
"""Old SDK token protocol. Get an access token from the main credential."""
2828
logger.debug("CredentialAdaptor.get_token: scopes=%r, kwargs=%r", scopes, kwargs)
2929

30-
# Discard unsupported kwargs: tenant_id, enable_cae
31-
filtered_kwargs = {}
32-
if 'data' in kwargs:
33-
filtered_kwargs['data'] = kwargs['data']
30+
msal_kwargs = _prepare_msal_kwargs(kwargs)
31+
return build_sdk_access_token(self._credential.acquire_token(list(scopes), **msal_kwargs))
3432

35-
return build_sdk_access_token(self._credential.acquire_token(list(scopes), **filtered_kwargs))
33+
def get_token_info(self, *scopes, options=None):
34+
"""New SDK token protocol. Get an access token from the main credential."""
35+
logger.debug("CredentialAdaptor.get_token_info: scopes=%r, options=%r", scopes, options)
36+
37+
msal_kwargs = _prepare_msal_kwargs(options)
38+
return _build_sdk_access_token_info(self._credential.acquire_token(list(scopes), **msal_kwargs))
3639

3740
def get_auxiliary_tokens(self, *scopes, **kwargs):
3841
"""Get access tokens from auxiliary credentials."""
@@ -41,3 +44,32 @@ def get_auxiliary_tokens(self, *scopes, **kwargs):
4144
return [build_sdk_access_token(cred.acquire_token(list(scopes), **kwargs))
4245
for cred in self._auxiliary_credentials]
4346
return None
47+
48+
49+
def _prepare_msal_kwargs(options=None):
50+
# Preserve supported options and discard unsupported options (tenant_id, enable_cae).
51+
# Both get_token's kwargs and get_token_info's options are accepted as their schema is the same (at least for now).
52+
msal_kwargs = {}
53+
if options:
54+
# For VM SSH. TokenRequestOptions doesn't support 'data'.
55+
if 'data' in options:
56+
msal_kwargs['data'] = options['data']
57+
# For CAE
58+
if 'claims' in options:
59+
msal_kwargs['claims'] = options['claims']
60+
return msal_kwargs
61+
62+
63+
def _build_sdk_access_token_info(token_entry):
64+
# MSAL token entry sample:
65+
# {
66+
# 'access_token': 'eyJ0eXAiOiJKV...',
67+
# 'token_type': 'Bearer',
68+
# 'expires_in': 1618,
69+
# 'token_source': 'cache'
70+
# }
71+
from .constants import ACCESS_TOKEN, EXPIRES_IN
72+
from .util import _now_timestamp
73+
from azure.core.credentials import AccessTokenInfo
74+
75+
return AccessTokenInfo(token_entry[ACCESS_TOKEN], _now_timestamp() + token_entry[EXPIRES_IN])
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
7+
import unittest
8+
from collections import namedtuple
9+
from unittest import mock
10+
11+
from ..credential_adaptor import CredentialAdaptor
12+
13+
14+
MOCK_ACCESS_TOKEN = "mock_access_token"
15+
MOCK_DATA = {"test_data": "value1"}
16+
MOCK_CLAIMS = {"test_claims": "value2"}
17+
18+
class MsalCredentialStub:
19+
20+
def __init__(self, *args, **kwargs):
21+
self.acquire_token_scopes = None
22+
self.acquire_token_kwargs = None
23+
super().__init__()
24+
25+
def acquire_token(self, scopes, **kwargs):
26+
self.acquire_token_scopes = scopes
27+
self.acquire_token_kwargs = kwargs
28+
return {
29+
'access_token': MOCK_ACCESS_TOKEN,
30+
'token_type': 'Bearer',
31+
'expires_in': 1800,
32+
'token_source': 'cache'
33+
}
34+
35+
def _now_timestamp_mock():
36+
# 2021-09-06 08:55:23
37+
return 1630918523
38+
39+
40+
class TestCredentialAdaptor(unittest.TestCase):
41+
42+
@mock.patch('azure.cli.core.auth.util._now_timestamp', new=_now_timestamp_mock)
43+
def test_get_token(self):
44+
msal_cred = MsalCredentialStub()
45+
sdk_cred = CredentialAdaptor(msal_cred)
46+
access_token = sdk_cred.get_token('https://management.core.windows.net//.default')
47+
assert msal_cred.acquire_token_scopes == ['https://management.core.windows.net//.default']
48+
49+
from ..util import AccessToken
50+
assert isinstance(access_token, AccessToken)
51+
assert access_token.token == MOCK_ACCESS_TOKEN
52+
assert access_token.expires_on == 1630920323
53+
54+
sdk_cred.get_token('https://management.core.windows.net//.default', data=MOCK_DATA)
55+
assert msal_cred.acquire_token_kwargs['data'] == MOCK_DATA
56+
57+
sdk_cred.get_token('https://management.core.windows.net//.default', claims=MOCK_CLAIMS)
58+
assert msal_cred.acquire_token_kwargs['claims'] == MOCK_CLAIMS
59+
60+
61+
@mock.patch('azure.cli.core.auth.util._now_timestamp', new=_now_timestamp_mock)
62+
def test_get_token_info(self):
63+
msal_cred = MsalCredentialStub()
64+
sdk_cred = CredentialAdaptor(msal_cred)
65+
access_token_info = sdk_cred.get_token_info('https://management.core.windows.net//.default')
66+
67+
from azure.core.credentials import AccessTokenInfo
68+
assert isinstance(access_token_info, AccessTokenInfo)
69+
assert access_token_info.token == MOCK_ACCESS_TOKEN
70+
assert access_token_info.expires_on == 1630920323
71+
assert access_token_info.token_type == 'Bearer'
72+
73+
assert msal_cred.acquire_token_scopes == ['https://management.core.windows.net//.default']
74+
75+
# Actually, TokenRequestOptions doesn't support 'data'.
76+
sdk_cred.get_token_info('https://management.core.windows.net//.default', options={'data': MOCK_DATA})
77+
assert msal_cred.acquire_token_kwargs['data'] == MOCK_DATA
78+
79+
sdk_cred.get_token_info('https://management.core.windows.net//.default', options={'claims': MOCK_CLAIMS})
80+
assert msal_cred.acquire_token_kwargs['claims'] == MOCK_CLAIMS
81+
82+
83+
if __name__ == '__main__':
84+
unittest.main()

0 commit comments

Comments
 (0)