From 11e704d124801fcacfaf363fa9a6bfeb2b6d7f31 Mon Sep 17 00:00:00 2001 From: Brett Hazen <2651260+bhazen@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:50:23 -0500 Subject: [PATCH 1/3] Added server start time and current time to provide more context to info in diagnostic dump --- .../DependencyInjection/BuilderExtensions/Core.cs | 6 +++++- .../Licensing/V2/Diagnostics/DiagnosticContext.cs | 6 ++++++ .../AssemblyInfoDiagnosticEntry.cs | 2 +- .../AuthSchemeInfoDiagnosticEntry.cs | 2 +- .../BasicServerInfoDiagnosticEntry.cs | 4 +++- .../DiagnosticEntries/ClientInfoDiagnosticEntry.cs | 2 +- .../DataProtectionDiagnosticEntry.cs | 2 +- .../EndpointUsageDiagnosticEntry.cs | 2 +- .../IdentityServerOptionsDiagnosticEntry.cs | 2 +- .../LicenseUsageDiagnosticEntry.cs | 2 +- .../RegisteredImplementationsDiagnosticEntry.cs | 2 +- .../ResourceInfoDiagnosticEntry.cs | 2 +- .../TokenIssueCountDiagnosticEntry.cs | 2 +- .../Licensing/V2/Diagnostics/DiagnosticSummary.cs | 6 ++++-- .../Licensing/V2/Diagnostics/IDiagnosticEntry.cs | 2 +- .../BasicServerInfoDiagnosticEntryTests.cs | 6 +++++- .../DiagnosticEntries/DiagnosticEntryTestHelper.cs | 4 ++-- .../Licensing/v2/DiagnosticSummaryTests.cs | 14 +++++++------- 18 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticContext.cs diff --git a/identity-server/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs b/identity-server/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs index 874f1815a..50ed0d3b0 100644 --- a/identity-server/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs +++ b/identity-server/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs @@ -228,7 +228,11 @@ public static class IdentityServerBuilderExtensionsCore builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(serviceProvider => new DiagnosticSummary( + DateTime.UtcNow, + serviceProvider.GetServices(), + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService())); builder.Services.AddHostedService(); return builder; diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticContext.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticContext.cs new file mode 100644 index 000000000..0552ff033 --- /dev/null +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticContext.cs @@ -0,0 +1,6 @@ +// Copyright (c) Duende Software. All rights reserved. +// See LICENSE in the project root for license information. + +namespace Duende.IdentityServer.Licensing.V2.Diagnostics; + +public record DiagnosticContext(DateTime ServerStartTime, DateTime CurrentSeverTime); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AssemblyInfoDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AssemblyInfoDiagnosticEntry.cs index 30719d68e..7ece50ac6 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AssemblyInfoDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AssemblyInfoDiagnosticEntry.cs @@ -39,7 +39,7 @@ internal class AssemblyInfoDiagnosticEntry : IDiagnosticEntry _startsWithMatches = startsWithMatches ?? _defaultStartsWithMatches; } - public Task WriteAsync(Utf8JsonWriter writer) + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { var assemblies = GetAssemblyInfo(); writer.WriteStartObject("AssemblyInfo"); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AuthSchemeInfoDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AuthSchemeInfoDiagnosticEntry.cs index 70af5a8f8..40277db4e 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AuthSchemeInfoDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/AuthSchemeInfoDiagnosticEntry.cs @@ -8,7 +8,7 @@ namespace Duende.IdentityServer.Licensing.V2.Diagnostics.DiagnosticEntries; internal class AuthSchemeInfoDiagnosticEntry(IAuthenticationSchemeProvider authenticationSchemeProvider) : IDiagnosticEntry { - public async Task WriteAsync(Utf8JsonWriter writer) + public async Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { var schemes = await authenticationSchemeProvider.GetAllSchemesAsync(); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/BasicServerInfoDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/BasicServerInfoDiagnosticEntry.cs index ba5ad55a2..2f4d7db17 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/BasicServerInfoDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/BasicServerInfoDiagnosticEntry.cs @@ -7,11 +7,13 @@ namespace Duende.IdentityServer.Licensing.V2.Diagnostics.DiagnosticEntries; internal class BasicServerInfoDiagnosticEntry(Func hostNameResolver) : IDiagnosticEntry { - public Task WriteAsync(Utf8JsonWriter writer) + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { writer.WriteStartObject("BasicServerInfo"); writer.WriteString("HostName", hostNameResolver()); + writer.WriteString("ServerStartTime", context.ServerStartTime.ToString("o")); + writer.WriteString("CurrentServerTime", context.CurrentSeverTime.ToString("o")); writer.WriteEndObject(); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/ClientInfoDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/ClientInfoDiagnosticEntry.cs index 122d5f60a..227344547 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/ClientInfoDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/ClientInfoDiagnosticEntry.cs @@ -13,7 +13,7 @@ internal class ClientInfoDiagnosticEntry(ClientLoadedTracker clientLoadedTracker WriteIndented = false }; - public Task WriteAsync(Utf8JsonWriter writer) + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { writer.WriteStartArray("Clients"); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/DataProtectionDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/DataProtectionDiagnosticEntry.cs index 571e47e61..917bd1029 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/DataProtectionDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/DataProtectionDiagnosticEntry.cs @@ -10,7 +10,7 @@ namespace Duende.IdentityServer.Licensing.V2.Diagnostics.DiagnosticEntries; internal class DataProtectionDiagnosticEntry(IOptions dataProtectionOptions, IOptions keyManagementOptions) : IDiagnosticEntry { - public Task WriteAsync(Utf8JsonWriter writer) + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { writer.WriteStartObject("DataProtectionConfiguration"); writer.WriteString("ApplicationDiscriminator", dataProtectionOptions?.Value?.ApplicationDiscriminator ?? "Not Configured"); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/EndpointUsageDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/EndpointUsageDiagnosticEntry.cs index 183223e12..9bff2c916 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/EndpointUsageDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/EndpointUsageDiagnosticEntry.cs @@ -45,7 +45,7 @@ internal class EndpointUsageDiagnosticEntry : IDiagnosticEntry _meterListener.Start(); } - public Task WriteAsync(Utf8JsonWriter writer) + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { writer.WriteStartObject("EndpointUsage"); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/IdentityServerOptionsDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/IdentityServerOptionsDiagnosticEntry.cs index 0bd818446..0d65a1b38 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/IdentityServerOptionsDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/IdentityServerOptionsDiagnosticEntry.cs @@ -23,7 +23,7 @@ internal class IdentityServerOptionsDiagnosticEntry(IOptions entries, IdentityServerOptions options, ILoggerFactory loggerFactory) +internal class DiagnosticSummary(DateTime serverStartTime, IEnumerable entries, IdentityServerOptions options, ILoggerFactory loggerFactory) { private readonly ILogger _logger = loggerFactory.CreateLogger("Duende.IdentityServer.Diagnostics.Summary"); + public async Task PrintSummary() { var bufferWriter = new ArrayBufferWriter(); @@ -19,9 +20,10 @@ internal class DiagnosticSummary(IEnumerable entries, Identity writer.WriteStartObject(); + var diagnosticContext = new DiagnosticContext(serverStartTime, DateTime.UtcNow); foreach (var diagnosticEntry in entries) { - await diagnosticEntry.WriteAsync(writer); + await diagnosticEntry.WriteAsync(diagnosticContext, writer); } writer.WriteEndObject(); diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/IDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/IDiagnosticEntry.cs index 22114e69a..a44640da4 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/IDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/IDiagnosticEntry.cs @@ -7,5 +7,5 @@ namespace Duende.IdentityServer.Licensing.V2.Diagnostics; internal interface IDiagnosticEntry { - Task WriteAsync(Utf8JsonWriter writer); + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer); } diff --git a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/BasicServerInfoDiagnosticEntryTests.cs b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/BasicServerInfoDiagnosticEntryTests.cs index 46eebaeda..b9f464ea8 100644 --- a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/BasicServerInfoDiagnosticEntryTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/BasicServerInfoDiagnosticEntryTests.cs @@ -12,11 +12,15 @@ public class BasicServerInfoDiagnosticEntryTests public async Task WriteAsync_ShouldWriteBasicServerInfo() { const string expectedHostName = "testing.local"; + var expectedServerStartTime = DateTime.UtcNow.AddMinutes(-5); + var expectedCurrentServerTime = DateTime.UtcNow; var subject = new BasicServerInfoDiagnosticEntry(() => expectedHostName); - var result = await DiagnosticEntryTestHelper.WriteEntryToJson(subject); + var result = await DiagnosticEntryTestHelper.WriteEntryToJson(subject, expectedServerStartTime, expectedCurrentServerTime); var basicServerInfo = result.RootElement.GetProperty("BasicServerInfo"); basicServerInfo.GetProperty("HostName").GetString().ShouldBe(expectedHostName); + basicServerInfo.GetProperty("ServerStartTime").GetString().ShouldBe(expectedServerStartTime.ToString("o")); + basicServerInfo.GetProperty("CurrentServerTime").GetString().ShouldBe(expectedCurrentServerTime.ToString("o")); } } diff --git a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/DiagnosticEntryTestHelper.cs b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/DiagnosticEntryTestHelper.cs index 0c6a74f06..9147a1bc5 100644 --- a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/DiagnosticEntryTestHelper.cs +++ b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/DiagnosticEntryTestHelper.cs @@ -10,14 +10,14 @@ namespace IdentityServer.UnitTests.Licensing.V2.DiagnosticEntries; internal static class DiagnosticEntryTestHelper { - public static async Task WriteEntryToJson(IDiagnosticEntry subject) + public static async Task WriteEntryToJson(IDiagnosticEntry subject, DateTime? serverStartTime = null, DateTime? currentServerTime = null) { var bufferWriter = new ArrayBufferWriter(); await using var writer = new Utf8JsonWriter(bufferWriter, new JsonWriterOptions { Indented = false }); writer.WriteStartObject(); - await subject.WriteAsync(writer); + await subject.WriteAsync(new DiagnosticContext(serverStartTime ?? DateTime.UtcNow.AddMinutes(-5), currentServerTime ?? DateTime.UtcNow), writer); writer.WriteEndObject(); await writer.FlushAsync(); diff --git a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticSummaryTests.cs b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticSummaryTests.cs index 00a334ebd..ab3ae75bd 100644 --- a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticSummaryTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticSummaryTests.cs @@ -25,7 +25,7 @@ public class DiagnosticSummaryTests secondDiagnosticEntry, thirdDiagnosticEntry }; - var summary = new DiagnosticSummary(entries, new IdentityServerOptions(), new StubLoggerFactory(logger)); + var summary = new DiagnosticSummary(DateTime.UtcNow, entries, new IdentityServerOptions(), new StubLoggerFactory(logger)); await summary.PrintSummary(); @@ -42,7 +42,7 @@ public class DiagnosticSummaryTests var logger = new FakeLogger(); var diagnosticEntry = new LongDiagnosticEntry { OutputLength = chunkSize * 2 }; - var summary = new DiagnosticSummary([diagnosticEntry], options, new StubLoggerFactory(logger)); + var summary = new DiagnosticSummary(DateTime.UtcNow, [diagnosticEntry], options, new StubLoggerFactory(logger)); await summary.PrintSummary(); @@ -61,7 +61,7 @@ public class DiagnosticSummaryTests var logger = new FakeLogger(); var diagnosticEntry = new LongDiagnosticEntry { OutputLength = 2, OutputCharacter = '€' }; - var summary = new DiagnosticSummary([diagnosticEntry], options, new StubLoggerFactory(logger)); + var summary = new DiagnosticSummary(DateTime.UtcNow, [diagnosticEntry], options, new StubLoggerFactory(logger)); await summary.PrintSummary(); @@ -76,7 +76,7 @@ public class DiagnosticSummaryTests var logger = new FakeLogger(); var diagnosticEntry = new LongDiagnosticEntry { OutputLength = options.Diagnostics.ChunkSize * 2 }; - var summary = new DiagnosticSummary([diagnosticEntry], options, new StubLoggerFactory(logger)); + var summary = new DiagnosticSummary(DateTime.UtcNow, [diagnosticEntry], options, new StubLoggerFactory(logger)); await summary.PrintSummary(); foreach (var entry in logger.Collector.GetSnapshot()) @@ -91,7 +91,7 @@ public class DiagnosticSummaryTests var options = new IdentityServerOptions(); var logger = new FakeLogger(); var diagnosticEntry = new LongDiagnosticEntry { OutputLength = 100000 }; - var summary = new DiagnosticSummary([diagnosticEntry], options, new StubLoggerFactory(logger)); + var summary = new DiagnosticSummary(DateTime.UtcNow, [diagnosticEntry], options, new StubLoggerFactory(logger)); await summary.PrintSummary(); @@ -103,7 +103,7 @@ public class DiagnosticSummaryTests private class TestDiagnosticEntry : IDiagnosticEntry { public bool WasCalled { get; private set; } - public Task WriteAsync(Utf8JsonWriter writer) + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { WasCalled = true; return Task.CompletedTask; @@ -115,7 +115,7 @@ public class DiagnosticSummaryTests public int OutputLength { get; set; } public char OutputCharacter { get; set; } = 'x'; - public Task WriteAsync(Utf8JsonWriter writer) + public Task WriteAsync(DiagnosticContext context, Utf8JsonWriter writer) { writer.WriteString("test", new string(OutputCharacter, OutputLength)); return Task.CompletedTask; From 11ac95a21395142095c3cc6d77f2452b7f58f86c Mon Sep 17 00:00:00 2001 From: Brett Hazen <2651260+bhazen@users.noreply.github.com> Date: Tue, 22 Jul 2025 15:01:06 -0500 Subject: [PATCH 2/3] Added refresh token as a distinct grant type count --- .../DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs | 4 ++++ identity-server/src/Storage/Models/GrantType.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs index e685df259..7109fd4e5 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs @@ -25,6 +25,7 @@ internal class TokenIssueCountDiagnosticEntry : IDiagnosticEntry private long _clientCredentialsGrantTypeFlows; private long _resourceOwnerPasswordGrantTypeFlows; private long _deviceFlowGrantTypeFlows; + private long _refreshTokenGrantTypeFlows; private long _otherGrantTypeFlows; private readonly MeterListener _meterListener; @@ -178,6 +179,9 @@ internal class TokenIssueCountDiagnosticEntry : IDiagnosticEntry case GrantType.DeviceFlow: Interlocked.Increment(ref _deviceFlowGrantTypeFlows); break; + case GrantType.RefreshToken: + Interlocked.Increment(ref _refreshTokenGrantTypeFlows); + break; default: Interlocked.Increment(ref _otherGrantTypeFlows); break; diff --git a/identity-server/src/Storage/Models/GrantType.cs b/identity-server/src/Storage/Models/GrantType.cs index b67ec0d17..48e96fbbc 100644 --- a/identity-server/src/Storage/Models/GrantType.cs +++ b/identity-server/src/Storage/Models/GrantType.cs @@ -14,4 +14,5 @@ public static class GrantType public const string ClientCredentials = "client_credentials"; public const string ResourceOwnerPassword = "password"; public const string DeviceFlow = "urn:ietf:params:oauth:grant-type:device_code"; + public const string RefreshToken = "refresh_token"; } From 9509218d9b709858ceba3248f73503e64c443c9a Mon Sep 17 00:00:00 2001 From: Brett Hazen <2651260+bhazen@users.noreply.github.com> Date: Tue, 22 Jul 2025 15:29:45 -0500 Subject: [PATCH 3/3] Reworked structure of token issue count diagnostic entry based on feedback --- .../TokenIssueCountDiagnosticEntry.cs | 61 +++++++------ .../TokenIssueCountDiagnosticEntryTests.cs | 85 +++++++++++-------- 2 files changed, 85 insertions(+), 61 deletions(-) diff --git a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs index 7109fd4e5..ee7a18ce6 100644 --- a/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs +++ b/identity-server/src/IdentityServer/Licensing/V2/Diagnostics/DiagnosticEntries/TokenIssueCountDiagnosticEntry.cs @@ -13,12 +13,12 @@ internal class TokenIssueCountDiagnosticEntry : IDiagnosticEntry private long _jwtTokenIssued; private long _referenceTokenIssued; private long _refreshTokenIssued; - private long _jwtDPoPTokenIssued; - private long _referenceDPoPTokenIssued; - private long _jwtMTLSTokenIssued; - private long _referenceMTLSTokenIssued; private long _idTokenIssued; + private long _tokensWithNoConstraint; + private long _tokensWithDPoPConstraint; + private long _tokensWithMtlsConstraint; + private long _implicitGrantTypeFlows; private long _hybridGrantTypeFlows; private long _authorizationCodeGrantTypeFlows; @@ -52,21 +52,33 @@ internal class TokenIssueCountDiagnosticEntry : IDiagnosticEntry writer.WritePropertyName("TokenIssueCounts"); writer.WriteStartObject(); - writer.WriteNumber("Jwt", _jwtTokenIssued); - writer.WriteNumber("Reference", _referenceTokenIssued); - writer.WriteNumber("JwtDPoP", _jwtDPoPTokenIssued); - writer.WriteNumber("ReferenceDPoP", _referenceDPoPTokenIssued); - writer.WriteNumber("JwtMTLS", _jwtMTLSTokenIssued); - writer.WriteNumber("ReferenceMTLS", _referenceMTLSTokenIssued); - writer.WriteNumber("Refresh", _refreshTokenIssued); - writer.WriteNumber("Id", _idTokenIssued); + writer.WriteStartObject("RequestsByGrantType"); writer.WriteNumber(GrantType.Implicit, _implicitGrantTypeFlows); writer.WriteNumber(GrantType.Hybrid, _hybridGrantTypeFlows); writer.WriteNumber(GrantType.AuthorizationCode, _authorizationCodeGrantTypeFlows); writer.WriteNumber(GrantType.ClientCredentials, _clientCredentialsGrantTypeFlows); writer.WriteNumber(GrantType.ResourceOwnerPassword, _resourceOwnerPasswordGrantTypeFlows); writer.WriteNumber(GrantType.DeviceFlow, _deviceFlowGrantTypeFlows); + writer.WriteNumber(GrantType.RefreshToken, _refreshTokenGrantTypeFlows); writer.WriteNumber("Other", _otherGrantTypeFlows); + writer.WriteEndObject(); + + writer.WriteStartObject("AccessTokensByType"); + writer.WriteNumber("Jwt", _jwtTokenIssued); + writer.WriteNumber("Reference", _referenceTokenIssued); + writer.WriteEndObject(); + + writer.WriteStartObject("AccessTokensBySenderConstraint"); + writer.WriteNumber("None", _tokensWithNoConstraint); + writer.WriteNumber("DPoP", _tokensWithDPoPConstraint); + writer.WriteNumber("mTLS", _tokensWithMtlsConstraint); + writer.WriteEndObject(); + + writer.WriteStartObject("TokensByType"); + writer.WriteNumber("Access", _jwtTokenIssued + _referenceTokenIssued); + writer.WriteNumber("Refresh", _refreshTokenIssued); + writer.WriteNumber("Id", _idTokenIssued); + writer.WriteEndObject(); writer.WriteEndObject(); @@ -120,25 +132,26 @@ internal class TokenIssueCountDiagnosticEntry : IDiagnosticEntry if (accessTokenIssued) { - switch (proofType) + switch (accessTokenType) { - case ProofType.None when accessTokenType == AccessTokenType.Jwt: + case AccessTokenType.Jwt: Interlocked.Increment(ref _jwtTokenIssued); break; - case ProofType.None when accessTokenType == AccessTokenType.Reference: + case AccessTokenType.Reference: Interlocked.Increment(ref _referenceTokenIssued); break; - case ProofType.DPoP when accessTokenType == AccessTokenType.Jwt: - Interlocked.Increment(ref _jwtDPoPTokenIssued); + } + + switch (proofType) + { + case ProofType.None: + Interlocked.Increment(ref _tokensWithNoConstraint); break; - case ProofType.DPoP when accessTokenType == AccessTokenType.Reference: - Interlocked.Increment(ref _referenceDPoPTokenIssued); + case ProofType.ClientCertificate: + Interlocked.Increment(ref _tokensWithMtlsConstraint); break; - case ProofType.ClientCertificate when accessTokenType == AccessTokenType.Jwt: - Interlocked.Increment(ref _jwtMTLSTokenIssued); - break; - case ProofType.ClientCertificate when accessTokenType == AccessTokenType.Reference: - Interlocked.Increment(ref _referenceMTLSTokenIssued); + case ProofType.DPoP: + Interlocked.Increment(ref _tokensWithDPoPConstraint); break; } } diff --git a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/TokenIssueCountDiagnosticEntryTests.cs b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/TokenIssueCountDiagnosticEntryTests.cs index 76dc13cf2..e1040e1ce 100644 --- a/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/TokenIssueCountDiagnosticEntryTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Licensing/v2/DiagnosticEntries/TokenIssueCountDiagnosticEntryTests.cs @@ -18,7 +18,7 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("Jwt").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("AccessTokensByType").GetProperty("Jwt").GetInt64().ShouldBe(1); } [Fact] @@ -28,67 +28,67 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("Reference").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("AccessTokensByType").GetProperty("Reference").GetInt64().ShouldBe(1); } [Fact] - public async Task Should_Count_JwtDPoPToken() + public async Task Should_Count_DPoP_Constraint_For_DPoP_Constrained_JWT() { IssueToken(GrantType.AuthorizationCode, true, AccessTokenType.Jwt, false, ProofType.DPoP, false); var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("JwtDPoP").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("AccessTokensBySenderConstraint").GetProperty("DPoP").GetInt64().ShouldBe(1); } [Fact] - public async Task Should_Count_ReferenceDPoPToken() + public async Task Should_Count_DPoP_Constraint_For_DPoP_Constrained_Reference_Token() { IssueToken(GrantType.AuthorizationCode, true, AccessTokenType.Reference, false, ProofType.DPoP, false); var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("ReferenceDPoP").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("AccessTokensBySenderConstraint").GetProperty("DPoP").GetInt64().ShouldBe(1); } [Fact] - public async Task Should_Count_JwtMTlsToken() + public async Task Should_Count_mTLS_Constraint_For_mTLS_Constrained_JWT() { IssueToken(GrantType.AuthorizationCode, true, AccessTokenType.Jwt, false, ProofType.ClientCertificate, false); var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("JwtMTLS").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("AccessTokensBySenderConstraint").GetProperty("mTLS").GetInt64().ShouldBe(1); } [Fact] - public async Task Should_Count_ReferenceMTlsToken() + public async Task Should_Count_mTLS_Constraint_For_mTLS_Constrained_Reference_Token() { IssueToken(GrantType.AuthorizationCode, true, AccessTokenType.Reference, false, ProofType.ClientCertificate, false); var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("ReferenceMTLS").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("AccessTokensBySenderConstraint").GetProperty("mTLS").GetInt64().ShouldBe(1); } [Fact] - public async Task Should_Count_RefreshToken() + public async Task Should_Count_Refresh_Token() { IssueToken("refresh_token", false, AccessTokenType.Jwt, true, ProofType.None, false); var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("Refresh").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("TokensByType").GetProperty("Refresh").GetInt64().ShouldBe(1); } [Fact] - public async Task Should_Count_IdToken() + public async Task Should_Count_Id_Token() { IssueToken(GrantType.AuthorizationCode, false, AccessTokenType.Jwt, false, ProofType.None, true); var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); - result.RootElement.GetProperty("TokenIssueCounts").GetProperty("Id").GetInt64().ShouldBe(1); + result.RootElement.GetProperty("TokenIssueCounts").GetProperty("TokensByType").GetProperty("Id").GetInt64().ShouldBe(1); } [Fact] @@ -100,9 +100,11 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); var tokenIssueCounts = result.RootElement.GetProperty("TokenIssueCounts"); - tokenIssueCounts.GetProperty("Jwt").GetInt64().ShouldBe(1); - tokenIssueCounts.GetProperty("JwtDPoP").GetInt64().ShouldBe(1); - tokenIssueCounts.GetProperty("Refresh").GetInt64().ShouldBe(1); + tokenIssueCounts.GetProperty("AccessTokensByType").GetProperty("Jwt").GetInt64().ShouldBe(2); + var senderConstraint = tokenIssueCounts.GetProperty("AccessTokensBySenderConstraint"); + senderConstraint.GetProperty("None").GetInt64().ShouldBe(1); + senderConstraint.GetProperty("DPoP").GetInt64().ShouldBe(1); + tokenIssueCounts.GetProperty("TokensByType").GetProperty("Refresh").GetInt64().ShouldBe(1); } [Fact] @@ -113,14 +115,17 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); var tokenIssueCounts = result.RootElement.GetProperty("TokenIssueCounts"); - tokenIssueCounts.GetProperty("Jwt").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("Reference").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("JwtDPoP").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("JwtMTLS").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("ReferenceDPoP").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("ReferenceMTLS").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("Refresh").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("Id").GetInt64().ShouldBe(0); + var accessTokensByType = tokenIssueCounts.GetProperty("AccessTokensByType"); + accessTokensByType.GetProperty("Jwt").GetInt64().ShouldBe(0); + accessTokensByType.GetProperty("Reference").GetInt64().ShouldBe(0); + var senderConstraint = tokenIssueCounts.GetProperty("AccessTokensBySenderConstraint"); + senderConstraint.GetProperty("None").GetInt64().ShouldBe(0); + senderConstraint.GetProperty("DPoP").GetInt64().ShouldBe(0); + senderConstraint.GetProperty("mTLS").GetInt64().ShouldBe(0); + var tokensByType = tokenIssueCounts.GetProperty("TokensByType"); + tokensByType.GetProperty("Access").GetInt64().ShouldBe(0); + tokensByType.GetProperty("Refresh").GetInt64().ShouldBe(0); + tokensByType.GetProperty("Id").GetInt64().ShouldBe(0); } [Fact] @@ -131,7 +136,7 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); var tokenIssueCounts = result.RootElement.GetProperty("TokenIssueCounts"); - tokenIssueCounts.GetProperty(GrantType.AuthorizationCode).GetInt64().ShouldBe(1); + tokenIssueCounts.GetProperty("RequestsByGrantType").GetProperty(GrantType.AuthorizationCode).GetInt64().ShouldBe(1); } [Fact] @@ -143,8 +148,9 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); var tokenIssueCounts = result.RootElement.GetProperty("TokenIssueCounts"); - tokenIssueCounts.GetProperty(GrantType.AuthorizationCode).GetInt64().ShouldBe(1); - tokenIssueCounts.GetProperty(GrantType.ClientCredentials).GetInt64().ShouldBe(1); + var grantTypeCounts = tokenIssueCounts.GetProperty("RequestsByGrantType"); + grantTypeCounts.GetProperty(GrantType.AuthorizationCode).GetInt64().ShouldBe(1); + grantTypeCounts.GetProperty(GrantType.ClientCredentials).GetInt64().ShouldBe(1); } [Fact] @@ -156,7 +162,7 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); var tokenIssueCounts = result.RootElement.GetProperty("TokenIssueCounts"); - tokenIssueCounts.GetProperty(GrantType.AuthorizationCode).GetInt64().ShouldBe(2); + tokenIssueCounts.GetProperty("RequestsByGrantType").GetProperty(GrantType.AuthorizationCode).GetInt64().ShouldBe(2); } [Fact] @@ -174,9 +180,10 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); var tokenIssueCounts = result.RootElement.GetProperty("TokenIssueCounts"); + var grantTypeCounts = tokenIssueCounts.GetProperty("RequestsByGrantType"); foreach (var grantType in grantTypes) { - tokenIssueCounts.GetProperty(grantType).GetInt64().ShouldBe(1); + grantTypeCounts.GetProperty(grantType).GetInt64().ShouldBe(1); } } @@ -188,13 +195,17 @@ public class TokenIssueCountDiagnosticEntryTests var result = await DiagnosticEntryTestHelper.WriteEntryToJson(_subject); var tokenIssueCounts = result.RootElement.GetProperty("TokenIssueCounts"); - tokenIssueCounts.GetProperty("Jwt").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("Reference").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("JwtDPoP").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("JwtMTLS").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("ReferenceDPoP").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("ReferenceMTLS").GetInt64().ShouldBe(0); - tokenIssueCounts.GetProperty("Refresh").GetInt64().ShouldBe(0); + var accessTokensByType = tokenIssueCounts.GetProperty("AccessTokensByType"); + accessTokensByType.GetProperty("Jwt").GetInt64().ShouldBe(0); + accessTokensByType.GetProperty("Reference").GetInt64().ShouldBe(0); + var senderConstraint = tokenIssueCounts.GetProperty("AccessTokensBySenderConstraint"); + senderConstraint.GetProperty("None").GetInt64().ShouldBe(0); + senderConstraint.GetProperty("DPoP").GetInt64().ShouldBe(0); + senderConstraint.GetProperty("mTLS").GetInt64().ShouldBe(0); + var tokensByType = tokenIssueCounts.GetProperty("TokensByType"); + tokensByType.GetProperty("Access").GetInt64().ShouldBe(0); + tokensByType.GetProperty("Refresh").GetInt64().ShouldBe(0); + tokensByType.GetProperty("Id").GetInt64().ShouldBe(0); } private void IssueToken(string grantType, bool accessTokenIssued, AccessTokenType? accessTokenType, bool refreshTokenIssued,