Skip to content

Commit e4748a3

Browse files
authored
Add metrics to Identity (#62078)
1 parent b7bc579 commit e4748a3

14 files changed

+1959
-274
lines changed

src/Identity/Core/src/Microsoft.AspNetCore.Identity.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<Compile Include="$(SharedSourceRoot)DefaultMessageEmailSender.cs" />
16+
<Compile Include="$(SharedSourceRoot)DefaultMessageEmailSender.cs" LinkBase="Shared" />
17+
<Compile Include="$(SharedSourceRoot)Metrics\MetricsConstants.cs" LinkBase="Shared" />
1718
</ItemGroup>
1819

1920
<ItemGroup>

src/Identity/Core/src/SignInManager.cs

Lines changed: 203 additions & 34 deletions
Large diffs are not rendered by default.
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics;
5+
using System.Diagnostics.Metrics;
6+
7+
namespace Microsoft.AspNetCore.Identity;
8+
9+
internal sealed class SignInManagerMetrics : IDisposable
10+
{
11+
public const string MeterName = "Microsoft.AspNetCore.Identity";
12+
13+
public const string AuthenticateCounterName = "aspnetcore.identity.sign_in.authenticate";
14+
public const string RememberTwoFactorCounterName = "aspnetcore.identity.sign_in.remember_two_factor";
15+
public const string ForgetTwoFactorCounterName = "aspnetcore.identity.sign_in.forget_two_factor";
16+
public const string CheckPasswordCounterName = "aspnetcore.identity.sign_in.check_password";
17+
public const string SignInUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_in_principal";
18+
public const string SignOutUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_out_principal";
19+
20+
private readonly Meter _meter;
21+
private readonly Counter<long> _authenticateCounter;
22+
private readonly Counter<long> _rememberTwoFactorClientCounter;
23+
private readonly Counter<long> _forgetTwoFactorCounter;
24+
private readonly Counter<long> _checkPasswordCounter;
25+
private readonly Counter<long> _signInUserPrincipalCounter;
26+
private readonly Counter<long> _signOutUserPrincipalCounter;
27+
28+
public SignInManagerMetrics(IMeterFactory meterFactory)
29+
{
30+
_meter = meterFactory.Create(MeterName);
31+
32+
_authenticateCounter = _meter.CreateCounter<long>(AuthenticateCounterName, "count", "The number of authenticate attempts. The authenticate counter is incremented by sign in methods such as PasswordSignInAsync and TwoFactorSignInAsync.");
33+
_rememberTwoFactorClientCounter = _meter.CreateCounter<long>(RememberTwoFactorCounterName, "count", "The number of two factor clients remembered.");
34+
_forgetTwoFactorCounter = _meter.CreateCounter<long>(ForgetTwoFactorCounterName, "count", "The number of two factor clients forgotten.");
35+
_checkPasswordCounter = _meter.CreateCounter<long>(CheckPasswordCounterName, "count", "The number of check password attempts. Checks that the account is in a state that can log in and that the password is valid using the UserManager.CheckPasswordAsync method.");
36+
_signInUserPrincipalCounter = _meter.CreateCounter<long>(SignInUserPrincipalCounterName, "count", "The number of calls to sign in user principals.");
37+
_signOutUserPrincipalCounter = _meter.CreateCounter<long>(SignOutUserPrincipalCounterName, "count", "The number of calls to sign out user principals.");
38+
}
39+
40+
internal void CheckPasswordSignIn(string userType, SignInResult? result, Exception? exception = null)
41+
{
42+
if (!_checkPasswordCounter.Enabled)
43+
{
44+
return;
45+
}
46+
47+
var tags = new TagList
48+
{
49+
{ "aspnetcore.identity.user_type", userType },
50+
};
51+
AddSignInResult(ref tags, result);
52+
AddExceptionTags(ref tags, exception);
53+
54+
_checkPasswordCounter.Add(1, tags);
55+
}
56+
57+
internal void AuthenticateSignIn(string userType, string authenticationScheme, SignInResult? result, SignInType signInType, bool? isPersistent, Exception? exception = null)
58+
{
59+
if (!_authenticateCounter.Enabled)
60+
{
61+
return;
62+
}
63+
64+
var tags = new TagList
65+
{
66+
{ "aspnetcore.identity.user_type", userType },
67+
{ "aspnetcore.identity.authentication_scheme", authenticationScheme },
68+
{ "aspnetcore.identity.sign_in.type", GetSignInType(signInType) },
69+
};
70+
AddIsPersistent(ref tags, isPersistent);
71+
AddSignInResult(ref tags, result);
72+
AddExceptionTags(ref tags, exception);
73+
74+
_authenticateCounter.Add(1, tags);
75+
}
76+
77+
internal void SignInUserPrincipal(string userType, string authenticationScheme, bool? isPersistent, Exception? exception = null)
78+
{
79+
if (!_signInUserPrincipalCounter.Enabled)
80+
{
81+
return;
82+
}
83+
84+
var tags = new TagList
85+
{
86+
{ "aspnetcore.identity.user_type", userType },
87+
{ "aspnetcore.identity.authentication_scheme", authenticationScheme },
88+
};
89+
AddIsPersistent(ref tags, isPersistent);
90+
AddExceptionTags(ref tags, exception);
91+
92+
_signInUserPrincipalCounter.Add(1, tags);
93+
}
94+
95+
internal void SignOutUserPrincipal(string userType, string authenticationScheme, Exception? exception = null)
96+
{
97+
if (!_signOutUserPrincipalCounter.Enabled)
98+
{
99+
return;
100+
}
101+
102+
var tags = new TagList
103+
{
104+
{ "aspnetcore.identity.user_type", userType },
105+
{ "aspnetcore.identity.authentication_scheme", authenticationScheme },
106+
};
107+
AddExceptionTags(ref tags, exception);
108+
109+
_signOutUserPrincipalCounter.Add(1, tags);
110+
}
111+
112+
internal void RememberTwoFactorClient(string userType, string authenticationScheme, Exception? exception = null)
113+
{
114+
if (!_rememberTwoFactorClientCounter.Enabled)
115+
{
116+
return;
117+
}
118+
119+
var tags = new TagList
120+
{
121+
{ "aspnetcore.identity.user_type", userType },
122+
{ "aspnetcore.identity.authentication_scheme", authenticationScheme }
123+
};
124+
AddExceptionTags(ref tags, exception);
125+
126+
_rememberTwoFactorClientCounter.Add(1, tags);
127+
}
128+
129+
internal void ForgetTwoFactorClient(string userType, string authenticationScheme, Exception? exception = null)
130+
{
131+
if (!_forgetTwoFactorCounter.Enabled)
132+
{
133+
return;
134+
}
135+
136+
var tags = new TagList
137+
{
138+
{ "aspnetcore.identity.user_type", userType },
139+
{ "aspnetcore.identity.authentication_scheme", authenticationScheme }
140+
};
141+
AddExceptionTags(ref tags, exception);
142+
143+
_forgetTwoFactorCounter.Add(1, tags);
144+
}
145+
146+
public void Dispose()
147+
{
148+
_meter.Dispose();
149+
}
150+
151+
private static void AddIsPersistent(ref TagList tags, bool? isPersistent)
152+
{
153+
if (isPersistent != null)
154+
{
155+
tags.Add("aspnetcore.identity.sign_in.is_persistent", isPersistent.Value);
156+
}
157+
}
158+
159+
private static void AddSignInResult(ref TagList tags, SignInResult? result)
160+
{
161+
if (result != null)
162+
{
163+
tags.Add("aspnetcore.identity.sign_in.result", GetSignInResult(result));
164+
}
165+
}
166+
167+
private static void AddExceptionTags(ref TagList tags, Exception? exception)
168+
{
169+
if (exception != null)
170+
{
171+
tags.Add("error.type", exception.GetType().FullName!);
172+
}
173+
}
174+
175+
private static string GetSignInType(SignInType signInType)
176+
{
177+
return signInType switch
178+
{
179+
SignInType.Password => "password",
180+
SignInType.TwoFactorRecoveryCode => "two_factor_recovery_code",
181+
SignInType.TwoFactorAuthenticator => "two_factor_authenticator",
182+
SignInType.TwoFactor => "two_factor",
183+
SignInType.External => "external",
184+
SignInType.Passkey => "passkey",
185+
_ => "_UNKNOWN"
186+
};
187+
}
188+
189+
private static string GetSignInResult(SignInResult result)
190+
{
191+
return result switch
192+
{
193+
{ Succeeded: true } => "success",
194+
{ IsLockedOut: true } => "locked_out",
195+
{ IsNotAllowed: true } => "not_allowed",
196+
{ RequiresTwoFactor: true } => "requires_two_factor",
197+
_ => "failure"
198+
};
199+
}
200+
}

src/Identity/Core/src/SignInType.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Identity;
5+
6+
internal enum SignInType
7+
{
8+
Refresh,
9+
Password,
10+
TwoFactorRecoveryCode,
11+
TwoFactorAuthenticator,
12+
TwoFactor,
13+
External,
14+
Passkey
15+
}

src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,4 @@ virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetPasskeysAsync(TUser!
5555
virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemovePasskeyAsync(TUser! user, byte[]! credentialId) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult!>!
5656
virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetPasskeyAsync(TUser! user, Microsoft.AspNetCore.Identity.UserPasskeyInfo! passkey) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult!>!
5757
virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserPasskey.get -> bool
58+
Microsoft.AspNetCore.Identity.UserManager<TUser>.ServiceProvider.get -> System.IServiceProvider!

0 commit comments

Comments
 (0)