Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
105e513
Added a test case to test presence of Signature in JsonWebToken. This…
saurabhsathe-ms May 1, 2025
b5c02e6
Included reference and implemented serialization logic
saurabhsathe-ms May 2, 2025
90d5667
Added a json parser that can parse the stringified claim without brea…
saurabhsathe-ms May 2, 2025
2a0c1b1
Found an ovverriden create token method for our requirement. Removing…
saurabhsathe-ms May 2, 2025
9b0c1bd
Added a check to ensure that the claimvalue is a valid json
saurabhsathe-ms May 2, 2025
b7a2280
Added logic to handle actor token from claims dictionary. Updated tes…
saurabhsathe-ms May 3, 2025
7b65a01
Updated JsonWebTokenHandlerCreateToken.cs to accomodate new changes. …
saurabhsathe-ms May 4, 2025
cd264f5
Removed the print statements and added header
saurabhsathe-ms May 4, 2025
a7b578d
"ActorTokenInClaimsDictionaryShouldBeProperlySerialized" now properly…
saurabhsathe-ms May 6, 2025
7a657e9
Added new testcases, added comments to testcase and modified existing…
saurabhsathe-ms May 6, 2025
04257cd
NIT repairs
saurabhsathe-ms May 6, 2025
d354200
Added one testcase to test MaxActorChainLength values.
saurabhsathe-ms May 6, 2025
ff64ddd
Removed Console.Writeline debugging statements
saurabhsathe-ms May 6, 2025
f66c4d0
Moved the Actor chain length parameter into a Configuration class tha…
saurabhsathe-ms May 7, 2025
5dc4a49
Made the configuration class public and static
saurabhsathe-ms May 7, 2025
ccb0688
Update src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.…
saurabhsathe-ms May 7, 2025
92a6285
Update src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.…
saurabhsathe-ms May 7, 2025
99f7fd5
Repaired testcases as per the suggestions on PR
saurabhsathe-ms May 7, 2025
4185264
Renamed JsonWebTokenConfiguration to JsonWebTokenHandlerConfiguration
saurabhsathe-ms May 7, 2025
8dcbdb3
Removed the comments on testcase
saurabhsathe-ms May 7, 2025
89cd0e4
Updated the configuration summary.
saurabhsathe-ms May 8, 2025
3ad51eb
Moved the MaxActorChainLength to SecurityTokenDescriptor
saurabhsathe-ms May 8, 2025
3f348bc
All test cases passed and introduced JWTclaimTypeName
saurabhsathe-ms May 8, 2025
787099b
Updated JsonWebToken.cs to now use SecurityTokenDescriptor.ActorClaim…
saurabhsathe-ms May 8, 2025
a92b6d6
Introduced a flag that we will be using to turn the feature on or off
saurabhsathe-ms May 9, 2025
36623ea
Cleaned the code and brought it all under one function
saurabhsathe-ms May 12, 2025
e3c25f9
Implemented non static version of ActorClaimName
saurabhsathe-ms May 13, 2025
e20d067
Removed console lines, Added a condition to check if the max actor ch…
saurabhsathe-ms May 13, 2025
d531454
moved actor chain depth to Security Token Descriptor
saurabhsathe-ms May 13, 2025
d79c61b
Updated validation parameters to validate JWT token upto a certain limit
saurabhsathe-ms May 15, 2025
f7de606
Removed old serialization. Added json object serialization and update…
saurabhsathe-ms May 17, 2025
f3c6ed2
Removed the changes to PublicUnshipped files for each frameworks
saurabhsathe-ms May 18, 2025
4185c6f
Added a delegate that users can use to validate their token
saurabhsathe-ms May 19, 2025
4c57aaf
NIT repairs round 1
saurabhsathe-ms May 19, 2025
a585112
Removed everything from TokenValidationParameters and the delegate
saurabhsathe-ms May 20, 2025
e6638ae
Revert "Removed everything from TokenValidationParameters and the del…
saurabhsathe-ms May 21, 2025
95db310
Renamed the switch and removed the validation code
saurabhsathe-ms May 21, 2025
9a1f6ed
Changed delegate name and added the tests to test our new function?
saurabhsathe-ms May 22, 2025
5e10aec
Added a testcase to test if act claim was properly deserialized
saurabhsathe-ms May 22, 2025
3b21635
Updated my local branch with dev
saurabhsathe-ms May 22, 2025
a7398a8
Created delegate and added testcases
saurabhsathe-ms May 23, 2025
a8efcb8
Fixed one bug in Deserialization. Divided all the testcases in 2 part…
saurabhsathe-ms May 23, 2025
4d2770f
NIT repairs, renamed some fields and adjusted some default values as …
saurabhsathe-ms May 23, 2025
bd78d2e
Some more NIT repairs
saurabhsathe-ms May 23, 2025
9503403
Merge branch 'dev' into ssathe/serializeClaimsIdentity
saurabhsathe-ms May 23, 2025
78cc3bc
Updated the code custom actclaimretrievervalidator call with token va…
saurabhsathe-ms May 27, 2025
bbb2f02
Updated summary for our new AppContextSwitch
saurabhsathe-ms May 27, 2025
e7952d1
Removed App context switch replaced it with request based property
saurabhsathe-ms Jun 4, 2025
e3e47ee
NIT updates
saurabhsathe-ms Jun 4, 2025
06b5820
Merge branch 'dev' into ssathe/serializeClaimsIdentity
saurabhsathe-ms Jun 4, 2025
1dfda65
Removed the use of flag during serialization and everything is working
saurabhsathe-ms Jun 7, 2025
6caaae5
Removed the flag from SecurityTokenDescriptor altogether
saurabhsathe-ms Jun 7, 2025
7cbe282
Removed the flag from token validation parameters too. Also added one…
saurabhsathe-ms Jun 7, 2025
8eede12
Latest pull
saurabhsathe-ms Jun 16, 2025
e5ce2bc
Merge branch 'ssathe/serializeClaimsIdentityWithoutFlag' into ssathe/…
saurabhsathe-ms Jun 16, 2025
e28773c
Resolved feedback!
saurabhsathe-ms Jun 19, 2025
badf08a
Change HasKey with TryGetValue for perf improvement
saurabhsathe-ms Jun 19, 2025
2c23923
Merge branch 'dev' into ssathe/serializeClaimsIdentity
saurabhsathe-ms Jun 20, 2025
2ab6ef6
Merge with main
saurabhsathe-ms Sep 30, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ public partial class JsonWebTokenHandler : TokenHandler
{
private static readonly SecurityTokenDescriptor s_emptyTokenDescriptor = new();

private static int _maxActorChainLength = 5; // Default value

/// <summary>
/// Gets or sets the maximum depth allowed when processing nested actor tokens.
/// This prevents excessive recursion when handling deeply nested actor tokens.
/// The value must be at least 0. Value 0 would mean that the actor token is not allowed to be nested.
/// Default value is 5.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the value is less than 1.</exception>
public static int MaxActorChainLength
{
get => _maxActorChainLength;
set
{
if (value < 0)
throw LogHelper.LogExceptionMessage(
new ArgumentOutOfRangeException(nameof(value),
LogHelper.FormatInvariant("IDX14314: MaxActorChainLength must be non negative. Value provided: {0}", value)));

_maxActorChainLength = value;
}
}

/// <summary>
/// Creates an unsigned JSON Web Signature (JWS).
/// </summary>
Expand Down Expand Up @@ -143,7 +166,8 @@ public virtual string CreateToken(SecurityTokenDescriptor tokenDescriptor)
internal static string CreateToken(
SecurityTokenDescriptor tokenDescriptor,
bool setdefaultTimesOnTokenCreation,
int tokenLifetimeInMinutes)
int tokenLifetimeInMinutes,
int actorChainDepth = 0)
{
// The form of a JWS is: Base64UrlEncoding(UTF8(Header)) | . | Base64UrlEncoding(Payload) | . | Base64UrlEncoding(Signature)
// Where the Header is specifically the UTF8 bytes of the JSON, whereas the Payload encoding is not specified, but UTF8 is used by everyone.
Expand Down Expand Up @@ -175,7 +199,8 @@ internal static string CreateToken(
ref writer,
tokenDescriptor,
setdefaultTimesOnTokenCreation,
tokenLifetimeInMinutes);
tokenLifetimeInMinutes,
actorChainDepth);

