|
2 | 2 | # All rights reserved.
|
3 | 3 | #
|
4 | 4 | # This code is licensed under the MIT License.
|
| 5 | +import calendar |
| 6 | +import datetime |
5 | 7 | import json
|
6 | 8 | import logging
|
7 | 9 | import os
|
| 10 | +import re |
8 | 11 | import socket
|
9 | 12 | import sys
|
10 | 13 | import time
|
@@ -432,6 +435,33 @@ def _obtain_token(http_client, managed_identity, resource):
|
432 | 435 | return _obtain_token_on_azure_vm(http_client, managed_identity, resource)
|
433 | 436 |
|
434 | 437 |
|
| 438 | +def _parse_expires_on(raw: str) -> int: |
| 439 | + try: |
| 440 | + return int(raw) # It is typically an epoch time |
| 441 | + except ValueError: |
| 442 | + pass |
| 443 | + try: |
| 444 | + # '2024-10-18T19:51:37.0000000+00:00' was observed in |
| 445 | + # https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/4963 |
| 446 | + if sys.version_info < (3, 11): # Does not support 7-digit microseconds |
| 447 | + raw = re.sub( # Strip microseconds portion using regex |
| 448 | + r'(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d+)([+-]\d{2}:\d{2})', |
| 449 | + r'\1\3', |
| 450 | + raw) |
| 451 | + return int(datetime.datetime.fromisoformat(raw).timestamp()) |
| 452 | + except ValueError: |
| 453 | + pass |
| 454 | + for format in ( |
| 455 | + "%m/%d/%Y %H:%M:%S %z", # 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 |
| 456 | + "%m/%d/%Y %I:%M:%S %p %z", # 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 |
| 457 | + ): |
| 458 | + try: |
| 459 | + return calendar.timegm(time.strptime(raw, format)) |
| 460 | + except ValueError: |
| 461 | + pass |
| 462 | + raise ManagedIdentityError(f"Cannot parse expires_on: {raw}") |
| 463 | + |
| 464 | + |
435 | 465 | def _adjust_param(params, managed_identity, types_mapping=None):
|
436 | 466 | # Modify the params dict in place
|
437 | 467 | id_name = (types_mapping or ManagedIdentity._types_mapping).get(
|
@@ -504,7 +534,7 @@ def _obtain_token_on_app_service(
|
504 | 534 | if payload.get("access_token") and payload.get("expires_on"):
|
505 | 535 | return { # Normalizing the payload into OAuth2 format
|
506 | 536 | "access_token": payload["access_token"],
|
507 |
| - "expires_in": int(payload["expires_on"]) - int(time.time()), |
| 537 | + "expires_in": _parse_expires_on(payload["expires_on"]) - int(time.time()), |
508 | 538 | "resource": payload.get("resource"),
|
509 | 539 | "token_type": payload.get("token_type", "Bearer"),
|
510 | 540 | }
|
@@ -538,7 +568,7 @@ def _obtain_token_on_machine_learning(
|
538 | 568 | if payload.get("access_token") and payload.get("expires_on"):
|
539 | 569 | return { # Normalizing the payload into OAuth2 format
|
540 | 570 | "access_token": payload["access_token"],
|
541 |
| - "expires_in": int(payload["expires_on"]) - int(time.time()), |
| 571 | + "expires_in": _parse_expires_on(payload["expires_on"]) - int(time.time()), |
542 | 572 | "resource": payload.get("resource"),
|
543 | 573 | "token_type": payload.get("token_type", "Bearer"),
|
544 | 574 | }
|
|
0 commit comments