I'm creating a web app with .net using a login for users. After registration the users' data is stored in my local SQL database and the user must be able to log in using these.
Currently I've managed to register users and they can log in when I use a hard coded password and user name but I can't figure out how I can check the user's given credentials and the ones in the database to check if these match.
I did extensive research but didn't find a proper solution but surprisingly could not come up with a working solution.
Here is my current code:
My users model:
namespace Models
{
public class Users
{
public int Id { get; set; }
public string userName { get; set; }
public string userPassword { get; set; }
public string userEmail { get; set; }
public string userCompany { get; set; }
public class UsersDBContext : DbContext
{
public DbSet<Users> Users { get; set; }
}
}
}
My controller
namespace Eindwerk.Controllers
{
public class UsersController : Controller
{
private Users.UsersDBContext db = new Users.UsersDBContext();
// GET: /Users/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Users users)
{
if (ModelState.IsValid)
{
if(users.userName == "NAAM" && users.userPassword == "WACHTWOORD")
{
FormsAuthentication.SetAuthCookie(users.userName, false);
return RedirectToAction("", "Home");
}
{
ModelState.AddModelError("", "Invalid username and/or password");
}
}
return View();
}
My View
#model Eindwerk.Models.Users
#{
ViewBag.Title = "Login";
Layout = "../_Login.cshtml";
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<div class="panel-body">
<fieldset>
<div class="editor-field">
#Html.TextBoxFor(model => model.userName, new { #class = "form-control", Value="Username"})
#Html.ValidationMessageFor(model => model.userName)
</div>
<br />
<div class="editor-field">
#Html.TextBoxFor(model => model.userPassword, new { #class = "form-control", Value= "Password" })
#Html.ValidationMessageFor(model => model.userPassword)
</div>
<br />
<br />
<p>
<input type="submit" value="SIGN IN" class="btn btn-outline btn-primary btn-lg btn-block"/>
</p>
</fieldset>
</div>
}
So instead of using if(users.userName == "NAAM" && users.userPassword == "WACHTWOORD")
I want to check properly if the user is valid and registered in my database or not so I can grant or deny access.
Anybody a proper solution? Or link to decent documentation in order to resolve this issue?
Any help is really appreciated!
You need to reference the db.Users collection.
[HttpPost]
public ActionResult Index(Users users)
{
if (ModelState.IsValid)
{
var hash = GenerateHash(users.userPassword,Settings.Default.salt);
var authUser = db.Users.FirstOrDefault(row => row.userName == users.userName && row.userPassword == hash )
if ( authUser != null )
{
Session["role"] = authUser.Role;
FormsAuthentication.SetAuthCookie(users.userName, false);
return RedirectToAction("", "Home");
}
else
{
ModelState.AddModelError("", "Invalid username and/or password");
}
}
return View();
}
private static string GenerateHash(string value, string salt)
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(salt + value);
data = System.Security.Cryptography.MD5.Create().ComputeHash(data);
return Convert.ToBase64String(data);
}
See MD5 hash with salt for keeping password in DB in C# for a more indepth discussion of salting and hashing. Note that I'm assuming you have a Settings class with the salt value. You can replace this with another mechanism for retrieving the salt.
Related
I have a problem with my login action. The submit button in the login form is not working. The signup action successfully stores data in the database but I am having a problem with the login action. I am fetching data in the login action but I think it does not work. I don't know a reason.
----Models-----
public class User
{
public int UserID { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Confirmpassword { get; set; }
public string mobile { get; set; }
public string Gender { get; set; }
}
--dbcontext----
namespace MVC_Boostrap.Models
{
public class Projectdatacontext : DbContext
{
public DbSet<User> Users { set; get; }
}
}
------controller------
public ActionResult Login( User user)
{
User u = p1.Users.FirstOrDefault(Users => Users.Email == user.Email && Users.Password == Users.Password);
if (u != null)
{
ViewBag.Message = "Successfully Login";
return View();
}
else
{
ViewBag.Message = "Enter Correct Email and Password";
return View();
}
//return RedirectToAction("Welcome", u);
}
---SignUp action-------------
[HttpPost]
public ActionResult Signup( User user)
{
try
{
p1.Users.Add(user);
p1.SaveChanges();
ViewBag.Message = "Signup Succesfully";
//return RedirectToAction("Welcome", user);
}
catch(Exception ex)
{
ViewBag.Message = "Signup error:" + ex.ToString();
}
return View();
}
-------------login view-------------------------
#model MVC_Boostrap.Models.User
#{
ViewBag.Title = "Login";
}
<body>
<div class="container" style="margin-top:20px;">
<div class="row">
<div class="col-md-4">
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h4 class="card-title">Login Form</h4>
#if (ViewBag.Message != null)
{
<div class="alert alert-primary">#ViewBag.Message</div>
}
<div class="form-group">
#Html.Label("Email ID:")
#Html.TextBoxFor(Model => Model.Email, new { #Placeholder = "Please Enter Email-ID", #class = "form-control", #type = "email" })
</div>
<div class="form-group">
#Html.Label("Password :")
#Html.TextBoxFor(Model => Model.Password, new { #Placeholder = "Please enter your Passoword", #class = "form-control", #type = "password" })
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Login</button> //this button does not perform anythng
</div>
</div>
<div class="card-footer">
New Users Signup Here
</div>
</div>
</div>
<div class="col-md-4">
</div>
</div>
</div>
</body>
No error message in the browser console and visual studio.
Your problem on the login view is that your input elements are not inside any <form> element so therefore when you click on the submit button the browser really does not know where to send that data so it just returns you to the same screen by hitting the HttpGet action of Login. To fix this problem simply do
#using(Html.BeginForm("Login","ControllerName")){ //code inside here }
Now moving onto your controller where you also have a problem which is your Login action isn't decorated with HttpPost attribute so when the submit button is clicked the default HttpGet Login action will be hit. Just a small change similar to that of your Signup action you have to do
[HttpPost]
public ActionResult Login(User user) { //code inside here }
This answer explains what happens when you decorate your action with HttpPost attribute.
I have a simple mvc5 code first application, It has a ms SQL database in the back-end and and a form in the front-end.
While I insert into database via the front end form, it does not generate any error, everything seems OK but when i check the back end database table, then all values in the newly inserted row are showing as NULL.
This is my code for model:
public class students
{
public int Id { get; set; }
[Display(Name = "Name")]
public string st_name { get; set; }
[Display(Name = "Father's Name")]
public string st_father_name { get; set; }
public string st_contact { get; set; }
}
This is the View Model class:
public class AddStudentViewModel
{
public students stdntss { get; set; }
}
This is the controller:
public ActionResult Index()
{
var std = _context.stdnts;
if (std==null)
{
return Content("Nothing Found");
}
return View(std);
}
public ActionResult AddStudent()
{
return View();
}
[HttpPost]
public ActionResult Insert(students st)
{
_context.stdnts.Add(st);
_context.SaveChanges();
return RedirectToAction("Index","Students");
}
And finally this is the view:
#model school2.ViewModels.AddStudentViewModel
#{
ViewBag.Title = "AddStudent";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>New student's registration form</h2>
#using (Html.BeginForm("Insert","Students"))
{
<div class="form-group">
#Html.LabelFor(m=> m.stdntss.st_name)
#Html.TextBoxFor(m=> m.stdntss.st_name, new { #class="form-control"})
</div>
<div class="form-group">
#Html.LabelFor(m => m.stdntss.st_father_name)
#Html.TextBoxFor(m => m.stdntss.st_father_name, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.stdntss.st_contact)
#Html.TextBoxFor(m => m.stdntss.st_contact, new { #class = "form-control" })
</div>
<button type="submit" class="btn btn-primary">Save</button>
}
Kindly assist me if anyone has any clue?
One way to solve this is to change the POST method to accept the same model as the view.
try changing
public ActionResult Insert(students st)
{
_context.stdnts.Add(st);
_context.SaveChanges();
return RedirectToAction("Index","Students");
}
to
public ActionResult Insert(AddStudentViewModel st)
{
_context.stdnts.Add(st.stdntss );
_context.SaveChanges();
return RedirectToAction("Index","Students");
}
or changing the model of the form to simply be student.
I think that change #Html.TextBoxFor(model=> model.stdntss.st_name, new { #class="form-control"}). because call Model, #model school2.ViewModels.AddStudentViewModel . Variable default Model.
I've checked a lot of other threads before and I couldn't really find anything specific to my issue, which is the following:
I'm working with ApplicationUsers on my project. Next to that, each user has a profile (which is a different table). I want to fill in the profile while I am "creating" a new ApplicationUser in my backoffice, considering they are connected to one another by the userId. The issue is that this is not quite working. I'm using ViewModels and I've made a specific Viewmodel for ApplicationUsers as well (UserViewModel).
So far, I can make a new ApplicationUser perfectly fine, but once i start to try and create a Profile using the same form as the one I've used for my ApplicationUser things starts to go wrong. I have a few hunches where I'm going wrong (for example only using 1 model in my view (create/edit.cshtml, though i'm pretty sure you can only use 1 model on create in a view?).
Below is my viewmodel for user (UserViewModel.cs)
As you can see here my UserViewModel has a virtual property Profile which should enable the possibility of creating profiles together with the users? Or maybe I'm just dead wrong here already.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using App.Models;
using App.Models.ViewModels;
namespace App.Models.Identity.ViewModels
{
public class UserViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public virtual Profile Profile { get; set; }
}
}
The following are my edit functions (UserController.cs)
Keep in mind that everything already pretty much works when I remove any reference to the profile. It's when I start to try and add profile fields (be it here or in the view below) that the problems start to arise.
[HttpGet]
public IActionResult Edit(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(400);
}
var model = _kletsContext.Users.FirstOrDefault(m => m.Id == id);
var profile = _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.Id);
if(model == null)
{
return RedirectToAction("Index");
}
var viewModel = new UserViewModel
{
UserId = model.Id,
UserName = model.UserName,
Email = model.Email,
};
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(UserViewModel model, Profile profile)
{
UserViewModel viewModel = null;
try
{
if(!ModelState.IsValid)
throw new Exception("The User model is not valid!");
var originalModel = _kletsContext.Users.FirstOrDefault(m => m.Id == model.UserId);
var originalProfile = _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.UserId);
if(originalModel == null)
throw new Exception("The existing User: " + model.UserName + " doesn't exists anymore!");
originalModel.UserName = model.UserName;
originalModel.Email = model.Email;
originalProfile.Age = profile.Age;
_kletsContext.Users.Attach(originalModel);
_kletsContext.Profiles.Attach(profile);
_kletsContext.Entry(originalModel).State = EntityState.Modified;
_kletsContext.Entry(profile).State = EntityState.Modified;
if (_kletsContext.SaveChanges() == 0)
{
throw new Exception("The User model could not be saved!");
}
return RedirectToAction("Index");
}
catch(Exception ex)
{
ModelState.AddModelError(string.Empty, "Unable to save changes.");
viewModel = new UserViewModel
{
UserId = model.UserId,
UserName = model.UserName,
Email = model.Email,
};
}
return View(viewModel);
}
Below is my Edit.cshtml:
#model App.Models.Identity.ViewModels.UserViewModel
#{
ViewBag.Title = "User";
ViewBag.SubTitle = "Nieuwe";
ViewBag.Description = "Aanmaak van een nieuwe User";
Layout = "~/Areas/Backoffice/Views/Shared/_Layout.cshtml";
}
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
Aanmaak van een nieuwe User
</div>
<div class="panel-body">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.UserId)
<fieldset>
<legend class="hidden">Aanmaak van een nieuwe User</legend>
#Html.ValidationSummary("", new {#class="alert-danger"})
<div class="form-group">
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName, new { #class= "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Email)
#Html.TextBoxFor(m => m.Email, new { #class= "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Profile.Age)
#Html.TextBoxFor(m => m.Profile.Age, new { #class= "form-control" })
</div>
#Html.ActionLink("Terug naar het overzicht", "Index", new { }, new { #class= "btn btn-default" })
<input type="submit" value="Save" class="btn btn-primary" />
</fieldset>
}
</div>
</div>
</div>
</div>
Extra: I'll add my ProfileViewModel if necessary, or my DBContext file (or any model). Just let me know. I've been looking at this for a while, but I'm pretty sure that I'm just misunderstanding something pretty basic?
PS: I end up getting the exceptionError so I know there has to be a general problem where nothing inside of my Try works. See the image below
https://gyazo.com/399e59d7d3cfdab2141726fc49ad6786
You should try to use a lean flat viewmodel
public class UserProfileViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
and in your GET action
public ActionResult Edit(string id)
{
var model = _kletsContext.Users.FirstOrDefault(m => m.Id == id);
var profile = _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.Id);
if(model == null)
{
return RedirectToAction("Index");
}
var vm = new UserProfileViewModel
{
UserId = model.Id,
UserName = model.UserName,
Email = model.Email,
Age = profile.Age
};
return View(vm);
}
Your razor view will be strongly typed to this view model
#model YourNamespaceHere.UserProfileViewModel
#using(Html.BeginForm())
{
<label>UserName</label>
#Html.TextBoxFor(s=>s.UserName)
<label>Email</label>
#Html.TextBoxFor(s=>s.Email)
<label>Age</label>
#Html.TextBoxFor(s=>s.Age)
#Html.HiddenFor(s=>s.UserId)
<input type="submit" />
}
And in your HttpPost action, We will use this view model as the method parameter and the posted form will be converted to an object of this class by the default model binder.
[HttpPost]
public ActionResult Edit(UserProfileViewModel model)
{
if(ModelState.IsValid)
{
var u = _kletsContext.Users.FirstOrDefault(m => m.Id == model.UserId);
var p= _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.UserId);
//Set the new values
u.Email = model.Email;
if(p!=null)
{
p.Age=model.Age;
_kletsContext.Entry(p).State = EntityState.Modified;
}
else
{
// to do :Create a new profile record
}
_kletsContext.Entry(u).State = EntityState.Modified;
_kletsContext.SaveChanges();
// to redirect to some success page
}
return View(model);
}
i have created a login page i want it to display validation messages if user doesnt enter username and password on click of button,i have written code as below
Homecontroller.cs
public ActionResult Login()
{
// ViewBag.Message = "Your contact page.";
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(logintable p)
{
abEntities db = new abEntities();
List<sp_logindata_Result> data = db.sp_logindata(p.Username, p.Password).ToList();
Session["LogedUserID"] = data.First().UserID.ToString();
Session["UserNme"] = data.First().FullName.ToString();
int typeVal = data.First().Usertype;
if (typeVal == 1)
{
return RedirectToAction("page1");
}
else if (typeVal == 2)
{
return RedirectToAction("page2");
}
else
{
return RedirectToAction("Error");
}
return View(data);
}
logintable.cs
public partial class logintable
{
public logintable()
{
this.PerformanceDetails = new HashSet<PerformanceDetail>();
this.EvaluationDetails = new HashSet<EvaluationDetail>();
}
public string FullName { get; set; }
[Required(ErrorMessage = "Employee name is required")]
public string Username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
public virtual ICollection<PerformanceDetail> PerformanceDetails { get; set; }
public virtual ICollection<EvaluationDetail> EvaluationDetails { get; set; }
}
login.cshtml
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div>
<fieldset>
<legend>Login</legend>
<div class="editor-field">
#Html.TextBoxFor(u => u.Username)
#Html.ValidationMessageFor(u => u.Username)
</div>
<div class="editor-label">
#Html.LabelFor(u => u.Password)
</div>
<div class="editor-field">
#Html.PasswordFor(u => u.Password)
#Html.ValidationMessageFor(u => u.Password)
</div>
<input type="submit" value="Log In" />
</fieldset>
</div>
}
what i am missing here so it is not displaying messages.
That won't happen automatically
You need to follow at minimum certain steps:
Add validation Rules to your Model [You already done]
Add the scripts that will take care of client side validation (unobstrusive validation) [Required].
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Add the bundle in BundleConfig.cs file, under App_Start folder
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
~/Scripts/jquery.validate*"));
I wan't to validate a login form. I just wan't to check if the email field and password field are filled in. If not, an error message should display. I can't get it to work.
My actionresult Login:
[HttpPost]
public ActionResult Login(string email, string password, string returnUrl)
{
if (ModelState.IsValid)
{
try
{
User user = accountDal.GetAllUsers().FirstOrDefault(x => x.Email == email && x.Password == password);
if (user != null)
{
FormsAuthentication.SetAuthCookie(user.Email, false);
Response.Redirect(returnUrl, false);
}
return View();
}
catch
{
return View();
}
}
return View();
}
So as you can see, I check if the ModelState is valid.
Here is my login view:
#model ProjectInvoice.ViewModels.LoginViewModel
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<h3>Login</h3>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<p>
<input type="submit" value="Create" />
</p>
}
And my ViewModel:
public class LoginViewModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "Password")]
public string Password { get; set; }
}
If I use a breakpoint at the if (ModelState.IsValid) it says it is true, while this is not the case. Am I missing something, or am I doing something wrong?
You need to post back your model so the properties of your model can be validated
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
....
Note also, when you return the view, you need to return the model
return View(model);
You need to have something like following:
public ActionResult Login(LoginViewModel loginViewModel, string returnUrl)
{
if (ModelState.IsValid)
{
try
{
User user = accountDal.GetAllUsers().FirstOrDefault(x => x.Email == loginViewModel.Email && x.Password == loginViewModel.Password);
if (user != null)
{
FormsAuthentication.SetAuthCookie(user.Email, false);
Response.Redirect(returnUrl, false);
}
return View();
}
catch
{
return View();
}
}
return View(loginViewModel);
}
The reason why it is not working is ModelState.IsValid validates your model against the data annotation applied to the LoginViewModel class's members.
You need to use data annotations. Like this:
public class LoginViewModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "Password")]
public string Password { get; set; }
}
This will invalidate you're model state. And then, in the contriller, you return it to the View using something liek this:
return View(model)