// mark end of payload
int payloadEnd = (int)utf8ByteMemoryStream.Length;
Expand Down Expand Up @@ -601,12 +626,14 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes
/// <param name="tokenDescriptor">The <see cref="SecurityTokenDescriptor"/> used to create the token.</param>
/// <param name="setDefaultTimesOnTokenCreation">A boolean that controls if expiration, notbefore, issuedat should be added if missing.</param>
/// <param name="tokenLifetimeInMinutes">The default value for the token lifetime in minutes.</param>
/// <param name="actorChainDepth">Controls the recursion length while parsing nested actor tokens</param>
/// <returns>A dictionary of claims.</returns>
internal static void WriteJwsPayload(
ref Utf8JsonWriter writer,
SecurityTokenDescriptor tokenDescriptor,
bool setDefaultTimesOnTokenCreation,
int tokenLifetimeInMinutes)
int tokenLifetimeInMinutes,
int actorChainDepth)
{
bool descriptorClaimsAudienceChecked = false;
bool audienceSet = false;
Expand All @@ -618,6 +645,7 @@ internal static void WriteJwsPayload(
bool iatSet = false;
bool descriptorClaimsNbfChecked = false;
bool nbfSet = false;
bool isActorTokenSet = false;

writer.WriteStartObject();

Expand Down Expand Up @@ -673,6 +701,10 @@ internal static void WriteJwsPayload(
{
foreach (KeyValuePair<string, object> kvp in tokenDescriptor.Claims)
{
if (kvp.Key.Equals(JwtRegisteredClaimNames.Actort, StringComparison.Ordinal))
{
continue;
}
if (!descriptorClaimsAudienceChecked && kvp.Key.Equals(JwtRegisteredClaimNames.Aud, StringComparison.Ordinal))
{
descriptorClaimsAudienceChecked = true;
Expand Down Expand Up @@ -753,9 +785,36 @@ internal static void WriteJwsPayload(

JsonPrimitives.WriteObject(ref writer, kvp.Key, kvp.Value);
}
if (tokenDescriptor.Claims.ContainsKey(JwtRegisteredClaimNames.Actort))
{
// Check for maximum actor chain depth
if (actorChainDepth >= MaxActorChainLength)
{
throw LogHelper.LogExceptionMessage(
new SecurityTokenException(
LogHelper.FormatInvariant(
LogMessages.IDX14313,
LogHelper.MarkAsNonPII(MaxActorChainLength))));
}
if (isActorTokenSet)
{
if (LogHelper.IsEnabled(EventLogLevel.Informational))
LogHelper.LogInformation(LogHelper.FormatInvariant(LogMessages.IDX14113, LogHelper.MarkAsNonPII(nameof(tokenDescriptor.Expires))));

}
ClaimsIdentity actor = tokenDescriptor.Claims[JwtRegisteredClaimNames.Actort] as ClaimsIdentity;
var actorTokenDescriptor = new SecurityTokenDescriptor
{
Subject = actor
};
actorChainDepth = actorChainDepth + 1;
string actorToken = CreateToken(actorTokenDescriptor, false, 0, actorChainDepth);
JsonPrimitives.WriteObject(ref writer, JwtRegisteredClaimNames.Actort, actorToken);
isActorTokenSet = true;
}
}

AddSubjectClaims(ref writer, tokenDescriptor, audienceSet, issuerSet, ref expSet, ref iatSet, ref nbfSet);
AddSubjectClaims(ref writer, tokenDescriptor, audienceSet, issuerSet, ref expSet, ref iatSet, ref nbfSet, ref isActorTokenSet, actorChainDepth);

// By default we set these three properties only if they haven't been detected before.
if (setDefaultTimesOnTokenCreation && !(expSet && iatSet && nbfSet))
Expand Down Expand Up @@ -792,7 +851,9 @@ internal static void AddSubjectClaims(
bool issuerSet,
ref bool expSet,
ref bool iatSet,
ref bool nbfSet)
ref bool nbfSet,
ref bool isActorTokenSet,
int actorChainDepth = 0)
{
if (tokenDescriptor.Subject == null)
return;
Expand All @@ -805,6 +866,26 @@ internal static void AddSubjectClaims(

bool checkClaims = tokenDescriptor.Claims != null && tokenDescriptor.Claims.Count > 0;

if (!isActorTokenSet && tokenDescriptor.Subject.Actor != null)
{
if (actorChainDepth >= MaxActorChainLength)
{
throw LogHelper.LogExceptionMessage(
new SecurityTokenException(
LogHelper.FormatInvariant(
LogMessages.IDX14313,
LogHelper.MarkAsNonPII(MaxActorChainLength))));
}
var actorTokenDescriptor = new SecurityTokenDescriptor
{
Subject = tokenDescriptor.Subject.Actor,
};

string actorToken = CreateToken(actorTokenDescriptor, false, 0, actorChainDepth + 1);
writer.WritePropertyName(JwtRegisteredClaimNames.Actort);
writer.WriteStringValue(actorToken);
isActorTokenSet = true;
}
foreach (Claim claim in tokenDescriptor.Subject.Claims)
{
if (claim == null)
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.IdentityModel.JsonWebTokens/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ internal static class LogMessages
internal const string IDX14310 = "IDX14310: JWE authentication tag is missing.";
internal const string IDX14311 = "IDX14311: Unable to decode the authentication tag as a Base64Url encoded string.";
internal const string IDX14312 = "IDX14312: Unable to decode the cipher text as a Base64Url encoded string.";
internal const string IDX14313 = "IDX14313: Unable to serialize actor token. Actor token chain exceeded maximum depth of {0}";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\..\build\common.props" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.MaxActorChainLength.get -> int
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.MaxActorChainLength.set -> void
Loading
Loading