Skip to content
  •  
  •  
  •  
6 changes: 5 additions & 1 deletion build/platform_and_feature_flags.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<Project>
<!-- System.Text.Json is used on all target frameworks -->
<PropertyGroup>
<DefineConstants>$(DefineConstants);SUPPORTS_SYSTEM_TEXT_JSON</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == '$(TargetFrameworkNetCore)' or '$(TargetFramework)' == '$(TargetFrameworkNet)'">
<DefineConstants>$(DefineConstants);SUPPORTS_SYSTEM_TEXT_JSON;NET_CORE;SUPPORTS_CONFIDENTIAL_CLIENT;SUPPORTS_CUSTOM_CACHE;SUPPORTS_BROKER;SUPPORTS_WIN32;</DefineConstants>
<DefineConstants>$(DefineConstants);NET_CORE;SUPPORTS_CONFIDENTIAL_CLIENT;SUPPORTS_CUSTOM_CACHE;SUPPORTS_BROKER;SUPPORTS_WIN32;</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == '$(TargetFrameworkNet)' or '$(TargetFramework)' == '$(TargetFrameworkNetDesktop462)' or '$(TargetFramework)' == '$(TargetFrameworkNetDesktop472)' or '$(TargetFramework)' == '$(TargetFrameworkNetStandard)'">
<DefineConstants>$(DefineConstants);SUPPORTS_OTEL;</DefineConstants>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.Identity.Client.Utils;

