Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Correct expires_on values of tokens from AzureCliCredential
  • Loading branch information
chlowell committed Oct 7, 2020
commit d77a9e35a9c22dbaae242c2d547df8cf7d43c1b3
6 changes: 6 additions & 0 deletions sdk/identity/azure-identity/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release History

## 1.4.1 (2020-10-07)
### Fixed
- `AzureCliCredential.get_token` correctly sets token expiration time,
preventing clients from using expired tokens
([#14331](https://github.com/Azure/azure-sdk-for-python/pull/14331))

## 1.4.0 (2020-08-10)
### Added
- `DefaultAzureCredential` uses the value of environment variable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import platform
import re
import sys
import time
from typing import TYPE_CHECKING

import subprocess
Expand Down Expand Up @@ -68,14 +69,18 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=no-self-use,unused-arg
def parse_token(output):
"""Parse output of 'az account get-access-token' to an AccessToken.

In particular, convert the CLI's "expiresOn" value, the string representation of a naive datetime, to epoch seconds.
In particular, convert the "expiresOn" value to epoch seconds. This value is a naive local datetime as returned by
datetime.fromtimestamp.
"""
try:
token = json.loads(output)
parsed_expires_on = datetime.strptime(token["expiresOn"], "%Y-%m-%d %H:%M:%S.%f")

# calculate seconds since the epoch; parsed_expires_on is naive
expires_on = (parsed_expires_on - datetime.fromtimestamp(0)).total_seconds()
dt = datetime.strptime(token["expiresOn"], "%Y-%m-%d %H:%M:%S.%f")
if hasattr(dt, "timestamp"):
# Python >= 3.3
expires_on = dt.timestamp()
else:
# taken from Python 3.5's datetime.timestamp()
expires_on = time.mktime((dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, -1, -1, -1))

return AccessToken(token["accessToken"], int(expires_on))
except (KeyError, ValueError):
Expand Down
7 changes: 3 additions & 4 deletions sdk/identity/azure-identity/tests/test_cli_credential.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@ def test_get_token():
"""The credential should parse the CLI's output to an AccessToken"""

access_token = "access token"
valid_seconds = 42
expected_expires_on = 1602015811
successful_output = json.dumps(
{
# expiresOn is a naive datetime representing valid_seconds from the epoch
"expiresOn": datetime.fromtimestamp(valid_seconds).strftime("%Y-%m-%d %H:%M:%S.%f"),
"expiresOn": datetime.fromtimestamp(expected_expires_on).strftime("%Y-%m-%d %H:%M:%S.%f"),
"accessToken": access_token,
"subscription": "some-guid",
"tenant": "some-guid",
Expand All @@ -68,7 +67,7 @@ def test_get_token():

assert token.token == access_token
assert type(token.expires_on) == int
assert token.expires_on == valid_seconds
assert token.expires_on == expected_expires_on


def test_cli_not_installed_linux():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,10 @@ async def test_get_token():
"""The credential should parse the CLI's output to an AccessToken"""

access_token = "access token"
valid_seconds = 42
expected_expires_on = 1602015811
successful_output = json.dumps(
{
# expiresOn is a naive datetime representing valid_seconds from the epoch
"expiresOn": datetime.fromtimestamp(valid_seconds).strftime("%Y-%m-%d %H:%M:%S.%f"),
"expiresOn": datetime.fromtimestamp(expected_expires_on).strftime("%Y-%m-%d %H:%M:%S.%f"),
"accessToken": access_token,
"subscription": "some-guid",
"tenant": "some-guid",
Expand All @@ -93,7 +92,7 @@ async def test_get_token():

assert token.token == access_token
assert type(token.expires_on) == int
assert token.expires_on == valid_seconds
assert token.expires_on == expected_expires_on


async def test_cli_not_installed_linux():
Expand Down