in sdk/identity/azure-identity/src/token_credential_impl.cpp [258:477]
AccessToken TokenCredentialImpl::ParseToken(
std::string const& jsonString,
std::string const& accessTokenPropertyName,
std::string const& expiresInPropertyName,
std::vector<std::string> const& expiresOnPropertyNames,
std::string const& refreshInPropertyName,
bool proactiveRenewal,
int utcDiffSeconds)
{
json parsedJson;
try
{
parsedJson = Azure::Core::Json::_internal::json::parse(jsonString);
}
catch (json::exception const&)
{
IdentityLog::Write(
IdentityLog::Level::Verbose,
ParseTokenLogPrefix + "Cannot parse the string '" + jsonString + "' as JSON.");
throw;
}
if (!parsedJson.contains(accessTokenPropertyName)
|| !parsedJson[accessTokenPropertyName].is_string())
{
ThrowJsonPropertyError(
accessTokenPropertyName,
parsedJson,
accessTokenPropertyName,
expiresInPropertyName,
expiresOnPropertyNames);
}
AccessToken accessToken;
accessToken.Token = parsedJson[accessTokenPropertyName].get<std::string>();
accessToken.ExpiresOn = std::chrono::system_clock::now();
// expiresIn = number of seconds until refresh.
// expiresOn = timestamp of refresh expressed as seconds since epoch.
if (!refreshInPropertyName.empty() && parsedJson.contains(refreshInPropertyName))
{
auto const& refreshIn = parsedJson[refreshInPropertyName];
if (refreshIn.is_number_unsigned())
{
try
{
// 'refresh_in' as number (seconds until refresh)
auto const value = refreshIn.get<std::int64_t>();
if (value <= MaxExpirationInSeconds)
{
static_assert(
MaxExpirationInSeconds <= (std::numeric_limits<std::int32_t>::max)(),
"Can safely cast to int32");
accessToken.ExpiresOn += std::chrono::seconds(static_cast<std::int32_t>(value));
return accessToken;
}
}
catch (std::exception const&)
{
// refreshIn.get<std::int64_t>() has thrown, we may throw later.
}
}
}
if (parsedJson.contains(expiresInPropertyName))
{
auto const& expiresIn = parsedJson[expiresInPropertyName];
if (expiresIn.is_number_unsigned())
{
try
{
// 'expires_in' as number (seconds until expiration)
auto const value = expiresIn.get<std::int64_t>();
if (value <= MaxExpirationInSeconds)
{
static_assert(
MaxExpirationInSeconds <= (std::numeric_limits<std::int32_t>::max)(),
"Can safely cast to int32");
auto expiresInSeconds = std::chrono::seconds(static_cast<std::int32_t>(value));
accessToken.ExpiresOn
+= proactiveRenewal ? GetProactiveRenewalSeconds(expiresInSeconds) : expiresInSeconds;
return accessToken;
}
}
catch (std::exception const&)
{
// expiresIn.get<std::int64_t>() has thrown, we may throw later.
}
}
if (expiresIn.is_string())
{
try
{
// 'expires_in' as numeric string (seconds until expiration)
static_assert(
MaxExpirationInSeconds <= (std::numeric_limits<std::int32_t>::max)(),
"Can safely cast to int32");
auto expiresInSeconds = std::chrono::seconds(static_cast<std::int32_t>(
ParseNumericExpiration(expiresIn.get<std::string>(), MaxExpirationInSeconds)));
accessToken.ExpiresOn
+= proactiveRenewal ? GetProactiveRenewalSeconds(expiresInSeconds) : expiresInSeconds;
return accessToken;
}
catch (std::exception const&)
{
// ParseNumericExpiration() has thrown, we may throw later.
}
}
}
std::vector<std::string> nonEmptyExpiresOnPropertyNames;
std::copy_if(
expiresOnPropertyNames.begin(),
expiresOnPropertyNames.end(),
std::back_inserter(nonEmptyExpiresOnPropertyNames),
[](auto const& propertyName) { return !propertyName.empty(); });
if (nonEmptyExpiresOnPropertyNames.empty())
{
// The code was not able to parse the value of 'expires_in', and the caller did not pass any
// 'expires_on' for us to find and try parse.
ThrowJsonPropertyError(
expiresInPropertyName,
parsedJson,
accessTokenPropertyName,
expiresInPropertyName,
nonEmptyExpiresOnPropertyNames);
}
for (auto const& expiresOnPropertyName : nonEmptyExpiresOnPropertyNames)
{
if (parsedJson.contains(expiresOnPropertyName))
{
auto const& expiresOn = parsedJson[expiresOnPropertyName];
if (expiresOn.is_number_unsigned())
{
try
{
// 'expires_on' as number (posix time representing an absolute timestamp)
auto const value = expiresOn.get<std::int64_t>();
if (value <= MaxPosixTimestamp)
{
accessToken.ExpiresOn = proactiveRenewal
? GetProactiveRenewalDateTime(value)
: PosixTimeConverter::PosixTimeToDateTime(value);
return accessToken;
}
}
catch (std::exception const&)
{
// expiresIn.get<std::int64_t>() has thrown, we may throw later.
}
}
auto const tzOffsetStr = TimeZoneOffsetAsString(utcDiffSeconds);
if (expiresOn.is_string())
{
bool successfulParse = false;
auto const expiresOnAsString = expiresOn.get<std::string>();
for (auto const& parse : {
std::function<DateTime(std::string const&)>([&](auto const& s) {
// 'expires_on' as RFC3339 date string (absolute timestamp)
auto dateTime = DateTime::Parse(s + tzOffsetStr, DateTime::DateFormat::Rfc3339);
return proactiveRenewal ? GetProactiveRenewalDateTime(
PosixTimeConverter::DateTimeToPosixTime(dateTime))
: dateTime;
}),
std::function<DateTime(std::string const&)>([&](auto const& s) {
// 'expires_on' as numeric string (posix time representing an absolute timestamp)
auto value = ParseNumericExpiration(s, MaxPosixTimestamp);
return proactiveRenewal ? GetProactiveRenewalDateTime(value)
: PosixTimeConverter::PosixTimeToDateTime(value);
}),
std::function<DateTime(std::string const&)>([&](auto const& s) {
// 'expires_on' as RFC1123 date string (absolute timestamp)
auto dateTime = DateTime::Parse(s, DateTime::DateFormat::Rfc1123);
return proactiveRenewal ? GetProactiveRenewalDateTime(
PosixTimeConverter::DateTimeToPosixTime(dateTime))
: dateTime;
}),
})
{
try
{
accessToken.ExpiresOn = parse(expiresOnAsString);
// Workaround for Warning C26800 - Use of a moved from object: 'accessToken'
// (lifetime.1) on MSVC version 14.40.33807+.
// Returning accessToken here directly causes the warning.
successfulParse = true;
break;
}
catch (std::exception const&)
{
// parse() has thrown, we may throw later.
}
}
if (successfulParse)
{
return accessToken;
}
}
}
}
ThrowJsonPropertyError(
nonEmptyExpiresOnPropertyNames.back(),
parsedJson,
accessTokenPropertyName,
expiresInPropertyName,
nonEmptyExpiresOnPropertyNames);
}