How can I restrict access until user has confirmed email link - asp.net-mvc

I am finally able to send Email confirmation on my MVC 5 Application
The user now receives an email and the EmailConfirmed field is updated from False to True. However, the user is still able to login without confirming the email.
My question is how can I restrict access until user has confirmed email link
Below is my ConfirmEmail Method.
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string Token, string Email)
{
ApplicationUser user = this.UserManager.FindById(Token);
if (user != null)
{
if (user.Email == Email)
{
user.EmailConfirmed = true;
await UserManager.UpdateAsync(user);
//await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home", new { ConfirmedEmail = user.Email });
}
else
{
return RedirectToAction("Confirm", "Account", new { Email = user.Email });
}
}
else
{
return RedirectToAction("Confirm", "Account", new { Email = "" });
}
}
[AllowAnonymous]
public ActionResult Confirm(string Email)
{
ViewBag.Email = Email; return View();
}
Thank you everyone for reading.
Ceci
----- UPDATE ------
I added the code below to the /Account/Login Controller
var user = await UserManager.FindByNameAsync(model.UserName);
if(user != null){
if (!await UserManager.IsEmailConfirmedAsync(user.UserName)) {
return View("ErrorNotConfirmed");
}
}
But its returning an error. UserId not Found.

I am posting this code in case someone needs it.
Basically I replaced the code above with this code:
var userid = UserManager.FindByEmail(model.UserName).Id;
if (!UserManager.IsEmailConfirmed(userid))
{
return View("EmailNotConfirmed");
}
It works beautifully now.

Related

UserManager.FindByNameAsync slow in ASP.NET MVC

I have a site based on ASP.NET MVC framework. Although i am using async method to login the user, i have found that it takes forever to get user logged in into the site. I have used Visual Studio's diagnostic tools and found that this line takes most of the time during the code execution.
var user = await UserManager.FindByNameAsync(model.Email);
Full Code:
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user != null)
{
var getPasswordResult = UserManager.CheckPassword(user, model.Password);
if (getPasswordResult)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
var identity = await UserManager.CreateIdentityAsync(
user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = model.RememberMe }, identity);
if (model.RememberMe == true)
{
HttpCookie cookie = new HttpCookie("UsersLogin");
cookie.Values.Add("UserName", model.Email);
cookie.Expires = DateTime.Now.AddDays(15);
Response.Cookies.Add(cookie);
}
return RedirectToAction("NavigateAuthUser", "Home", new { ReturnUrl = returnUrl });
}
else
{
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
return new EmptyResult();
}
Any suggestion to improve the performance?
Thanks
Sanjeev
For workaround I use:
_db.Users.First(x => x.UserName == User.Identity.Name);
But I'm sure this is not the best way
Develop my own method FindByEmail, this way I greatly improve the perfomance
public AspNetUsers FindByEmail(string Email)
{
try
{
var _modelo = new SIGMAEntities();
return _modelo.AspNetUsers.FirstOrDefault(x => x.Email.Trim().ToLower() == Email);
}
catch (Exception)
{
throw;
}
}

Simple custom register/login system how to hash and retrieve password

I have created a simple register/login system and right now when the user registers it just stores their plain password in the database. I am wondering how I can hash it then when they login, how do I unhash it and compare it to what they put in.
All I have in my users table/model class is username and password
UsersController
// GET: Register User
public ActionResult Register(User user)
{
if (ModelState.IsValid)
{
using (UserContext db = new UserContext())
{
db.Users.Add(user);
db.SaveChanges();
}
ModelState.Clear();
}
return View();
}
// POST: Login User
[HttpPost]
public ActionResult Login(User user)
{
using (UserContext db = new UserContext())
{
var usr = db.Users.SingleOrDefault(u => u.Username == user.Username && u.Password == user.Password);
if (usr != null)
{
Session["UserID"] = usr.Username.ToString();
return RedirectToAction("Index", "Profile");
}
else
{
ModelState.AddModelError("", "Username or Password Incorrect");
}
}
return View();
}

Explicit password and email validation in Microsoft.AspNet.Identity, why needed?