namespace Microsoft.Identity.Client.KeyAttestation.Attestation
Expand Down Expand Up @@ -42,6 +43,24 @@ internal static bool TryExtractExpirationClaim(string jwt, out DateTimeOffset ex
return false;

// Parse the exp claim (Unix timestamp in seconds)
// STJ deserializes numbers in Dictionary<string,object> as JsonElement, not long
if (expObj is JsonElement jsonElement)
{
if (jsonElement.TryGetInt64(out long elementLong))
{
expiresOn = DateTimeOffset.FromUnixTimeSeconds(elementLong);
return true;
}
// Handle number-as-string in JSON
if (jsonElement.ValueKind == JsonValueKind.String &&
long.TryParse(jsonElement.GetString(), out long parsedFromElement))
{
expiresOn = DateTimeOffset.FromUnixTimeSeconds(parsedFromElement);
return true;
}
}

// Fallback: direct long (shouldn't happen with STJ but kept for safety)
if (expObj is long expLong)
{
expiresOn = DateTimeOffset.FromUnixTimeSeconds(expLong);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@
using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.Utils;
using Microsoft.IdentityModel.Abstractions;
#if SUPPORTS_SYSTEM_TEXT_JSON
using System.Text.Json;
#else
using Microsoft.Identity.Json;
#endif

namespace Microsoft.Identity.Client
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@
using Microsoft.Identity.Client.Http.Retry;
using Microsoft.Identity.Client.ManagedIdentity.V2;

#if SUPPORTS_SYSTEM_TEXT_JSON
using System.Text.Json;
#else
using Microsoft.Identity.Json;
#endif

namespace Microsoft.Identity.Client
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.Identity.Client.Utils;
#if SUPPORTS_SYSTEM_TEXT_JSON
using JObject = System.Text.Json.Nodes.JsonObject;
#else
using Microsoft.Identity.Json.Linq;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have enough unit tests / or did we manual test to ensure we are still good in net462/472/netstandard2.0? an nothing is broken?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

net462/472/netstandard2.0 aren't targeted by any test project in this repo - that gap predates this PR. The good news is that STJ 6.0.10 itself targets netstandard2.0, so its behavior is consistent across all these TFMs. The net48 unit tests exercise all the same code paths (JsonHelper, cache serialization, IdToken parsing, etc.) and all pass. Happy to add a follow-up PR that adds net462/net472 to the unit test TargetFrameworks if you'd like explicit coverage.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to add a follow-up PR that adds net462/net472 to the unit test TargetFrameworks if you'd like explicit coverage.

I don't think we should do this, but we need a test pass on key features.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran the unit tests locally targeting net462 (1,248 passed) and net472 (1,845 passed) by temporarily adding those TFMs — all passing, no JSON-related failures.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is what @bgavrilMS suggested to do for the validation.

#endif

namespace Microsoft.Identity.Client
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,8 @@
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.Utils;
#if SUPPORTS_SYSTEM_TEXT_JSON
using JObject = System.Text.Json.Nodes.JsonObject;
using JToken = System.Text.Json.Nodes.JsonNode;
#else
using Microsoft.Identity.Json;
using Microsoft.Identity.Json.Linq;
#endif

namespace Microsoft.Identity.Client.AuthScheme.PoP
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#if SUPPORTS_SYSTEM_TEXT_JSON
using Microsoft.Identity.Client.Platforms.net;
using JsonProperty = System.Text.Json.Serialization.JsonIncludeAttribute;
#else
using Microsoft.Identity.Json;
#endif

namespace Microsoft.Identity.Client.Cache
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
// Licensed under the MIT License.

using Microsoft.Identity.Client.Utils;
#if SUPPORTS_SYSTEM_TEXT_JSON
using Microsoft.Identity.Client.Platforms.net;
#else
using Microsoft.Identity.Json;
#endif

namespace Microsoft.Identity.Client.Cache
{
Expand All @@ -15,22 +11,13 @@ namespace Microsoft.Identity.Client.Cache
internal class AdalResultWrapper
{

#if !SUPPORTS_SYSTEM_TEXT_JSON
[JsonProperty]
#endif
public AdalResult Result { get; set; }

#if !SUPPORTS_SYSTEM_TEXT_JSON
[JsonProperty]
#endif
public string RawClientInfo { get; set; }

/// <summary>
/// Gets the Refresh Token associated with the requested Access Token. Note: not all operations will return a Refresh Token.
/// </summary>
#if !SUPPORTS_SYSTEM_TEXT_JSON
[JsonProperty]
#endif
public string RefreshToken { get; set; }

/// <summary>
Expand All @@ -40,9 +27,6 @@ internal class AdalResultWrapper

// This is only needed for AcquireTokenByAuthorizationCode in which parameter resource is optional and we need
// to get it from the STS response.
#if !SUPPORTS_SYSTEM_TEXT_JSON
[JsonProperty]
#endif
internal string ResourceInResponse { get; set; }

/// <summary>
Expand All @@ -63,9 +47,6 @@ public string Serialize()
return JsonHelper.SerializeToJson(this);
}

#if !SUPPORTS_SYSTEM_TEXT_JSON
[JsonProperty]
#endif
public string UserAssertionHash { get; set; }

internal AdalResultWrapper Clone()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
// Licensed under the MIT License.

using System;
#if SUPPORTS_SYSTEM_TEXT_JSON
using Microsoft.Identity.Client.Platforms.net;
using JsonProperty = System.Text.Json.Serialization.JsonIncludeAttribute;
#else
using Microsoft.Identity.Json;
#endif

namespace Microsoft.Identity.Client.Cache
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
// Licensed under the MIT License.

using System.Collections.Generic;
#if SUPPORTS_SYSTEM_TEXT_JSON
using JToken = System.Text.Json.Nodes.JsonNode;
#else
using Microsoft.Identity.Json.Linq;
#endif

namespace Microsoft.Identity.Client.Cache
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
#if SUPPORTS_SYSTEM_TEXT_JSON
using System.Text.Json;
using System.Text.Json.Nodes;
using JObject = System.Text.Json.Nodes.JsonObject;
using JToken = System.Text.Json.Nodes.JsonNode;
#else
using Microsoft.Identity.Json;
using Microsoft.Identity.Json.Linq;
#endif

namespace Microsoft.Identity.Client.Cache.Items
{
internal class CacheSerializationContract
{
#if SUPPORTS_SYSTEM_TEXT_JSON
private static readonly JsonSerializerOptions NeverIgnoreJsonOptions = new()
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.Never
};
#endif

private static readonly IEnumerable<string> s_knownPropertyNames = new[] {
StorageJsonValues.CredentialTypeAccessToken,
Expand Down Expand Up @@ -56,14 +49,10 @@ public CacheSerializationContract(IDictionary<string, JToken> unknownNodes)

internal static CacheSerializationContract FromJsonString(string json)
{
#if SUPPORTS_SYSTEM_TEXT_JSON
var root = JsonNode.Parse(json, documentOptions: new JsonDocumentOptions
{
AllowTrailingCommas = true
}).AsObject();
#else
var root = JObject.Parse(json);
#endif
var unknownNodes = ExtractUnknownNodes(root);

var contract = new CacheSerializationContract(unknownNodes);
Expand Down Expand Up @@ -136,32 +125,18 @@ internal static CacheSerializationContract FromJsonString(string json)
return contract;

// private method for enumerating collection
#if SUPPORTS_SYSTEM_TEXT_JSON
static IEnumerable<JsonObject> GetElement(JsonObject root, string key)
{
foreach (var token in root[key].AsObject())
{
yield return token.Value as JObject;
}
}
#else
static IEnumerable<JObject> GetElement(JObject root, string key)
{
foreach (var token in root[key].Values())
{
yield return token as JObject;
}
}
#endif
}

private static IDictionary<string, JToken> ExtractUnknownNodes(JObject root)
{
#if SUPPORTS_SYSTEM_TEXT_JSON
return root
#else
return (root as IDictionary<string, JToken>)
#endif
.Where(kvp => !s_knownPropertyNames.Any(p => string.Equals(kvp.Key, p, StringComparison.OrdinalIgnoreCase)))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
Expand Down Expand Up @@ -218,23 +193,9 @@ internal string ToJsonString()
// Anything else
foreach (var kvp in UnknownNodes)
{
#if SUPPORTS_SYSTEM_TEXT_JSON
root[kvp.Key] = kvp.Value != null ? JToken.Parse(kvp.Value.ToJsonString()) : null;
#else
root[kvp.Key] = kvp.Value;
#endif
}
#if SUPPORTS_SYSTEM_TEXT_JSON
return root.ToJsonString(NeverIgnoreJsonOptions);
#else
return JsonConvert.SerializeObject(
root,
Formatting.None,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include
});
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.Utils;

#if SUPPORTS_SYSTEM_TEXT_JSON
using System.Text.Json.Nodes;
using System.Text.Json;
using JObject = System.Text.Json.Nodes.JsonObject;
#else
using Microsoft.Identity.Json.Linq;
#endif

namespace Microsoft.Identity.Client.Cache.Items
{
Expand Down Expand Up @@ -65,11 +61,7 @@ internal MsalAccessTokenCacheItem(
#if !MOBILE
private IDictionary<string, string> AcquireCacheParametersFromResponse(
IEnumerable<string> persistedCacheParameters,
#if SUPPORTS_SYSTEM_TEXT_JSON
Dictionary<string, JsonElement> extraDataFromResponse)
#else
Dictionary<string, JToken> extraDataFromResponse)
#endif
{
if (persistedCacheParameters == null || !persistedCacheParameters.Any())
{
Expand All @@ -78,14 +70,7 @@ private IDictionary<string, string> AcquireCacheParametersFromResponse(

var cacheParameters = extraDataFromResponse?
.Where(x => persistedCacheParameters.Contains(x.Key, StringComparer.InvariantCultureIgnoreCase))
#if SUPPORTS_SYSTEM_TEXT_JSON
.ToDictionary(x => x.Key, x => x.Value.ToString());
#else
//Avoid formatting arrays because it adds new lines after every element
.ToDictionary(x => x.Key, x => x.Value.Type == JTokenType.Array || x.Value.Type == JTokenType.Object ?
x.Value.ToString(Json.Formatting.None) :
x.Value.ToString());
#endif
return cacheParameters;
}

Expand Down Expand Up @@ -398,7 +383,6 @@ private void StoreDictionaryInJson(JObject json, string key, IDictionary<string,
{
if (values != null)
{
#if SUPPORTS_SYSTEM_TEXT_JSON
var obj = new JsonObject();

foreach (KeyValuePair<string, string> value in values)
Expand All @@ -407,9 +391,6 @@ private void StoreDictionaryInJson(JObject json, string key, IDictionary<string,
}

json[key] = obj;
#else
SetItemIfValueNotNull(json, key, JObject.FromObject(values));
#endif
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@
using Microsoft.Identity.Client.Cache.Keys;
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.Utils;
#if SUPPORTS_SYSTEM_TEXT_JSON
using System.Text.Json.Nodes;
using JObject = System.Text.Json.Nodes.JsonObject;
#else
using Microsoft.Identity.Json.Linq;
#endif

namespace Microsoft.Identity.Client.Cache.Items
{
Expand Down Expand Up @@ -236,7 +232,6 @@ internal override JObject ToJObject()
SetItemIfValueNotNull(json, StorageJsonKeys.AccountSource, AccountSource);
if (WamAccountIds != null && WamAccountIds.Any())
{
#if SUPPORTS_SYSTEM_TEXT_JSON
var obj = new JsonObject();

foreach (KeyValuePair<string, string> accId in WamAccountIds)
Expand All @@ -245,9 +240,6 @@ internal override JObject ToJObject()
}

json[StorageJsonKeys.WamAccountIds] = obj;
#else
json[StorageJsonKeys.WamAccountIds] = JObject.FromObject(WamAccountIds);
#endif
}

return json;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
using System.Collections.Generic;
using Microsoft.Identity.Client.Cache.Keys;
using Microsoft.Identity.Client.Utils;
#if SUPPORTS_SYSTEM_TEXT_JSON
using JObject = System.Text.Json.Nodes.JsonObject;
#else
using Microsoft.Identity.Json.Linq;
#endif

namespace Microsoft.Identity.Client.Cache.Items
{
Expand Down
Loading
Loading