Calling PasswordSignIn and SendTwoFactorCode in the same request












2















I would like to implement the following flow for two factor authentication in asp.net mvc:



var res = sign.PasswordSignIn("myusername", "mypassword", false, false);
if(res == SignInStatus.RequiresVerification)
sign.SendTwoFactorCode("EmailCode");


However I'm finding that the SendTwoFactorCode function is returning false and not sending the email because internally it is checking if the user is verified. See this line in the source. If I make a second request the call to SendTwoFactorCode works as I'm expecting.



Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?










share|improve this question























  • Has to do with cookies as part of the auth flow. Have you tried redirecting as suggested in the docs docs.microsoft.com/en-us/aspnet/identity/overview/features-api/…

    – Nkosi
    Nov 26 '18 at 20:38
















2















I would like to implement the following flow for two factor authentication in asp.net mvc:



var res = sign.PasswordSignIn("myusername", "mypassword", false, false);
if(res == SignInStatus.RequiresVerification)
sign.SendTwoFactorCode("EmailCode");


However I'm finding that the SendTwoFactorCode function is returning false and not sending the email because internally it is checking if the user is verified. See this line in the source. If I make a second request the call to SendTwoFactorCode works as I'm expecting.



Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?










share|improve this question























  • Has to do with cookies as part of the auth flow. Have you tried redirecting as suggested in the docs docs.microsoft.com/en-us/aspnet/identity/overview/features-api/…

    – Nkosi
    Nov 26 '18 at 20:38














2












2








2


1






I would like to implement the following flow for two factor authentication in asp.net mvc:



var res = sign.PasswordSignIn("myusername", "mypassword", false, false);
if(res == SignInStatus.RequiresVerification)
sign.SendTwoFactorCode("EmailCode");


However I'm finding that the SendTwoFactorCode function is returning false and not sending the email because internally it is checking if the user is verified. See this line in the source. If I make a second request the call to SendTwoFactorCode works as I'm expecting.



Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?










share|improve this question














I would like to implement the following flow for two factor authentication in asp.net mvc:



var res = sign.PasswordSignIn("myusername", "mypassword", false, false);
if(res == SignInStatus.RequiresVerification)
sign.SendTwoFactorCode("EmailCode");


However I'm finding that the SendTwoFactorCode function is returning false and not sending the email because internally it is checking if the user is verified. See this line in the source. If I make a second request the call to SendTwoFactorCode works as I'm expecting.



Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?







asp.net-mvc-4 asp.net-identity two-factor-authentication






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 22 '18 at 0:55









theycallmemortytheycallmemorty

7,240114366




7,240114366













  • Has to do with cookies as part of the auth flow. Have you tried redirecting as suggested in the docs docs.microsoft.com/en-us/aspnet/identity/overview/features-api/…

    – Nkosi
    Nov 26 '18 at 20:38



















  • Has to do with cookies as part of the auth flow. Have you tried redirecting as suggested in the docs docs.microsoft.com/en-us/aspnet/identity/overview/features-api/…

    – Nkosi
    Nov 26 '18 at 20:38

















Has to do with cookies as part of the auth flow. Have you tried redirecting as suggested in the docs docs.microsoft.com/en-us/aspnet/identity/overview/features-api/…

– Nkosi
Nov 26 '18 at 20:38





Has to do with cookies as part of the auth flow. Have you tried redirecting as suggested in the docs docs.microsoft.com/en-us/aspnet/identity/overview/features-api/…

– Nkosi
Nov 26 '18 at 20:38












1 Answer
1






active

oldest

votes


















5





+250










Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?




Short answer: No



Suggested alternative:



The flow usually suggested in documentation is




  1. Authenticate user via username and password.

  2. If valid user and password, and requires additional verification because 2FA is enabled then redirect to the page to Send the code.

  3. If user initiates the sending of the code, the code is sent and the user is then redirected to the verification page.


The second step is usually to confirm a provider, if there are multiple (ie SMS, email,...etc), to use for 2nd factor verification.



For example, the following does the redirect on RequiresVerification result



Account/Login



//...

var result = await signInManager.PasswordSignInAsync(username, password, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}


But since you have already determined that you are going to send the code via email then you can skip the second step and redirect directly to the verify code which can be where the code is sent and verified.



Account/VerifyCode



[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string returnUrl) {
var provider = "EmailCode";
// Require that the user has already logged in via username/password
var userId = await signInManager.GetVerifiedUserIdAsync();
if (userId == null) {
return View("Error");
}
// Generate the token and send it
if(!await signInManager.SendTwoFactorCodeAsync(provider)) {
return View("Error");
}

var model = new VerifyCodeViewModel {
ReturnUrl = returnUrl
};

return View(model);
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) {
var provider = "EmailCode";
if (!ModelState.IsValid) {
return View(model);
}

var result = await signInManager.TwoFactorSignInAsync(provider, model.Code, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}


This should allow the TwoFactorCookie to be included in the next request so that GetVerifiedUserIdAsync behaves as expected.



/// <summary>
/// Get the user id that has been verified already or null.
/// </summary>
/// <returns></returns>
public async Task<TKey> GetVerifiedUserIdAsync()
{
var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture();
if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId()))
{
return ConvertIdFromString(result.Identity.GetUserId());
}
return default(TKey);
}


