File: SignInManagerMetrics.cs
Web Access
Project: src\src\Identity\Core\src\Microsoft.AspNetCore.Identity.csproj (Microsoft.AspNetCore.Identity)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.Metrics;
 
namespace Microsoft.AspNetCore.Identity;
 
internal sealed class SignInManagerMetrics : IDisposable
{
    public const string MeterName = "Microsoft.AspNetCore.Identity";
 
    public const string AuthenticateCounterName = "aspnetcore.identity.sign_in.authenticate";
    public const string RememberTwoFactorCounterName = "aspnetcore.identity.sign_in.remember_two_factor";
    public const string ForgetTwoFactorCounterName = "aspnetcore.identity.sign_in.forget_two_factor";
    public const string CheckPasswordCounterName = "aspnetcore.identity.sign_in.check_password";
    public const string SignInUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_in_principal";
    public const string SignOutUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_out_principal";
 
    private readonly Meter _meter;
    private readonly Counter<long> _authenticateCounter;
    private readonly Counter<long> _rememberTwoFactorClientCounter;
    private readonly Counter<long> _forgetTwoFactorCounter;
    private readonly Counter<long> _checkPasswordCounter;
    private readonly Counter<long> _signInUserPrincipalCounter;
    private readonly Counter<long> _signOutUserPrincipalCounter;
 
    public SignInManagerMetrics(IMeterFactory meterFactory)
    {
        _meter = meterFactory.Create(MeterName);
 
        _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.");
        _rememberTwoFactorClientCounter = _meter.CreateCounter<long>(RememberTwoFactorCounterName, "count", "The number of two factor clients remembered.");
        _forgetTwoFactorCounter = _meter.CreateCounter<long>(ForgetTwoFactorCounterName, "count", "The number of two factor clients forgotten.");
        _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.");
        _signInUserPrincipalCounter = _meter.CreateCounter<long>(SignInUserPrincipalCounterName, "count", "The number of calls to sign in user principals.");
        _signOutUserPrincipalCounter = _meter.CreateCounter<long>(SignOutUserPrincipalCounterName, "count", "The number of calls to sign out user principals.");
    }
 
    internal void CheckPasswordSignIn(string userType, SignInResult? result, Exception? exception = null)
    {
        if (!_checkPasswordCounter.Enabled)
        {
            return;
        }
 
        var tags = new TagList
        {
            { "aspnetcore.identity.user_type", userType },
        };
        AddSignInResult(ref tags, result);
        AddExceptionTags(ref tags, exception);
 
        _checkPasswordCounter.Add(1, tags);
    }
 
    internal void AuthenticateSignIn(string userType, string authenticationScheme, SignInResult? result, SignInType signInType, bool? isPersistent, Exception? exception = null)
    {
        if (!_authenticateCounter.Enabled)
        {
            return;
        }
 
        var tags = new TagList
        {
            { "aspnetcore.identity.user_type", userType },
            { "aspnetcore.identity.authentication_scheme", authenticationScheme },
            { "aspnetcore.identity.sign_in.type", GetSignInType(signInType) },
        };
        AddIsPersistent(ref tags, isPersistent);
        AddSignInResult(ref tags, result);
        AddExceptionTags(ref tags, exception);
 
        _authenticateCounter.Add(1, tags);
    }
 
    internal void SignInUserPrincipal(string userType, string authenticationScheme, bool? isPersistent, Exception? exception = null)
    {
        if (!_signInUserPrincipalCounter.Enabled)
        {
            return;
        }
 
        var tags = new TagList
        {
            { "aspnetcore.identity.user_type", userType },
            { "aspnetcore.identity.authentication_scheme", authenticationScheme },
        };
        AddIsPersistent(ref tags, isPersistent);
        AddExceptionTags(ref tags, exception);
 
        _signInUserPrincipalCounter.Add(1, tags);
    }
 
    internal void SignOutUserPrincipal(string userType, string authenticationScheme, Exception? exception = null)
    {
        if (!_signOutUserPrincipalCounter.Enabled)
        {
            return;
        }
 
        var tags = new TagList
        {
            { "aspnetcore.identity.user_type", userType },
            { "aspnetcore.identity.authentication_scheme", authenticationScheme },
        };
        AddExceptionTags(ref tags, exception);
 
        _signOutUserPrincipalCounter.Add(1, tags);
    }
 
    internal void RememberTwoFactorClient(string userType, string authenticationScheme, Exception? exception = null)
    {
        if (!_rememberTwoFactorClientCounter.Enabled)
        {
            return;
        }
 
        var tags = new TagList
        {
            { "aspnetcore.identity.user_type", userType },
            { "aspnetcore.identity.authentication_scheme", authenticationScheme }
        };
        AddExceptionTags(ref tags, exception);
 
        _rememberTwoFactorClientCounter.Add(1, tags);
    }
 
    internal void ForgetTwoFactorClient(string userType, string authenticationScheme, Exception? exception = null)
    {
        if (!_forgetTwoFactorCounter.Enabled)
        {
            return;
        }
 
        var tags = new TagList
        {
            { "aspnetcore.identity.user_type", userType },
            { "aspnetcore.identity.authentication_scheme", authenticationScheme }
        };
        AddExceptionTags(ref tags, exception);
 
        _forgetTwoFactorCounter.Add(1, tags);
    }
 
    public void Dispose()
    {
        _meter.Dispose();
    }
 
    private static void AddIsPersistent(ref TagList tags, bool? isPersistent)
    {
        if (isPersistent != null)
        {
            tags.Add("aspnetcore.identity.sign_in.is_persistent", isPersistent.Value);
        }
    }
 
    private static void AddSignInResult(ref TagList tags, SignInResult? result)
    {
        if (result != null)
        {
            tags.Add("aspnetcore.identity.sign_in.result", GetSignInResult(result));
        }
    }
 
    private static void AddExceptionTags(ref TagList tags, Exception? exception)
    {
        if (exception != null)
        {
            tags.Add("error.type", exception.GetType().FullName!);
        }
    }
 
    private static string GetSignInType(SignInType signInType)
    {
        return signInType switch
        {
            SignInType.Password => "password",
            SignInType.TwoFactorRecoveryCode => "two_factor_recovery_code",
            SignInType.TwoFactorAuthenticator => "two_factor_authenticator",
            SignInType.TwoFactor => "two_factor",
            SignInType.External => "external",
            SignInType.Passkey => "passkey",
            _ => "_UNKNOWN"
        };
    }
 
    private static string GetSignInResult(SignInResult result)
    {
        return result switch
        {
            { Succeeded: true } => "success",
            { IsLockedOut: true } => "locked_out",
            { IsNotAllowed: true } => "not_allowed",
            { RequiresTwoFactor: true } => "requires_two_factor",
            _ => "failure"
        };
    }
}