SignInStatus always returns Success on TwoFactorAuthentication is enabled in webapi using asp.net identity - asp.net-web-api

I am implementing 2 factor authentication in WebApi, asp.net identity and OWIN. Every time I log in, I get SignInStatus = Success never reaches to SignInStatus = RequiresVerification though user TwoFactorAuthentication is enabled.
Below are some code snippets,
Startup.cs:
private void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
app.UseOAuthBearerTokens(OAuthOptions);
}
Action method for enabling two factor authentication,
[HttpPost]
public async Task<IHttpActionResult> EnableTwoFactorAuthentication()
{
var user = await this.AppUserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
IdentityResult result = await this.AppUserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
await this.AppSignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
}
return Ok();
}
Please suggest a solution.

If you get stuck here, one way to solve the problem is to copy the methods from SignInManager directly into your code and call those instead so you can step through the methods and see why you are getting the wrong status. For me the problem ended up being that I instantiated my UserManager with:
new MyUserManager()
instead of the right way:
HttpContext.GetOwinContext().Get<MyUserManager>()
I was using this as my template for setting it up:
https://github.com/adamtuliper/ASP.NET-Identity-Samples/tree/master/BasicTemplate%20-%20Two%20Factor/BasicTemplate

SignInManager return RequiresVerification if :
dbo.ASpnetUsers has for user set to true TwoFactorEnabled and EmailConfirmed and user email should be confirmed, email not be empty or null.
var result = SignInManager.PasswordSignIn(usernameIdentity, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", returnUrl);
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}

Related

Correct Implementation of Forgot Password AspNetBoilerPlate

Im using aspnetboilerplate (MVC) and wanted to implement a forgot password feature to allow the user to reset their own passwords using a link on the login screen.
I imagine this to work by generating a password reset code which is then emailed to the user.The user follows the link and is taken to a screen allowing them to reset the password.
Im stuck at the initial stage. i started with a copy of the login action after noticing that when attempting to log in the user object was returned. From here i attempt to set a password reset code.
[HttpPost]
[UnitOfWork]
public virtual async Task<JsonResult> ForgotPassword(ForgotPasswordViewModel forgotPasswordModel, string returnUrl = "", string returnUrlHash = "")
{
returnUrl = NormalizeReturnUrl(returnUrl);
if (!string.IsNullOrWhiteSpace(returnUrlHash))
{
returnUrl = returnUrl + returnUrlHash;
}
var loginResult = await _logInManager.LoginAsync(forgotPasswordModel.UsernameOrEmailAddress, "ForgotPassword", GetTenancyNameOrNull());
loginResult.User.SetNewPasswordResetCode();
switch (loginResult.Result)
{
case AbpLoginResultType.Success:
return Json(loginResult);
default:
throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, forgotPasswordModel.UsernameOrEmailAddress, GetTenancyNameOrNull());
}
}
Checking the AbpUser table after the
loginResult.User.SetNewPasswordResetCode();
i cannot see any password reset code for the user, they are all null.
Could someone point me in the right direction.
Thanks in advance
Thanks to answer below for being correct, just for completion below is exactly what worked. Obviously ignore the json return at the end
public virtual async Task<JsonResult> ForgotPassword(ForgotPasswordViewModel forgotPasswordModel, string returnUrl = "", string returnUrlHash = "")
{
//var user = await GetUserByChecking(emailAddress);
var user = await _userManager.FindByEmailAsync(forgotPasswordModel.UsernameOrEmailAddress);
if (user == null)
{
throw new UserFriendlyException("User not found!");
}
user.SetNewPasswordResetCode();
//Send an email to user with the below password reset code
/* Uri.EscapeDataString(user.PasswordResetCode) */
return Json("");
}
public class AccountAppService: IAccountAppService
{
public UserManager UserManager {get; set; }
public async Task SendPasswordResetCode(string emailAddress)
{
var user = await UserManager.FindByEmailAsync(emailAddress);
if (user == null)
{
throw new UserFriendlyException("User not found!");
}
user.SetNewPasswordResetCode();
//Send an email to user with the below password reset code
/* Uri.EscapeDataString(user.PasswordResetCode) */
}
}

How to redirect from Identity Area to Admin in ASP.NET CORE 2

I cant redirect from Identity Area:
if (role=="Admin")
{
return RedirectToAction("Index","Home",new { Area=Input.Role ,id=9});
}
To Admin Area Controller-Home,Action-Index.Always redirect me to Index in the Identity Area;
looking at your code I am still scratching my head as to the reason that someone would specify the Role at login. Can you articulate the reasoning behind this?
Simplest answer is inline with the code within the OnPostAsync(); that resides in
//this because of the routes you have in StartUp.cs
[Authorize(Roles ="Admin")]
[Area("admin")]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
Login.cs Page...
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
var user = await userManager.GetUserAsync(User); // Claims Principle
if (await userManager.IsInRoleAsync(user, "Admin"))
{
//SIMPLEST ANSWER since you using mixed environment with PAGES
return LocalRedirect("~/admin");
}
//TODO:
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
Check your issues below one by one:
I got error A method 'CakeStore.App.Areas.Admin.Controllers.HomeController.Index (CakeStore.App)' must not define attribute routed actions and non attribute routed actions at the same time, you should not define [HttpGet(Name ="AdminPanel")] and [Route(nameof(Admin) + "/[controller]")] at the same time.
//[HttpGet(Name ="AdminPanel")]
[Area(nameof(Admin))]
[Route(nameof(Admin) + "/[controller]")]
public IActionResult Index()
{
return View();
}
For var role = this.roleManage.GetUrl(Input.Username);, it will retrive the role by username, check whether you got expected role Admin.
return RedirectToAction("Index","Home",new { Area=Input.Role ,id=9});, you did not define id in Index, there is no need to add id route.