Source



Just like when you indicated




If I make a second request the call to SendTwoFactorCode works as I'm expecting.




That second request is important as it will include the cookie set in the previous request.



Reference How SignInManager checks for 2FA requirement






share|improve this answer


























  • Thanks, I'm disappointed our pals at MS chose to make it work this way but it doesn't seem like there is a viable alternative.

    – theycallmemorty
    Dec 5 '18 at 20:53











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53422493%2fcalling-passwordsignin-and-sendtwofactorcode-in-the-same-request%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









5





+250










Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?




Short answer: No



Suggested alternative:



The flow usually suggested in documentation is




  1. Authenticate user via username and password.

  2. If valid user and password, and requires additional verification because 2FA is enabled then redirect to the page to Send the code.

  3. If user initiates the sending of the code, the code is sent and the user is then redirected to the verification page.


The second step is usually to confirm a provider, if there are multiple (ie SMS, email,...etc), to use for 2nd factor verification.



For example, the following does the redirect on RequiresVerification result



Account/Login



//...

var result = await signInManager.PasswordSignInAsync(username, password, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}


But since you have already determined that you are going to send the code via email then you can skip the second step and redirect directly to the verify code which can be where the code is sent and verified.



Account/VerifyCode



[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string returnUrl) {
var provider = "EmailCode";
// Require that the user has already logged in via username/password
var userId = await signInManager.GetVerifiedUserIdAsync();
if (userId == null) {
return View("Error");
}
// Generate the token and send it
if(!await signInManager.SendTwoFactorCodeAsync(provider)) {
return View("Error");
}

var model = new VerifyCodeViewModel {
ReturnUrl = returnUrl
};

return View(model);
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) {
var provider = "EmailCode";
if (!ModelState.IsValid) {
return View(model);
}

var result = await signInManager.TwoFactorSignInAsync(provider, model.Code, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}


This should allow the TwoFactorCookie to be included in the next request so that GetVerifiedUserIdAsync behaves as expected.



/// <summary>
/// Get the user id that has been verified already or null.
/// </summary>
/// <returns></returns>
public async Task<TKey> GetVerifiedUserIdAsync()
{
var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture();
if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId()))
{
return ConvertIdFromString(result.Identity.GetUserId());
}
return default(TKey);
}


Source



Just like when you indicated




If I make a second request the call to SendTwoFactorCode works as I'm expecting.




That second request is important as it will include the cookie set in the previous request.



Reference How SignInManager checks for 2FA requirement






share|improve this answer


























  • Thanks, I'm disappointed our pals at MS chose to make it work this way but it doesn't seem like there is a viable alternative.

    – theycallmemorty
    Dec 5 '18 at 20:53
















5





+250










Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?




Short answer: No



Suggested alternative:



The flow usually suggested in documentation is




  1. Authenticate user via username and password.

  2. If valid user and password, and requires additional verification because 2FA is enabled then redirect to the page to Send the code.

  3. If user initiates the sending of the code, the code is sent and the user is then redirected to the verification page.


The second step is usually to confirm a provider, if there are multiple (ie SMS, email,...etc), to use for 2nd factor verification.



For example, the following does the redirect on RequiresVerification result



Account/Login



//...

var result = await signInManager.PasswordSignInAsync(username, password, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}


But since you have already determined that you are going to send the code via email then you can skip the second step and redirect directly to the verify code which can be where the code is sent and verified.



Account/VerifyCode



[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string returnUrl) {
var provider = "EmailCode";
// Require that the user has already logged in via username/password
var userId = await signInManager.GetVerifiedUserIdAsync();
if (userId == null) {
return View("Error");
}
// Generate the token and send it
if(!await signInManager.SendTwoFactorCodeAsync(provider)) {
return View("Error");
}

var model = new VerifyCodeViewModel {
ReturnUrl = returnUrl
};

return View(model);
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) {
var provider = "EmailCode";
if (!ModelState.IsValid) {
return View(model);
}

var result = await signInManager.TwoFactorSignInAsync(provider, model.Code, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}


This should allow the TwoFactorCookie to be included in the next request so that GetVerifiedUserIdAsync behaves as expected.



/// <summary>
/// Get the user id that has been verified already or null.
/// </summary>
/// <returns></returns>
public async Task<TKey> GetVerifiedUserIdAsync()
{
var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture();
if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId()))
{
return ConvertIdFromString(result.Identity.GetUserId());
}
return default(TKey);
}


Source



Just like when you indicated




If I make a second request the call to SendTwoFactorCode works as I'm expecting.




That second request is important as it will include the cookie set in the previous request.



Reference How SignInManager checks for 2FA requirement






share|improve this answer


























  • Thanks, I'm disappointed our pals at MS chose to make it work this way but it doesn't seem like there is a viable alternative.

    – theycallmemorty
    Dec 5 '18 at 20:53














5





+250







5





+250



