diff --git a/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPJwtBearerEvents.cs b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPJwtBearerEvents.cs index 1b37e392e..241098f7a 100644 --- a/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPJwtBearerEvents.cs +++ b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPJwtBearerEvents.cs @@ -39,7 +39,7 @@ internal class DPoPJwtBearerEvents /// public Task MessageReceived(MessageReceivedContext context) { - _logger.LogDebug("MessageReceived invoked in a JWT bearer authentication scheme using DPoP"); + using var activity = Tracing.ActivitySource.StartActivity("DPoPJwtBearerEvents.MessageReceived"); var dpopOptions = _optionsMonitor.Get(context.Scheme.Name); @@ -62,7 +62,7 @@ internal class DPoPJwtBearerEvents /// public async Task TokenValidated(TokenValidatedContext context) { - _logger.LogDebug("TokenValidated invoked in a JWT bearer authentication scheme using DPoP"); + using var activity = Tracing.ActivitySource.StartActivity("DPoPJwtBearerEvents.TokenValidated"); var dPoPOptions = _optionsMonitor.Get(context.Scheme.Name); @@ -135,7 +135,7 @@ internal class DPoPJwtBearerEvents /// /// Checks if the HTTP authorization header's 'scheme' is DPoP. /// - protected static bool IsDPoPAuthorizationScheme(HttpRequest request) + private static bool IsDPoPAuthorizationScheme(HttpRequest request) { var authz = request.Headers.Authorization.FirstOrDefault(); return authz?.StartsWith(DPoPPrefix, StringComparison.OrdinalIgnoreCase) == true; @@ -144,7 +144,7 @@ internal class DPoPJwtBearerEvents /// /// Attempts to retrieve a DPoP access token from an . /// - public bool TryGetDPoPAccessToken(HttpRequest request, + private bool TryGetDPoPAccessToken(HttpRequest request, int maxLength, [NotNullWhen(true)] out string? token) { @@ -172,7 +172,8 @@ internal class DPoPJwtBearerEvents /// public Task Challenge(JwtBearerChallengeContext context) { - _logger.LogDebug("Challenge invoked in a JWT bearer authentication scheme using DPoP"); + using var activity = Tracing.ActivitySource.StartActivity("DPoPJwtBearerEvents.Challenge"); + var dPoPOptions = _optionsMonitor.Get(context.Scheme.Name); if (!dPoPOptions.AllowBearerTokens) diff --git a/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPProofValidator.cs b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPProofValidator.cs index 88126f585..a4f4cdff9 100644 --- a/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPProofValidator.cs +++ b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/DPoPProofValidator.cs @@ -72,13 +72,14 @@ internal class DPoPProofValidator : IDPoPProofValidator /// public async Task Validate(DPoPProofValidationContext context, CancellationToken cancellationToken = default) { - Logger.LogDebug("Validating DPoP proof token"); + using var activity = Tracing.ActivitySource.StartActivity("DPoPProofValidator.Validate"); + var result = new DPoPProofValidationResult(); if (string.IsNullOrEmpty(context.ProofToken)) { Logger.LogDebug("Missing DPoP proof value"); - result.SetError("Missing DPoP proof value", OidcConstants.TokenErrors.InvalidRequest); + result.SetError("Missing DPoP proof token.", OidcConstants.TokenErrors.InvalidRequest); return result; } @@ -115,7 +116,7 @@ internal class DPoPProofValidator : IDPoPProofValidator await ValidateReplay(context, result, cancellationToken); if (result.IsError) { - Logger.LogDebug("Detected replay of DPoP proof token"); + Logger.LogInformation("Detected replay of DPoP proof token"); } Logger.LogDebug("Successfully validated DPoP proof token"); diff --git a/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/ReplayCache.cs b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/ReplayCache.cs index 601b2886c..651df3e07 100644 --- a/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/ReplayCache.cs +++ b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/ReplayCache.cs @@ -21,6 +21,8 @@ internal class ReplayCache : IReplayCache public async Task Add(string handle, TimeSpan expiration, CancellationToken ct) { + using var activity = Tracing.ActivitySource.StartActivity("ReplayCache.Add"); + var options = new HybridCacheEntryOptions { Expiration = expiration @@ -36,10 +38,15 @@ internal class ReplayCache : IReplayCache | HybridCacheEntryFlags.DisableUnderlyingData }; - public async Task Exists(string handle, CancellationToken ct) => await _cache.GetOrCreateAsync( + public async Task Exists(string handle, CancellationToken ct) + { + using var activity = Tracing.ActivitySource.StartActivity("ReplayCache.Exists"); + + return await _cache.GetOrCreateAsync( Prefix + handle, // The factory will never be invoked because the ReadOnlyEntryOptions set the DisableUnderlyingData flag cancel => throw new InvalidOperationException("Can't Happen"), ReadOnlyEntryOptions, cancellationToken: ct); + } } diff --git a/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/Tracing.cs b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/Tracing.cs new file mode 100644 index 000000000..731c81180 --- /dev/null +++ b/aspnetcore-authentication-jwtbearer/src/AspNetCore.Authentication.JwtBearer/DPoP/Tracing.cs @@ -0,0 +1,19 @@ +// Copyright (c) Duende Software. All rights reserved. +// See LICENSE in the project root for license information. + +using System.Diagnostics; + +namespace Duende.AspNetCore.Authentication.JwtBearer.DPoP; + +internal static class Tracing +{ + private static readonly Version? AssemblyVersion = typeof(Tracing).Assembly.GetName().Version; + + public static string ServiceVersion => + AssemblyVersion != null ? $"{AssemblyVersion.Major}.{AssemblyVersion.Minor}.{AssemblyVersion.Build}" : + "Unknown Version"; + + public static ActivitySource ActivitySource { get; } = new( + "Duende.AspNetCore.Authentication.JwtBearer", + ServiceVersion); +} diff --git a/identity-server/aspire/ServiceDefaults/Extensions.cs b/identity-server/aspire/ServiceDefaults/Extensions.cs index 023d49f88..5c5d31796 100644 --- a/identity-server/aspire/ServiceDefaults/Extensions.cs +++ b/identity-server/aspire/ServiceDefaults/Extensions.cs @@ -62,6 +62,7 @@ public static class Extensions .AddSource(IdentityServerConstants.Tracing.Services) .AddSource(IdentityServerConstants.Tracing.Stores) .AddSource(IdentityServerConstants.Tracing.Validation) + .AddSource("Duende.AspNetCore.Authentication.JwtBearer") .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation(); }); diff --git a/identity-server/clients/src/APIs/DPoPApi/DPoPApi.csproj b/identity-server/clients/src/APIs/DPoPApi/DPoPApi.csproj index 78bee6dce..37fd73ef6 100644 --- a/identity-server/clients/src/APIs/DPoPApi/DPoPApi.csproj +++ b/identity-server/clients/src/APIs/DPoPApi/DPoPApi.csproj @@ -1,12 +1,12 @@  + - diff --git a/identity-server/clients/src/APIs/DPoPApi/Program.cs b/identity-server/clients/src/APIs/DPoPApi/Program.cs index 31ddfb6c1..39cc70afe 100644 --- a/identity-server/clients/src/APIs/DPoPApi/Program.cs +++ b/identity-server/clients/src/APIs/DPoPApi/Program.cs @@ -32,7 +32,8 @@ try builder.Services.ConfigureDPoPTokensForScheme("token", options => { - options.TokenMode = DPoPMode.DPoPAndBearer; + options.AllowBearerTokens = true; + options.EnableReplayDetection = true; }); var app = builder.Build();