invalid_grant of OAuthAuthorizationServerProvider

I'm working on writing fully customized ASP.NET Identity for my WebAPi.
I have rewritten my own derived OAuthAuthorizationServerProvider in this way:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Check User availability ...
//ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
// if i couldn't found user in my DataBase ...
//if (user == null)
//{
//context.SetError("invalid_grant", "The user name or password is incorrect.");
// return;
//}
context.Validated();
}
}
GrantResourceOwnerCredentials just returns an invalid_grant error for each calls. i want to handle it but, i don't know how.
ValidateClientAuthentication is where you would do your authentication checks and this is where you throw errors if anything doesn't match.
move your code there and do the checks before you call context.Validated(). You only call the Validate method once you make sure everything is validated correctly.
here is an example of such an implementation I did a while back:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
//first try to get the client details from the Authorization Basic header
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
//no details in the Authorization Header so try to find matching post values
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("client_not_authorized", "invalid client details");
return Task.FromResult<object>(null);
}
var dataLayer = new RepoManager(new DataLayerDapper()).DataLayer;
var audienceDto = dataLayer.GetAudience(clientId);
if (audienceDto == null || !clientSecret.Equals(audienceDto.Secret))
{
context.SetError("unauthorized_client", "unauthorized client");
return Task.FromResult<object>(null);
}
context.Validated();
return Task.FromResult<object>(null);
}
Notice how the checks happen in order and certain errors are raised with some appropriate errors.
This code takes a client id and client secret from an authorization header but you can easily drop all that and replace it with your own checks and database calls.
The important part is that this is where you deal with stuff like this and this is where you set the errors so your clients know what's going on.
GrantResourceOwnerCredentials this is where you get once the call is properly authenticated, at which point you can start creating tokens, adding claims and creating the authentication ticket. This method does not get hit if the previous one fails to authenticate the request.
Here is a working example:
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var identity = new ClaimsIdentity("JWT");
identity.AddClaim(new Claim("clientID", context.ClientId));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"audience", context.ClientId
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
return Task.FromResult<object>(null);
}
Now, if you get an invalid grant error that usually happens because you either didn't set up the grant_type in your initial call or you set up the wrong value.
in my case I had to setup this:
"grant_type", "password"

How to add default role to Asp.net identity user manager

I am new and exploring the Asp.net Identity, I wanted to add a default Role to my users. However, I failed to run this code during runtime and it shows me this error "Role admin does not exist."
var userManager = new UserManager<IdentityUser>(userStore);
var user = userManager.Find(UserName.Text, Password.Text);
userManager.AddToRole(user.Id,"admin");
I am aware that i didn't created the Role of "admin", Does there any Simplest way to implement this Role based authorization?
Here is how it should be done, first check if the role exists, and if it doesn't add the role.
public async Task<ActionResult> Register(RegisterViewModel model, ApplicationDbContext context)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
if (!roleManager.RoleExists("Administrator"))
{
await roleManager.CreateAsync(new IdentityRole("Administrator"));
}
await UserManager.AddToRoleAsync(user.Id, "Administrator");
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Notice how the ApplicationDbContext is passed in the method signature.

WebSecurity vs FormsAuthentication in ASP.NET MVC4

I guess I am trying to mix two providers in project but I am looking to use websecurity in conjunction to my forms authentication. I need websecurity for OAUTH authentication using Facebook, and google.
The error that I am getting when I try to login using facebook is
To call this method, the Membership.Provider property must be an instance of ExtendedMembershipProvider.
Here are the code samples. How can I use both?
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
{
return RedirectToLocal(returnUrl);
}
if (User.Identity.IsAuthenticated)
{
// If the current user is logged in add the new account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
return RedirectToLocal(returnUrl);
}
else
{
// User is new, ask for their desired membership name
string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
ViewBag.ReturnUrl = returnUrl;
return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });
}
}
and
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Could possibly be related to the same issue as me MVC4 ExtendedMembershipProvider and entityframework
.. I removed the universal providers nuget package and this particular error dissappeared.
Also this "very recent" article by Jon Galloway may help.
If you are using Visual Studio, you might want to save yourself all this effort. The MVC 4 Internet template comes with four external identity providers out of the box. I have tested them and Google Accounts, Microsoft account, Facebook login, and Twitter login all work fine, with zero lines of code!
I think the same is provided with the Web Form template too.
More info at http://blogs.msdn.com/b/webdev/archive/2012/08/15/oauth-openid-support-for-webforms-mvc-and-webpages.aspx.
You can use an implementation of ExtendedMembershipProvider. For ex: the built-in SimpleMembershipProvider.
Every ExtendedMembershipProvider IS A MembershipProvider .
Read more at Jon Galloway's Blog.

Resources