I am big fan of Adam Freeman's books. At his Pro Asp.net mvc 5 platform, in chapter 13, page 325, the following code confused me. Does anyone have the explanation why he used the email and password validation explicitly?
The call this.UserManager.UpdateAsync(user) should return a result with same errors generated by this.UserManager.UserValidator.ValidateAsync(user) and this.UserManager.PasswordValidator.ValidateAsync(password). Is he not doing the same thing twice? Or there is a special purpose?
[HttpPost]
public async Task<ActionResult> Edit(string id, string email, string password)
{
AppUser user = await this.UserManager.FindByIdAsync(id);
if (user != null)
{
user.Email = email;
IdentityResult validEmail = await this.UserManager.UserValidator.ValidateAsync(user);
if (!validEmail.Succeeded)
{
this.AddErrorsFromResult(validEmail);
}
IdentityResult validPass = null;
if (password != string.Empty)
{
validPass = await this.UserManager.PasswordValidator.ValidateAsync(password);
if (validPass.Succeeded)
{
user.PasswordHash = this.UserManager.PasswordHasher.HashPassword(password);
}
else
{
this.AddErrorsFromResult(validPass);
}
}
if ((validEmail.Succeeded && validPass == null)
|| (validEmail.Succeeded && password != string.Empty && validPass.Succeeded))
{
IdentityResult result = await this.UserManager.UpdateAsync(user);
if (result.Succeeded)
{
return this.RedirectToAction("Index");
}
this.AddErrorsFromResult(result);
}
}
else
{
ModelState.AddModelError(string.Empty, "User not found");
}
return this.View(user);
}
private AppUserManager UserManager
{
get
{
return HttpContext.GetOwinContext().GetUserManager<AppUserManager>();
}
}
private void AddErrorsFromResult(IdentityResult result)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError(string.Empty, error);
}
}
in source code of identity UserManager class UpdateAsync method is like this:
public virtual async Task<IdentityResult> UpdateAsync(TUser user)
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
var result = await UserValidator.ValidateAsync(user).ConfigureAwait(false);
if (!result.Succeeded)
{
return result;
}
await Store.UpdateAsync(user).ConfigureAwait(false);
return IdentityResult.Success;
}
that calls UserValidator.ValidateAsync(user) method for validating that username is not illegal or user not registered before with a different Owner Id and does not care for validating Email address or password string. if you want to validate passwords and do your custom checks you must create custom validators .
you can find Default UserValidator source code here

Authorize Attribute with Roles

