77using System . Text ;
88using Microsoft . AspNetCore . Authentication ;
99using Microsoft . AspNetCore . Http ;
10+ using Microsoft . Extensions . DependencyInjection ;
1011using Microsoft . Extensions . Logging ;
1112using Microsoft . Extensions . Options ;
1213
@@ -24,6 +25,7 @@ public class SignInManager<TUser> where TUser : class
2425 private readonly IHttpContextAccessor _contextAccessor ;
2526 private readonly IAuthenticationSchemeProvider _schemes ;
2627 private readonly IUserConfirmation < TUser > _confirmation ;
28+ private readonly SignInManagerMetrics ? _metrics ;
2729 private HttpContext ? _context ;
2830 private TwoFactorAuthenticationInfo ? _twoFactorInfo ;
2931
@@ -56,6 +58,7 @@ public SignInManager(UserManager<TUser> userManager,
5658 Logger = logger ;
5759 _schemes = schemes ;
5860 _confirmation = confirmation ;
61+ _metrics = userManager . ServiceProvider ? . GetService < SignInManagerMetrics > ( ) ;
5962 }
6063
6164 /// <summary>
@@ -160,25 +163,39 @@ public virtual async Task<bool> CanSignInAsync(TUser user)
160163 /// <param name="user">The user to sign-in.</param>
161164 /// <returns>The task object representing the asynchronous operation.</returns>
162165 public virtual async Task RefreshSignInAsync ( TUser user )
166+ {
167+ try
168+ {
169+ var ( success , isPersistent ) = await RefreshSignInCoreAsync ( user ) ;
170+ _metrics ? . RefreshSignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , success , isPersistent ) ;
171+ }
172+ catch ( Exception ex )
173+ {
174+ _metrics ? . RefreshSignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , success : null , isPersistent : null , ex ) ;
175+ throw ;
176+ }
177+ }
178+
179+ private async Task < ( bool success , bool ? isPersistent ) > RefreshSignInCoreAsync ( TUser user )
163180 {
164181 var auth = await Context . AuthenticateAsync ( AuthenticationScheme ) ;
165182 if ( ! auth . Succeeded || auth . Principal ? . Identity ? . IsAuthenticated != true )
166183 {
167184 Logger . LogError ( "RefreshSignInAsync prevented because the user is not currently authenticated. Use SignInAsync instead for initial sign in." ) ;
168- return ;
185+ return ( false , auth . Properties ? . IsPersistent ) ;
169186 }
170187
171188 var authenticatedUserId = UserManager . GetUserId ( auth . Principal ) ;
172189 var newUserId = await UserManager . GetUserIdAsync ( user ) ;
173190 if ( authenticatedUserId == null || authenticatedUserId != newUserId )
174191 {
175192 Logger . LogError ( "RefreshSignInAsync prevented because currently authenticated user has a different UserId. Use SignInAsync instead to change users." ) ;
176- return ;
193+ return ( false , auth . Properties ? . IsPersistent ) ;
177194 }
178195
179196 IList < Claim > claims = Array . Empty < Claim > ( ) ;
180- var authenticationMethod = auth ? . Principal ? . FindFirst ( ClaimTypes . AuthenticationMethod ) ;
181- var amr = auth ? . Principal ? . FindFirst ( "amr" ) ;
197+ var authenticationMethod = auth . Principal ? . FindFirst ( ClaimTypes . AuthenticationMethod ) ;
198+ var amr = auth . Principal ? . FindFirst ( "amr" ) ;
182199
183200 if ( authenticationMethod != null || amr != null )
184201 {
@@ -193,7 +210,8 @@ public virtual async Task RefreshSignInAsync(TUser user)
193210 }
194211 }
195212
196- await SignInWithClaimsAsync ( user , auth ? . Properties , claims ) ;
213+ await SignInWithClaimsAsync ( user , auth . Properties , claims ) ;
214+ return ( true , auth . Properties ? . IsPersistent ?? false ) ;
197215 }
198216
199217 /// <summary>
@@ -345,12 +363,23 @@ public virtual async Task<bool> ValidateSecurityStampAsync(TUser? user, string?
345363 public virtual async Task < SignInResult > PasswordSignInAsync ( TUser user , string password ,
346364 bool isPersistent , bool lockoutOnFailure )
347365 {
348- ArgumentNullException . ThrowIfNull ( user ) ;
366+ try
367+ {
368+ ArgumentNullException . ThrowIfNull ( user ) ;
369+
370+ var attempt = await CheckPasswordSignInAsync ( user , password , lockoutOnFailure ) ;
371+ var result = attempt . Succeeded
372+ ? await SignInOrTwoFactorAsync ( user , isPersistent )
373+ : attempt ;
374+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result , SignInType . Password , isPersistent ) ;
349375
350- var attempt = await CheckPasswordSignInAsync ( user , password , lockoutOnFailure ) ;
351- return attempt . Succeeded
352- ? await SignInOrTwoFactorAsync ( user , isPersistent )
353- : attempt ;
376+ return result ;
377+ }
378+ catch ( Exception ex )
379+ {
380+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result : null , SignInType . Password , isPersistent , ex ) ;
381+ throw ;
382+ }
354383 }
355384
356385 /// <summary>
@@ -369,6 +398,7 @@ public virtual async Task<SignInResult> PasswordSignInAsync(string userName, str
369398 var user = await UserManager . FindByNameAsync ( userName ) ;
370399 if ( user == null )
371400 {
401+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , SignInResult . Failed , SignInType . Password , isPersistent ) ;
372402 return SignInResult . Failed ;
373403 }
374404
@@ -386,8 +416,24 @@ public virtual async Task<SignInResult> PasswordSignInAsync(string userName, str
386416 /// <returns></returns>
387417 public virtual async Task < SignInResult > CheckPasswordSignInAsync ( TUser user , string password , bool lockoutOnFailure )
388418 {
389- ArgumentNullException . ThrowIfNull ( user ) ;
419+ try
420+ {
421+ ArgumentNullException . ThrowIfNull ( user ) ;
422+
423+ var result = await CheckPasswordSignInCoreAsync ( user , password , lockoutOnFailure ) ;
424+ _metrics ? . CheckPasswordSignIn ( typeof ( TUser ) . FullName ! , result ) ;
425+
426+ return result ;
427+ }
428+ catch ( Exception ex )
429+ {
430+ _metrics ? . CheckPasswordSignIn ( typeof ( TUser ) . FullName ! , result : null , ex ) ;
431+ throw ;
432+ }
433+ }
390434
435+ private async Task < SignInResult > CheckPasswordSignInCoreAsync ( TUser user , string password , bool lockoutOnFailure )
436+ {
391437 var error = await PreSignInCheck ( user ) ;
392438 if ( error != null )
393439 {
@@ -461,19 +507,37 @@ public virtual async Task<bool> IsTwoFactorClientRememberedAsync(TUser user)
461507 /// <returns>The task object representing the asynchronous operation.</returns>
462508 public virtual async Task RememberTwoFactorClientAsync ( TUser user )
463509 {
464- var principal = await StoreRememberClient ( user ) ;
465- await Context . SignInAsync ( IdentityConstants . TwoFactorRememberMeScheme ,
466- principal ,
467- new AuthenticationProperties { IsPersistent = true } ) ;
510+ try
511+ {
512+ var principal = await StoreRememberClient ( user ) ;
513+ await Context . SignInAsync ( IdentityConstants . TwoFactorRememberMeScheme ,
514+ principal ,
515+ new AuthenticationProperties { IsPersistent = true } ) ;
516+ _metrics ? . RememberTwoFactorClient ( typeof ( TUser ) . FullName ! , IdentityConstants . TwoFactorRememberMeScheme ) ;
517+ }
518+ catch ( Exception ex )
519+ {
520+ _metrics ? . RememberTwoFactorClient ( typeof ( TUser ) . FullName ! , IdentityConstants . TwoFactorRememberMeScheme , ex ) ;
521+ throw ;
522+ }
468523 }
469524
470525 /// <summary>
471526 /// Clears the "Remember this browser flag" from the current browser, as an asynchronous operation.
472527 /// </summary>
473528 /// <returns>The task object representing the asynchronous operation.</returns>
474- public virtual Task ForgetTwoFactorClientAsync ( )
529+ public virtual async Task ForgetTwoFactorClientAsync ( )
475530 {
476- return Context . SignOutAsync ( IdentityConstants . TwoFactorRememberMeScheme ) ;
531+ try
532+ {
533+ await Context . SignOutAsync ( IdentityConstants . TwoFactorRememberMeScheme ) ;
534+ _metrics ? . ForgetTwoFactorClient ( typeof ( TUser ) . FullName ! , IdentityConstants . TwoFactorRememberMeScheme ) ;
535+ }
536+ catch ( Exception ex )
537+ {
538+ _metrics ? . ForgetTwoFactorClient ( typeof ( TUser ) . FullName ! , IdentityConstants . TwoFactorRememberMeScheme , ex ) ;
539+ throw ;
540+ }
477541 }
478542
479543 /// <summary>
@@ -482,6 +546,22 @@ public virtual Task ForgetTwoFactorClientAsync()
482546 /// <param name="recoveryCode">The two factor recovery code.</param>
483547 /// <returns></returns>
484548 public virtual async Task < SignInResult > TwoFactorRecoveryCodeSignInAsync ( string recoveryCode )
549+ {
550+ try
551+ {
552+ var result = await TwoFactorRecoveryCodeSignInCoreAsync ( recoveryCode ) ;
553+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result , SignInType . TwoFactorRecoveryCode , isPersistent : false ) ;
554+
555+ return result ;
556+ }
557+ catch ( Exception ex )
558+ {
559+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result : null , SignInType . TwoFactorRecoveryCode , isPersistent : false , ex ) ;
560+ throw ;
561+ }
562+ }
563+
564+ private async Task < SignInResult > TwoFactorRecoveryCodeSignInCoreAsync ( string recoveryCode )
485565 {
486566 var twoFactorInfo = await RetrieveTwoFactorInfoAsync ( ) ;
487567 if ( twoFactorInfo == null )
@@ -510,8 +590,10 @@ private async Task<SignInResult> DoTwoFactorSignInAsync(TUser user, TwoFactorAut
510590 return SignInResult . Failed ;
511591 }
512592
513- var claims = new List < Claim > ( ) ;
514- claims . Add ( new Claim ( "amr" , "mfa" ) ) ;
593+ var claims = new List < Claim >
594+ {
595+ new Claim ( "amr" , "mfa" )
596+ } ;
515597
516598 if ( twoFactorInfo . LoginProvider != null )
517599 {
@@ -545,6 +627,22 @@ private async Task<SignInResult> DoTwoFactorSignInAsync(TUser user, TwoFactorAut
545627 /// <returns>The task object representing the asynchronous operation containing the <see name="SignInResult"/>
546628 /// for the sign-in attempt.</returns>
547629 public virtual async Task < SignInResult > TwoFactorAuthenticatorSignInAsync ( string code , bool isPersistent , bool rememberClient )
630+ {
631+ try
632+ {
633+ var result = await TwoFactorAuthenticatorSignInCoreAsync ( code , isPersistent , rememberClient ) ;
634+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result , SignInType . TwoFactorAuthenticator , isPersistent ) ;
635+
636+ return result ;
637+ }
638+ catch ( Exception ex )
639+ {
640+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result : null , SignInType . TwoFactorAuthenticator , isPersistent , ex ) ;
641+ throw ;
642+ }
643+ }
644+
645+ private async Task < SignInResult > TwoFactorAuthenticatorSignInCoreAsync ( string code , bool isPersistent , bool rememberClient )
548646 {
549647 var twoFactorInfo = await RetrieveTwoFactorInfoAsync ( ) ;
550648 if ( twoFactorInfo == null )
@@ -593,6 +691,22 @@ public virtual async Task<SignInResult> TwoFactorAuthenticatorSignInAsync(string
593691 /// <returns>The task object representing the asynchronous operation containing the <see name="SignInResult"/>
594692 /// for the sign-in attempt.</returns>
595693 public virtual async Task < SignInResult > TwoFactorSignInAsync ( string provider , string code , bool isPersistent , bool rememberClient )
694+ {
695+ try
696+ {
697+ var result = await TwoFactorSignInCoreAsync ( provider , code , isPersistent , rememberClient ) ;
698+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result , SignInType . TwoFactor , isPersistent ) ;
699+
700+ return result ;
701+ }
702+ catch ( Exception ex )
703+ {
704+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result : null , SignInType . TwoFactor , isPersistent , ex ) ;
705+ throw ;
706+ }
707+ }
708+
709+ private async Task < SignInResult > TwoFactorSignInCoreAsync ( string provider , string code , bool isPersistent , bool rememberClient )
596710 {
597711 var twoFactorInfo = await RetrieveTwoFactorInfoAsync ( ) ;
598712 if ( twoFactorInfo == null )
@@ -666,6 +780,22 @@ public virtual Task<SignInResult> ExternalLoginSignInAsync(string loginProvider,
666780 /// <returns>The task object representing the asynchronous operation containing the <see name="SignInResult"/>
667781 /// for the sign-in attempt.</returns>
668782 public virtual async Task < SignInResult > ExternalLoginSignInAsync ( string loginProvider , string providerKey , bool isPersistent , bool bypassTwoFactor )
783+ {
784+ try
785+ {
786+ var result = await ExternalLoginSignInCoreAsync ( loginProvider , providerKey , isPersistent , bypassTwoFactor ) ;
787+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result , SignInType . External , isPersistent ) ;
788+
789+ return result ;
790+ }
791+ catch ( Exception ex )
792+ {
793+ _metrics ? . SignIn ( typeof ( TUser ) . FullName ! , AuthenticationScheme , result : null , SignInType . External , isPersistent , ex ) ;
794+ throw ;
795+ }
796+ }
797+
798+ private async Task < SignInResult > ExternalLoginSignInCoreAsync ( string loginProvider , string providerKey , bool isPersistent , bool bypassTwoFactor )
669799 {
670800 var user = await UserManager . FindByLoginAsync ( loginProvider , providerKey ) ;
671801 if ( user == null )
0 commit comments