If I'm trying to send an email immediately a new email has been confirmed, how to I go about it ?
The piece of code below is what I have in ConfirmEmail method. When I run the code, I get Exception Details: System.NullReferenceException: Object reference not set to an instance of an object. on await UserManager.SendEmailAsync(userId, "API", "API Code is: " + user.ApiCode);
I think UserManager.FindById is not returning anything, but not sure how to go about it.
Any pointers/ideas will help.
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
var user = UserManager.FindById(User.Identity.GetUserId());
await UserManager.SendEmailAsync(userId, "API", "API Code is: " + user.ApiCode);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
With the info you provided I guess maybe the problem is that the user is not authenticated and therefore User.Identity will return null, hence the NullReferenceException.
You could try to fetch the user information with the id you're receiving in the action parameter userId instead.
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
//var user = UserManager.FindById(User.Identity.GetUserId());
var user = UserManager.FindById(userId); //use the received parameter to get the user
await UserManager.SendEmailAsync(userId, "API", "API Code is: " + user.ApiCode);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
Hope this helps!
Related
I have a controller method from which I would like to return to my angular client some error condition when account object is null. I used exception below and it works, but is there a better way of reporting error string to the angular client then the below one.
[Authorize]
[HttpPost("get-schedule-from-pool/{id}")]
public ActionResult<AccountResponse> GetScheduleFromPool(int id, UpdateScheduleRequest scheduleReq)
{
// users can update their own account and admins can update any account
if (id != Account.Id && Account.Role != Role.Admin)
return Unauthorized(new { message = "Unauthorized" });
var account = _accountService.GetScheduleFromPool(id, scheduleReq);
if(account == null)
{
// Request.Body.
// return HttpError("Function :" + scheduleReq.UserFunction + " for date: " + scheduleReq.Date + " already taken");
throw new Exception("Sample exception.");
}
return Ok(account);
}
If you want to return a error message or error code to the client there's a few ways to do it.
Using StatusCode:
return StatusCode(StatusCodes.Status500InternalServerError,"Details");
or
return StatusCode(500,"Details")
Or using the default wrappers:
return Problem("Details");
Here a list of all error codes and what they mean.
I am creating a small demo for reset password in web API and identity using in .net MVC c#.and I am clicking on forgot password send one code in the mail using a query string. In the mail code is correct getting. now I am going for the change password and getting the code from the URL not getting propare in the token '==' last end not getting and get error invalid token how can do fix it this error anyone knows?
this my ForgotPassword method in Account Controller :
public async Task<IHttpActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
try
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
return Ok();
}
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
await UserManager.SendEmailAsync(user.Id, "Reset Password",Request.RequestUri.GetLeftPart(UriPartial.Authority) + "/home/ChangePassword?Code=" + code);
return Ok("Ok");
}
catch (Exception ex)
{
ExceptionsLogging.SendExcepToDB(ex);
throw new HttpException(500, ex.Message);
}
}
this is my ResetPasswordMethod :
public async Task<IHttpActionResult> ResetPassword(ChangePasswordBindingModel model)
{
try
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return Ok();
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.NewPassword); // here not getting code propare
if (result.Succeeded)
{
return Ok("Done");
}
return Ok();
}
catch (Exception ex)
{
ExceptionsLogging.SendExcepToDB(ex);
throw new HttpException(500, ex.Message);
}
}
any one know how can fix it i am try encode/decode but now getting propare
You must encode the generated token before sending the email
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var encodedCode = HttpUtility.UrlEncode(code);
await UserManager.SendEmailAsync(user.Id, "Reset Password",Request.RequestUri.GetLeftPart(UriPartial.Authority) + "/home/ChangePassword?Code=" + encodedCode);
return Ok("Ok");
I have an Response object that I want to return to the user if there is an exception in the controller. However when i try to send back BadRequest i cant seem to send back my Response object. So my question is how do i edit BadRequst to contain my Response object and/or how do i send back my Response object with an error status code ?
Controller
public async Task<IActionResult> Login([FromBody] LoginViewModel model) {
Response response = new Response();
try {
if (ModelState.IsValid) {
var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) {
logger.LogInformation(1, "User logged in.");
return Json("User logged in.");
}
if (result.RequiresTwoFactor) {
response.id = 1 ;
throw new LoginException("Login requiest two factor", new InvalidOperationException());
}
if (result.IsLockedOut) {
response.id = 2 ;
logger.LogWarning(2, "User account locked out.");
throw new LoginException("User account locked out", new InvalidOperationException());
}
else {
response.id = 3 ;
throw new LoginException("Invalid login attempt", new InvalidOperationException());
}
}
response.id = 4 ;
var modelErrors = ModelState.Values.ToList();
throw new LoginException("Model State Error", ModelState, new InvalidOperationException());
} catch (LoginException ex){
return BadRequest(response); // response with status: 400 Bad Request for URL, no response object is in here.
}
}
Front-End
login(email:any, password:any, remember:any){
//let body:User = {email:email, password:password};
let headers = new Headers({ 'Content-Type': 'application/json' });
let body = {Email:email, Password:password,RememberMe:false };
console.log(body);
this.http.post('/api/Account/Login', body ,{headers:headers})
.map(response => response.json())
.subscribe(
response => {
console.log("Success !!!:\n"+response);
this.router.navigate(['/home']);
},
error => {
console.log("Error !!!:\n"+error);
}
);
}
It's not really clear what you're asking?
You only want to return status code 400? Use the parameterless version of return BadRequest()
Want a different status code? Use different Method. BadRequest is named by the HTTP Statuscode 400 (BadRequest). If you want to set the status code yourself, use return Status(HttpStatusCode.Unauthorized); or whatever.
Want to return a error message as json? Use error classes or annonymous class to do so:
return BadRequest(new { ErrorMessage = "Account is locked." });
But whatever you want to do, throwing exceptions in Controller is wrong. An controller action should NEVER throw an exception. Also as pointed in the comments, exceptions are to be used for exceptional cases. A wrong password, locked user or wrong authentication method (i.e. no two factor auth when its required) are expected errors and shouldn't be handled via excpetions.
Exceptions (when thrown only) are inherently expensive operations in a computer program. For that reason Microsoft implemented an IdentityResult class to return a list of expected errors, rather than throwing exceptions during the call of signInManager.PasswordSignInAsync
Instead of returning IActionResult why don't you return a HttpResponseMessage and then you can do something like:
return Request.CreateResponse(System.Net.HttpStatusCode.BadRequest)
Not sure how best to post this, but it is to save anyone else from hours of searching trying to set up login validation using asp.net mvc identity 2.0
The code below works, but if anyone else can improve on it please do so and save others from hours of searching.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
//*******************************************************
// Check if email exists, if not no point in continuing
//*******************************************************
var user = await UserManager.FindByEmailAsync(model.Email);
if (user != null)
{
// var checkIfUsernamePasswordValid = await UserManager.FindAsync(model.Email,model.Password);
//*******************************************************
// As password is hashed, need to use line below to return success
//*******************************************************
var checkIfUsernamePasswordValid = UserManager.PasswordHasher.VerifyHashedPassword(user.PasswordHash, model.Password);
//*******************************************************
//To stop user from login in before email is confirmed, use line below
//*******************************************************
var emailIsConfirmed = await UserManager.IsEmailConfirmedAsync(user.Id);
if (!emailIsConfirmed)
{
ModelState.AddModelError("", string.Format("Please check your email inbox and verify email address before trying to login"));
return View();
}
//*******************************************************
// Check if user is locked out due to invalid login attempts,
// If they are inform them
//*******************************************************
if (await UserManager.IsLockedOutAsync(user.Id))
{
ModelState.AddModelError("", string.Format("Your account has been locked out for {0} minutes due to {1} invalid login attempts.", ConfigurationManager.AppSettings["DefaultAccountLockoutTimeSpan"], ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"]));
}
//*******************************************************
// Count number of failed login attempts and display to user
// Before locking them out
//*******************************************************
else if (await UserManager.GetLockoutEnabledAsync(user.Id) && Convert.ToBoolean(checkIfUsernamePasswordValid) != true)
{
await UserManager.AccessFailedAsync(user.Id);
string message;
if (await UserManager.IsLockedOutAsync(user.Id))
{
message = string.Format("Your account has been locked out for {0} minutes due to {1} invalid login attempts.", ConfigurationManager.AppSettings["DefaultAccountLockoutTimeSpan"], ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"]);
}
else
{
int accessFailedCount = await UserManager.GetAccessFailedCountAsync(user.Id);
int attemptsLeft = Convert.ToInt32(ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"]) - accessFailedCount;
message = string.Format("Invalid email/password. You have {0} more attempt(s) before your account is locked out for {1} minutes.", attemptsLeft, ConfigurationManager.AppSettings["DefaultAccountLockoutTimeSpan"]);
}
ModelState.AddModelError("", message);
}
else
{
await SignInAsync(user, model.RememberMe);
// If user get login correct before lock out reset failed count
await UserManager.ResetAccessFailedCountAsync(user.Id);
return RedirectToLocal(returnUrl);
}
}
else
{
ModelState.AddModelError("", string.Format("Sorry we cannot find your email address."));
return View();
}
}
return View(model);
}
On final last step, in the public async Task<ActionResult> Register(RegisterViewModel model)
its automatically logs the user in, if you want to prevent that, comment out the following line.
await SignInAsync(user, isPersistent: false);
I had the following action which hung and never returned:
public Task<ActionResult> ManageProfile(ManageProfileMessageId? message)
{
ViewBag.StatusMessage =
message == ManageProfileMessageId.ChangeProfileSuccess
? "Your profile has been updated."
: message == ManageProfileMessageId.Error
? "An error has occurred."
: "";
ViewBag.ReturnUrl = Url.Action("ManageProfile");
var user = UserManager.FindByIdAsync(User.Identity.GetUserId());
var profileModel = new UserProfileViewModel
{
Email = user.Email,
City = user.City,
Country = user.Country
};
return View(profileModel);
}
but when I converted it to this:
public async Task<ActionResult> ManageProfile(ManageProfileMessageId? message)
{
ViewBag.StatusMessage =
message == ManageProfileMessageId.ChangeProfileSuccess
? "Your profile has been updated."
: message == ManageProfileMessageId.Error
? "An error has occurred."
: "";
ViewBag.ReturnUrl = Url.Action("ManageProfile");
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var profileModel = new UserProfileViewModel
{
Email = user.Email,
City = user.City,
Country = user.Country
};
return View(profileModel);
}
It returned right away. So Im not sure what is going on with this? If its as simple as the method returned without waiting on the result of FindByIdAsync, then why didn't I get a view with nothing in it.
So it appears to me that it neither waited for the return of:
UserManager.FindByIdAsync(User.Identity.GetUserId());
nor returned a null profile nor threw an exception. So I dont get whats happening here when it's hanging in the first example.
I assume your first example was using Result, thus causing a deadlock that I explain on my blog.
In summary, ASP.NET provides a "request context" which only allows one thread in at a time. When you block a thread using Result, that thread is locked into that context. Later, when FindByIdAsync attempts to resume on that context, it cannot because there's another thread already blocked in it.