Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -89,6 +89,7 @@ public static class IdentityServiceCollectionExtensions

// Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
services.AddMetrics();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
Expand Down
1 change: 1 addition & 0 deletions src/Identity/Core/src/Microsoft.AspNetCore.Identity.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Reference Include="Microsoft.AspNetCore.Authentication.BearerToken" />
<Reference Include="Microsoft.AspNetCore.Authorization.Policy" />
<Reference Include="Microsoft.AspNetCore.Http.Results" />
<Reference Include="Microsoft.Extensions.Diagnostics" />
<Reference Include="Microsoft.Extensions.Identity.Core" />
<Reference Include="System.Formats.Cbor" />
</ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion src/Identity/Core/src/SignInManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Security.Claims;
using System.Text;
Expand Down Expand Up @@ -63,7 +64,8 @@ public SignInManager(UserManager<TUser> userManager,
Logger = logger;
_schemes = schemes;
_confirmation = confirmation;
_metrics = userManager.ServiceProvider?.GetService<SignInManagerMetrics>();
// SignInManagerMetrics created from constructor because of difficulties registering internal type.
_metrics = userManager.ServiceProvider?.GetService<IMeterFactory>() is { } factory ? new SignInManagerMetrics(factory) : null;
_passkeyHandler = userManager.ServiceProvider?.GetService<IPasskeyHandler<TUser>>();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public static IdentityBuilder AddIdentityCore<TUser>(this IServiceCollection ser
{
// Services identity depends on
services.AddOptions().AddLogging();
services.AddMetrics();

// Services used by identity
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" />
<Reference Include="Microsoft.Extensions.Diagnostics" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Options" />

Expand Down
4 changes: 3 additions & 1 deletion src/Identity/Extensions.Core/src/UserManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Claims;
Expand Down Expand Up @@ -85,7 +86,8 @@ public UserManager(IUserStore<TUser> store,
ErrorDescriber = errors;
Logger = logger;
ServiceProvider = services;
_metrics = services?.GetService<UserManagerMetrics>();
// UserManagerMetrics created from constructor because of difficulties registering internal type.
_metrics = services?.GetService<IMeterFactory>() is { } factory ? new UserManagerMetrics(factory) : null;

if (userValidators != null)
{
Expand Down
41 changes: 41 additions & 0 deletions src/Identity/test/Identity.FunctionalTests/MapIdentityApiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#nullable enable

using System.Diagnostics.Metrics;
using System.Globalization;
using System.Net;
using System.Net.Http;
Expand All @@ -23,6 +24,8 @@
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.Metrics;
using Microsoft.Extensions.Diagnostics.Metrics.Testing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Time.Testing;
using Microsoft.Net.Http.Headers;
Expand Down Expand Up @@ -1274,6 +1277,44 @@ public async Task MustSendValidRequestToSendEmailChangeConfirmation()
AssertOk(await client.PostAsJsonAsync("/identity/login", new { Email = newEmail, Password = newPassword }));
}

[Fact]
public async Task MetricsAreRecordedForUserManagerAndSignInManager()
{
// Arrange
await using var app = await CreateAppAsync(services =>
{
AddIdentityApiEndpoints(services);
});
var meterFactory = app.Services.GetRequiredService<IMeterFactory>();

using var client = app.GetTestClient();

using var userCreateCollector = new MetricCollector<double>(meterFactory, "Microsoft.AspNetCore.Identity", "aspnetcore.identity.user.create.duration");
using var signInAuthenticateCollector = new MetricCollector<double>(meterFactory, "Microsoft.AspNetCore.Identity", "aspnetcore.identity.sign_in.authenticate.duration");

// Act - Register a new user
AssertOkAndEmpty(await client.PostAsJsonAsync("/identity/register", new { Email, Password }));

// Assert - UserManager metrics are recorded
var userCreateMeasurement = Assert.Single(userCreateCollector.GetMeasurementSnapshot());
Assert.True(userCreateMeasurement.Value > 0);
Assert.Equal("Identity.DefaultUI.WebSite.ApplicationUser", (string?)userCreateMeasurement.Tags["aspnetcore.identity.user_type"]);
Assert.Equal("success", (string?)userCreateMeasurement.Tags["aspnetcore.identity.result"]);

// Act - Login with the user
AssertOk(await client.PostAsJsonAsync("/identity/login", new { Email, Password }));

// Assert - SignInManager metrics are recorded
var signInMeasurements = signInAuthenticateCollector.GetMeasurementSnapshot();
Assert.NotEmpty(signInMeasurements);
var signInMeasurement = signInMeasurements.Last();
Assert.True(signInMeasurement.Value > 0);
Assert.Equal("Identity.DefaultUI.WebSite.ApplicationUser", (string?)signInMeasurement.Tags["aspnetcore.identity.user_type"]);
Assert.Equal("Identity.Bearer", (string?)signInMeasurement.Tags["aspnetcore.authentication.scheme"]);
Assert.Equal("success", (string?)signInMeasurement.Tags["aspnetcore.identity.sign_in.result"]);
Assert.Equal("password", (string?)signInMeasurement.Tags["aspnetcore.identity.sign_in.type"]);
}

private async Task<WebApplication> CreateAppAsync<TUser, TContext>(Action<IServiceCollection>? configureServices, bool autoStart = true)
where TUser : class, new()
where TContext : DbContext
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ContainsFunctionalTestAssets>true</ContainsFunctionalTestAssets>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
Expand All @@ -22,6 +22,7 @@

<Reference Include="Microsoft.AspNetCore.Mvc.Testing" />
<Reference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<Reference Include="Microsoft.Extensions.Diagnostics.Testing" />
<Reference Include="Microsoft.Extensions.TimeProvider.Testing" />
<Reference Include="AngleSharp" />

Expand Down
3 changes: 0 additions & 3 deletions src/Identity/test/Shared/MockHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public static Mock<UserManager<TUser>> MockUserManager<TUser>(
var services = new ServiceCollection();
if (meterFactory != null)
{
services.AddSingleton<SignInManagerMetrics>();
services.AddSingleton(meterFactory);
}
if (passkeyHandler != null)
Expand Down Expand Up @@ -61,8 +60,6 @@ public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store
var services = new ServiceCollection();
if (meterFactory != null)
{
services.AddSingleton<UserManagerMetrics>();
services.AddSingleton<SignInManagerMetrics>();
services.AddSingleton(meterFactory);
}
var userManager = new UserManager<TUser>(store, options.Object, new PasswordHasher<TUser>(),
Expand Down
Loading