5




+250






Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?




Short answer: No



Suggested alternative:



The flow usually suggested in documentation is




  1. Authenticate user via username and password.

  2. If valid user and password, and requires additional verification because 2FA is enabled then redirect to the page to Send the code.

  3. If user initiates the sending of the code, the code is sent and the user is then redirected to the verification page.


The second step is usually to confirm a provider, if there are multiple (ie SMS, email,...etc), to use for 2nd factor verification.



For example, the following does the redirect on RequiresVerification result



Account/Login



//...

var result = await signInManager.PasswordSignInAsync(username, password, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}


But since you have already determined that you are going to send the code via email then you can skip the second step and redirect directly to the verify code which can be where the code is sent and verified.



Account/VerifyCode



[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string returnUrl) {
var provider = "EmailCode";
// Require that the user has already logged in via username/password
var userId = await signInManager.GetVerifiedUserIdAsync();
if (userId == null) {
return View("Error");
}
// Generate the token and send it
if(!await signInManager.SendTwoFactorCodeAsync(provider)) {
return View("Error");
}

var model = new VerifyCodeViewModel {
ReturnUrl = returnUrl
};

return View(model);
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) {
var provider = "EmailCode";
if (!ModelState.IsValid) {
return View(model);
}

var result = await signInManager.TwoFactorSignInAsync(provider, model.Code, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}


This should allow the TwoFactorCookie to be included in the next request so that GetVerifiedUserIdAsync behaves as expected.



/// <summary>
/// Get the user id that has been verified already or null.
/// </summary>
/// <returns></returns>
public async Task<TKey> GetVerifiedUserIdAsync()
{
var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture();
if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId()))
{
return ConvertIdFromString(result.Identity.GetUserId());
}
return default(TKey);
}


Source



Just like when you indicated




If I make a second request the call to SendTwoFactorCode works as I'm expecting.




That second request is important as it will include the cookie set in the previous request.



Reference How SignInManager checks for 2FA requirement






share|improve this answer
















Is there a way to make SendTwoFactorCode work correctly immediately after a call to PasswordSignIn?




Short answer: No



Suggested alternative:



The flow usually suggested in documentation is




  1. Authenticate user via username and password.

  2. If valid user and password, and requires additional verification because 2FA is enabled then redirect to the page to Send the code.

  3. If user initiates the sending of the code, the code is sent and the user is then redirected to the verification page.


The second step is usually to confirm a provider, if there are multiple (ie SMS, email,...etc), to use for 2nd factor verification.



For example, the following does the redirect on RequiresVerification result



Account/Login



//...

var result = await signInManager.PasswordSignInAsync(username, password, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}


But since you have already determined that you are going to send the code via email then you can skip the second step and redirect directly to the verify code which can be where the code is sent and verified.



Account/VerifyCode



[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string returnUrl) {
var provider = "EmailCode";
// Require that the user has already logged in via username/password
var userId = await signInManager.GetVerifiedUserIdAsync();
if (userId == null) {
return View("Error");
}
// Generate the token and send it
if(!await signInManager.SendTwoFactorCodeAsync(provider)) {
return View("Error");
}

var model = new VerifyCodeViewModel {
ReturnUrl = returnUrl
};

return View(model);
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) {
var provider = "EmailCode";
if (!ModelState.IsValid) {
return View(model);
}

var result = await signInManager.TwoFactorSignInAsync(provider, model.Code, false, false);
switch (result) {
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("VerifyCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}


This should allow the TwoFactorCookie to be included in the next request so that GetVerifiedUserIdAsync behaves as expected.



/// <summary>
/// Get the user id that has been verified already or null.
/// </summary>
/// <returns></returns>
public async Task<TKey> GetVerifiedUserIdAsync()
{
var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture();
if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId()))
{
return ConvertIdFromString(result.Identity.GetUserId());
}
return default(TKey);
}


Source



Just like when you indicated




If I make a second request the call to SendTwoFactorCode works as I'm expecting.




That second request is important as it will include the cookie set in the previous request.



Reference How SignInManager checks for 2FA requirement







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 26 '18 at 22:53

























answered Nov 26 '18 at 22:22









NkosiNkosi

111k16123189




111k16123189













  • Thanks, I'm disappointed our pals at MS chose to make it work this way but it doesn't seem like there is a viable alternative.

    – theycallmemorty
    Dec 5 '18 at 20:53



















  • Thanks, I'm disappointed our pals at MS chose to make it work this way but it doesn't seem like there is a viable alternative.

    – theycallmemorty
    Dec 5 '18 at 20:53

















Thanks, I'm disappointed our pals at MS chose to make it work this way but it doesn't seem like there is a viable alternative.

– theycallmemorty
Dec 5 '18 at 20:53





Thanks, I'm disappointed our pals at MS chose to make it work this way but it doesn't seem like there is a viable alternative.

– theycallmemorty
Dec 5 '18 at 20:53


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53422493%2fcalling-passwordsignin-and-sendtwofactorcode-in-the-same-request%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

TypeError: fit_transform() missing 1 required positional argument: 'X'