I want to implement my custom authorization, I wonder what is wrong with my code even I got the user credentials correctly it still redirects me to my Login Method, please see the code below
Edit: I have successfully implemented the Authorize Attribute with Roles, for future readers please see code below
Login Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login (AdminViewModels.Login viewModel, string returnURL)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
PasswordHasher passwordVerify = new PasswordHasher();
var query = (from acc in db.accounts.Where(x => x.username == viewModel.Username)
select new { acc.username, acc.password}).FirstOrDefault();
if (query != null)
{
if (ModelState.IsValid)
{
var result = passwordVerify.VerifyHashedPassword(query.password, viewModel.Password);
switch (result)
{
case PasswordVerificationResult.Success:
//set forms ticket to be use in global.asax
SetupFormsAuthTicket(viewModel.Username, viewModel.rememeberMe);
return RedirectToLocal(returnURL);
case PasswordVerificationResult.Failed:
ModelState.AddModelError("", "Wrong Username or Password");
return View(viewModel);
}
}
}
return View(viewModel);
}
Forms Auth Ticket
private account SetupFormsAuthTicket(string userName, bool persistanceFlag)
{
account user = new account();
var userId = user.id;
var userData = userId.ToString(CultureInfo.InvariantCulture);
var authTicket = new FormsAuthenticationTicket(1, //version
userName, // user name
DateTime.Now, //creation
DateTime.Now.AddMinutes(20), //Expiration
persistanceFlag, //Persistent
userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
return user;
}
Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//take out user name from cookies
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string[] roles = null;
trainingEntities db = new trainingEntities();
//query database to get user roles
var query = (from acc in db.account_roles where acc.account.username == username select acc.role.role_name).ToArray();
roles = query;
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles);
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Now you can use [Authorize(Roles = "Admin")]
to any action method or on top of controller
I have successfully implemented the Authorize Attribute with Roles, for future readers please see code below.
Login Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login (AdminViewModels.Login viewModel, string returnURL)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
PasswordHasher passwordVerify = new PasswordHasher();
var query = (from acc in db.accounts.Where(x => x.username == viewModel.Username)
select new { acc.username, acc.password}).FirstOrDefault();
if (query != null)
{
if (ModelState.IsValid)
{
var result = passwordVerify.VerifyHashedPassword(query.password, viewModel.Password);
switch (result)
{
case PasswordVerificationResult.Success:
//set forms ticket to be use in global.asax
SetupFormsAuthTicket(viewModel.Username, viewModel.rememeberMe);
return RedirectToLocal(returnURL);
case PasswordVerificationResult.Failed:
ModelState.AddModelError("", "Wrong Username or Password");
return View(viewModel);
}
}
}
return View(viewModel);
}
FormsAuthTicket
private account SetupFormsAuthTicket(string userName, bool persistanceFlag)
{
account user = new account();
var userId = user.id;
var userData = userId.ToString(CultureInfo.InvariantCulture);
var authTicket = new FormsAuthenticationTicket(1, //version
userName, // user name
DateTime.Now, //creation
DateTime.Now.AddMinutes(20), //Expiration
persistanceFlag, //Persistent
userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
return user;
}
Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//take out user name from cookies
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string[] roles = null;
trainingEntities db = new trainingEntities();
//query database to get user roles
var query = (from acc in db.account_roles where acc.account.username == username select acc.role.role_name).ToArray();
roles = query;
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles);
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Now you can use [Authorize(Roles = "Admin")]
to any action method or on top of controller
as I see in ControllerLogin attribute it is now being applied in a variable, when it should be applied to a method or a class
[CustomAuthorization(UserRole="Admin")]
// GET: Manage
private trainingEntities db = new trainingEntities();
public ActionResult Index()
{
return View();
}
Private trainingEntities dB = new TrainingEntities();
[CustomAuthorization(UserRole="Admin")]
Public ActionResult Index()
{
//yourcode
}

Return error to angular ajax request from MVC web API

Ive used couple of days trying to figugure out how to return an error to angular ajax request to web api.
in my js AccountController i have a login method:
$scope.Login = function () {
AccountService.Login($scope.UserData.LoginName, $scope.UserData.Password).success(function (account) {
$scope.UserData = account;
}).error(function () {
console.log("failed");
});
};
and in web api i have folowing:
public Account Login(string loginName, string password)
{
var emptyAccount = new Account();
password = Encrypt(password);
var account = db.Accounts.FirstOrDefault(c=>c.Password == password && c.LoginName == loginName);
if (account == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
acount.Password = "";
return account;
}
The problem is that i throw a new HttpResponseException which fire off and dont return anything back to ajax. How do i fix this?
Normally, in this case it is the error handler that will get triggered.
.error(function () {
alert("login failed");
}
This is where you could handle the error.
Also you probably want to return 401 Unauthorized in this case instead of 404. Also in general it is considered bad practice to throw exceptions in cases where you can handle it gracefully:
public HttpResponseMessage Login(string loginName, string password)
{
var emptyAccount = new Account();
password = Encrypt(password);
var account = db.Accounts.FirstOrDefault(c => c.Password == password && c.LoginName == loginName);
if (account == null)
{
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "unauthorized");
}
acount.Password = "";
return Request.CreateResponse(HttpStatusCode.OK, account);
}
When you sending the data, and it's hit the servers, it will return header 200 OK because it's already hit your controller, even when your controller code is throw error (in later process).
so, if you want to know what error that thrown, I will create AccountResponse Dto into that, and introduce new Error field in that like so:
public class AccountResponse()
{
public Account account { get; set;}
public string ErrorMessage { get; set; }
}
and then on the controller:
public AccountResponse Login(string loginName, string password)
{
var emptyAccount = new Account();
password = Encrypt(password);
var account = db.Accounts.FirstOrDefault(c=>c.Password == password && c.LoginName == loginName);
if (account == null)
{
return new AccountResponse(){ ErrorMessage = Response.ResponseCodes.something;
}
AccountResponse.account = account;
AccountResponse.account.Password = "";
return AccountResponse;
}

Resources