From e033a0bbfe488dd579c9d4ed5c1d8d01fe3c7f2a Mon Sep 17 00:00:00 2001 From: Jenny Ferries Date: Sat, 12 Apr 2025 16:08:20 -0700 Subject: [PATCH 1/3] Use IList to avoid enumerator allocation --- .../ValidatorBenchmarks.cs | 49 +++++++++++++++++++ .../Validators.cs | 34 ++++++++++--- ...icrosoft.IdentityModel.Tokens.Tests.csproj | 4 +- .../Validation/ValidatorsTests.cs | 17 +++++++ 4 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 benchmark/Microsoft.IdentityModel.Benchmarks/ValidatorBenchmarks.cs diff --git a/benchmark/Microsoft.IdentityModel.Benchmarks/ValidatorBenchmarks.cs b/benchmark/Microsoft.IdentityModel.Benchmarks/ValidatorBenchmarks.cs new file mode 100644 index 0000000000..8a77031c81 --- /dev/null +++ b/benchmark/Microsoft.IdentityModel.Benchmarks/ValidatorBenchmarks.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using BenchmarkDotNet.Attributes; +using Microsoft.IdentityModel.Tokens; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.IdentityModel.Benchmarks +{ + /// + /// Benchmarks for validator methods to measure performance of different implementations + /// + public class ValidatorBenchmarks + { + private List _audiences; + private TokenValidationParameters _parametersWithList; + private TokenValidationParameters _parametersWithEnumerable; + + [GlobalSetup] + public void Setup() + { + _audiences = ["audience1"]; + var validAudiences = new List { "invalid1", "invalid2", "audience1" }; // Make sure audience is last to maximize iteration + + _parametersWithList = new TokenValidationParameters + { + ValidAudiences = validAudiences + }; + + _parametersWithEnumerable = new TokenValidationParameters + { + ValidAudiences = validAudiences.Select(x => x) // Force enumerable by using LINQ + }; + } + + [Benchmark(Baseline = true)] + public void ValidateAudience_WithList() + { + Validators.ValidateAudience(_audiences, null, _parametersWithList); + } + + [Benchmark] + public void ValidateAudience_WithEnumerable() + { + Validators.ValidateAudience(_audiences, null, _parametersWithEnumerable); + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs index 033afa3aea..6512deb41b 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs @@ -143,17 +143,37 @@ private static bool AudienceIsValid(IEnumerable audiences, TokenValidati if (string.IsNullOrWhiteSpace(tokenAudience)) continue; - foreach (string validAudience in validationParametersAudiences) + if (validationParametersAudiences is IList audienceList) { - if (string.IsNullOrWhiteSpace(validAudience)) - continue; + for (int i = 0; i < audienceList.Count; i++) + { + string validAudience = audienceList[i]; + if (string.IsNullOrWhiteSpace(validAudience)) + continue; - if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) + if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); + + return true; + } + } + } + else + { + foreach (string validAudience in validationParametersAudiences) { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); + if (string.IsNullOrWhiteSpace(validAudience)) + continue; + + if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); - return true; + return true; + } } } } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj index 339a012f98..b79e171d64 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs index 9d3ab30aad..2e90c50ee4 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs @@ -270,6 +270,23 @@ public static TheoryData ValidateAudienceTheoryDat Audiences = audiences1WithTwoSlashes, ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), TokenValidationParameters = new TokenValidationParameters{ ValidAudience = audience1 } + }, + new AudienceValidationTheoryData("ValidAudiencesList_OptimizedPath") + { + Audiences = new List { "audience1" }, + TokenValidationParameters = new TokenValidationParameters + { + ValidAudiences = new List { "invalidAudience", "audience1" } // Using List to test IList optimization + } + }, + new AudienceValidationTheoryData("ValidAudiencesList_EmptyList_OptimizedPath") + { + Audiences = new List { "audience1" }, + ExpectedException = ExpectedException.SecurityTokenInvalidAudienceException("IDX10214:"), + TokenValidationParameters = new TokenValidationParameters + { + ValidAudiences = new List() // Empty list should still return false through optimized path + } } }; } From bf63c6d27aea8d1ac8274ba292c324b84903dcc6 Mon Sep 17 00:00:00 2001 From: Jenny Ferries Date: Mon, 14 Apr 2025 10:34:32 -0700 Subject: [PATCH 2/3] Cline addressing PR feedback --- .../Validators.cs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs index 6512deb41b..6537571426 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs @@ -143,37 +143,36 @@ private static bool AudienceIsValid(IEnumerable audiences, TokenValidati if (string.IsNullOrWhiteSpace(tokenAudience)) continue; - if (validationParametersAudiences is IList audienceList) + bool TryMatchAudience(string validAudience) { - for (int i = 0; i < audienceList.Count; i++) + if (string.IsNullOrWhiteSpace(validAudience)) + return false; + + if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) { - string validAudience = audienceList[i]; - if (string.IsNullOrWhiteSpace(validAudience)) - continue; + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); - if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); + return true; + } + + return false; + } + if (validationParametersAudiences is IList audienceList) + { + for (int i = 0; i < audienceList.Count; i++) + { + if (TryMatchAudience(audienceList[i])) return true; - } } } else { foreach (string validAudience in validationParametersAudiences) { - if (string.IsNullOrWhiteSpace(validAudience)) - continue; - - if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); - + if (TryMatchAudience(validAudience)) return true; - } } } } From e0aa7a07fb30558e79a3d5ea82b86d7c0144e305 Mon Sep 17 00:00:00 2001 From: Jenny Ferries Date: Mon, 14 Apr 2025 10:37:23 -0700 Subject: [PATCH 3/3] revert whitespace change --- .../Microsoft.IdentityModel.Tokens.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj index b79e171d64..ae85d7c4b5 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj @@ -21,7 +21,7 @@ - +