diff --git a/src/Microsoft.AspNetCore.Identity/SignInManager.cs b/src/Microsoft.AspNetCore.Identity/SignInManager.cs
index e67106ab76..26840f65d0 100644
--- a/src/Microsoft.AspNetCore.Identity/SignInManager.cs
+++ b/src/Microsoft.AspNetCore.Identity/SignInManager.cs
@@ -433,7 +433,19 @@ namespace Microsoft.AspNetCore.Identity
/// Flag indicating whether the sign-in cookie should persist after the browser is closed.
/// The task object representing the asynchronous operation containing the
/// for the sign-in attempt.
- public virtual async Task ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent)
+ public virtual Task ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent)
+ => ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent, bypassTwoFactor: false);
+
+ ///
+ /// Signs in a user via a previously registered third party login, as an asynchronous operation.
+ ///
+ /// The login provider to use.
+ /// The unique provider identifier for the user.
+ /// Flag indicating whether the sign-in cookie should persist after the browser is closed.
+ /// Flag indicating whether to bypass two factor authentication.
+ /// The task object representing the asynchronous operation containing the
+ /// for the sign-in attempt.
+ public virtual async Task ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent, bool bypassTwoFactor)
{
var user = await UserManager.FindByLoginAsync(loginProvider, providerKey);
if (user == null)
@@ -446,7 +458,7 @@ namespace Microsoft.AspNetCore.Identity
{
return error;
}
- return await SignInOrTwoFactorAsync(user, isPersistent, loginProvider);
+ return await SignInOrTwoFactorAsync(user, isPersistent, loginProvider, bypassTwoFactor);
}
///
@@ -582,9 +594,10 @@ namespace Microsoft.AspNetCore.Identity
}
- private async Task SignInOrTwoFactorAsync(TUser user, bool isPersistent, string loginProvider = null)
+ private async Task SignInOrTwoFactorAsync(TUser user, bool isPersistent, string loginProvider = null, bool bypassTwoFactor = false)
{
- if (UserManager.SupportsUserTwoFactor &&
+ if (!bypassTwoFactor &&
+ UserManager.SupportsUserTwoFactor &&
await UserManager.GetTwoFactorEnabledAsync(user) &&
(await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0)
{
diff --git a/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs b/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs
index 929ec4b437..df1ae35d79 100644
--- a/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs
+++ b/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs
@@ -314,6 +314,52 @@ namespace Microsoft.AspNetCore.Identity.Test
auth.Verify();
}
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task ExternalSignInRequiresVerificationIfNotBypassed(bool bypass)
+ {
+ // Setup
+ var user = new TestUser { UserName = "Foo" };
+ const string loginProvider = "login";
+ const string providerKey = "fookey";
+ var manager = SetupUserManager(user);
+ manager.Setup(m => m.SupportsUserLockout).Returns(false).Verifiable();
+ manager.Setup(m => m.FindByLoginAsync(loginProvider, providerKey)).ReturnsAsync(user).Verifiable();
+ if (!bypass)
+ {
+ IList providers = new List();
+ providers.Add("PhoneNumber");
+ manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user)).Returns(Task.FromResult(providers)).Verifiable();
+ manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
+ manager.Setup(m => m.GetTwoFactorEnabledAsync(user)).ReturnsAsync(true).Verifiable();
+ }
+ var context = new Mock();
+ var auth = new Mock();
+ context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
+ var helper = SetupSignInManager(manager.Object, context.Object);
+
+ if (bypass)
+ {
+ SetupSignIn(auth, user.Id, false, loginProvider);
+ }
+ else
+ {
+ auth.Setup(a => a.SignInAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme,
+ It.Is(id => id.FindFirstValue(ClaimTypes.Name) == user.Id))).Returns(Task.FromResult(0)).Verifiable();
+ }
+
+ // Act
+ var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent: false, bypassTwoFactor: bypass);
+
+ // Assert
+ Assert.Equal(bypass, result.Succeeded);
+ Assert.Equal(!bypass, result.RequiresTwoFactor);
+ manager.Verify();
+ context.Verify();
+ auth.Verify();
+ }
+
[Theory]
[InlineData(true, true)]
[InlineData(true, false)]