Skip to content

Commit b535e07

Browse files
committed
Support ISO8601-ish format on expires_on in MI
1 parent 137dee4 commit b535e07

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

msal/managed_identity.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# All rights reserved.
33
#
44
# This code is licensed under the MIT License.
5+
import datetime
56
import json
67
import logging
78
import os
@@ -432,6 +433,29 @@ def _obtain_token(http_client, managed_identity, resource):
432433
return _obtain_token_on_azure_vm(http_client, managed_identity, resource)
433434

434435

436+
def _parse_expires_on(raw: str) -> int:
437+
try:
438+
return int(raw) # It is typically an epoch time
439+
except ValueError:
440+
pass
441+
try:
442+
# '2024-10-18T19:51:37.0000000+00:00' was observed in
443+
# https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/4963
444+
# We support format(s) YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]]
445+
return int(datetime.datetime.fromisoformat(raw).timestamp())
446+
except ValueError:
447+
pass
448+
for format in (
449+
"%m/%d/%Y %H:%M:%S +00:00", # Derived from https://github.com/Azure/azure-sdk-for-python/blob/azure-identity_1.21.0/sdk/identity/azure-identity/azure/identity/_credentials/azure_ml.py#L52
450+
"%m/%d/%Y %H:%M:%S %p +00:00", # Derived from https://github.com/Azure/azure-sdk-for-python/blob/azure-identity_1.21.0/sdk/identity/azure-identity/azure/identity/_credentials/azure_ml.py#L51
451+
):
452+
try:
453+
return int(time.mktime(time.strptime(raw, format)))
454+
except ValueError:
455+
pass
456+
raise ManagedIdentityError(f"Cannot parse expires_on: {raw}")
457+
458+
435459
def _adjust_param(params, managed_identity, types_mapping=None):
436460
# Modify the params dict in place
437461
id_name = (types_mapping or ManagedIdentity._types_mapping).get(
@@ -504,7 +528,7 @@ def _obtain_token_on_app_service(
504528
if payload.get("access_token") and payload.get("expires_on"):
505529
return { # Normalizing the payload into OAuth2 format
506530
"access_token": payload["access_token"],
507-
"expires_in": int(payload["expires_on"]) - int(time.time()),
531+
"expires_in": _parse_expires_on(payload["expires_on"]) - int(time.time()),
508532
"resource": payload.get("resource"),
509533
"token_type": payload.get("token_type", "Bearer"),
510534
}
@@ -538,7 +562,7 @@ def _obtain_token_on_machine_learning(
538562
if payload.get("access_token") and payload.get("expires_on"):
539563
return { # Normalizing the payload into OAuth2 format
540564
"access_token": payload["access_token"],
541-
"expires_in": int(payload["expires_on"]) - int(time.time()),
565+
"expires_in": _parse_expires_on(payload["expires_on"]) - int(time.time()),
542566
"resource": payload.get("resource"),
543567
"token_type": payload.get("token_type", "Bearer"),
544568
}

tests/test_mi.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
MACHINE_LEARNING,
2626
SERVICE_FABRIC,
2727
DEFAULT_TO_VM,
28+
_parse_expires_on,
2829
)
2930
from msal.token_cache import is_subdict_of
3031

@@ -49,6 +50,18 @@ def test_helper_class_should_be_interchangable_with_dict_which_could_be_loaded_f
4950
{"ManagedIdentityIdType": "SystemAssigned", "Id": None})
5051

5152

53+
class ExpiresOnTestCase(unittest.TestCase):
54+
def test_expires_on_parsing(self):
55+
for input, expected in {
56+
"1234567890": 1234567890,
57+
"1970-01-01T00:00:12.0000000+00:00": 12,
58+
"2024-10-18T19:51:37.0000000+00:00": 1729281097, # Copied from https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/4963
59+
"1/16/2020 5:24:12 AM +00:00": 1579181052, # Derived from https://github.com/Azure/azure-sdk-for-python/blob/azure-identity_1.21.0/sdk/identity/azure-identity/azure/identity/_credentials/azure_ml.py#L51
60+
"06/20/2019 02:57:58 +00:00": 1561024678, # Derived from https://github.com/Azure/azure-sdk-for-python/blob/azure-identity_1.21.0/sdk/identity/azure-identity/azure/identity/_credentials/azure_ml.py#L52
61+
}.items():
62+
self.assertEqual(_parse_expires_on(input), expected, f"Should parse {input} to {expected}")
63+
64+
5265
class ClientTestCase(unittest.TestCase):
5366
maxDiff = None
5467

0 commit comments

Comments
 (0)