diff --git a/src/azure-cli-core/azure/cli/core/auth/adal_authentication.py b/src/azure-cli-core/azure/cli/core/auth/adal_authentication.py index f56741afef1..e8ac5233f58 100644 --- a/src/azure-cli-core/azure/cli/core/auth/adal_authentication.py +++ b/src/azure-cli-core/azure/cli/core/auth/adal_authentication.py @@ -15,6 +15,7 @@ class MSIAuthenticationWrapper(MSIAuthentication): # This method is exposed for Azure Core. Add *scopes, **kwargs to fit azure.core requirement + # pylint: disable=line-too-long def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument logger.debug("MSIAuthenticationWrapper.get_token invoked by Track 2 SDK with scopes=%s", scopes) @@ -27,7 +28,8 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument # If available, use resource provided by SDK self.resource = resource self.set_token() - # Managed Identity token entry sample: + # VM managed identity endpoint 2018-02-01 token entry sample: + # curl "http://169.254.169.254:80/metadata/identity/oauth2/token?resource=https://management.core.windows.net/&api-version=2018-02-01" -H "Metadata: true" # { # "access_token": "eyJ0eXAiOiJKV...", # "client_id": "da95e381-d7ab-4fdc-8047-2457909c723b", @@ -35,10 +37,20 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument # "expires_on": "1605238724", # "ext_expires_in": "86399", # "not_before": "1605152024", - # "resource": "https://management.azure.com/", + # "resource": "https://management.core.windows.net/", # "token_type": "Bearer" # } - return AccessToken(self.token['access_token'], int(self.token['expires_on'])) + + # App Service managed identity endpoint 2017-09-01 token entry sample: + # curl "${MSI_ENDPOINT}?resource=https://management.core.windows.net/&api-version=2017-09-01" -H "secret: ${MSI_SECRET}" + # { + # "access_token": "eyJ0eXAiOiJKV...", + # "expires_on":"11/05/2021 15:18:31 +00:00", + # "resource":"https://management.core.windows.net/", + # "token_type":"Bearer", + # "client_id":"df45d93a-de31-47ca-acef-081ca60d1a83" + # } + return AccessToken(self.token['access_token'], _normalize_expires_on(self.token['expires_on'])) def set_token(self): import traceback @@ -69,3 +81,20 @@ def set_token(self): def signed_session(self, session=None): logger.debug("MSIAuthenticationWrapper.signed_session invoked by Track 1 SDK") super().signed_session(session) + + +def _normalize_expires_on(expires_on): + """ + The expires_on field returned by managed identity differs on Azure VM (epoch str) and App Service (datetime str). + Normalize to epoch int. + """ + try: + # Treat as epoch string "1605238724" + expires_on_epoch_int = int(expires_on) + except ValueError: + import datetime + # Treat as datetime string "11/05/2021 15:18:31 +00:00" + expires_on_epoch_int = int(datetime.datetime.strptime(expires_on, '%m/%d/%Y %H:%M:%S %z').timestamp()) + + logger.debug("Normalize expires_on: %r -> %r", expires_on, expires_on_epoch_int) + return expires_on_epoch_int diff --git a/src/azure-cli-core/azure/cli/core/auth/tests/test_adal_authentication.py b/src/azure-cli-core/azure/cli/core/auth/tests/test_adal_authentication.py new file mode 100644 index 00000000000..f3a48cb7c90 --- /dev/null +++ b/src/azure-cli-core/azure/cli/core/auth/tests/test_adal_authentication.py @@ -0,0 +1,18 @@ +# -------------------------------------------------------------------------------------------- +# 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 azure.cli.core.auth.adal_authentication import _normalize_expires_on + + +class TestUtil(unittest.TestCase): + def test_normalize_expires_on(self): + assert _normalize_expires_on("11/05/2021 15:18:31 +00:00") == 1636125511 + assert _normalize_expires_on('1636125511') == 1636125511 + + +if __name__ == '__main__': + unittest.main()