I'm new to ASP.NET MVC 5 and I'm finding very uncomfortable with Identity authentication + authorization framework. I know this is a new feature of the ASP.NET MVC framework, so I'd like to apply an alternative way to implement authentication in m y application.
Is it possible? I read I could use the FormsAuthenticationModule. Is this a good alternative? How can I use it in a MVC 5 based application?
I felt the same way when taking a look at Identity. It adds lots of unnecessary abstractions and does not suit with my case that I have legacy system which implemented customised authentication work-flow.
Tons of examples out there about OWIN authentication using Identity and EF by default which makes developers confused that OWIN has to go with Identity and Entity Framework.
But technically, you are able to strip out Identity to use only OWIN cookie authentication (Microsoft.Owin.Security.Cookies). The code turns out very simple, below is example I got from my code which eliminates trivial things:
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
var user = _userService.GetByEmail(model.Email);
//check username and password from database, naive checking:
//password should be in SHA
if (user != null && (user.Password == model.Password))
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Email, user.Email),
// can add more claims
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
// Add roles into claims
var roles = _roleService.GetByUserId(user.Id);
if (roles.Any())
{
var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
identity.AddClaims(roleClaims);
}
var context = Request.GetOwinContext();
var authManager = context.Authentication;
authManager.SignIn(new AuthenticationProperties
{ IsPersistent = model.RememberMe }, identity);
return RedirectToAction("Index", "Home");
}
// login failed.
}
public ActionResult LogOut()
{
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignOut("ApplicationCookie");
return RedirectToAction("Login");
}
Without Using Owin Security Methods:
Itz My Controller Coding
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(Employee emp, string returnUrl)
{
using(AdaptiveProjectEntities db = new AdaptiveProjectEntities())
{
string email = emp.Email;
// byte[] en = System.Text.Encoding.UTF8.GetBytes(emp.Password);
//var ee = Convert.ToBase64String(en);
string pass = emp.Password;
bool userValid = db.Employees.Any(user => user.Email == email && user.Password == pass);
if(userValid)
{
FormsAuthentication.SetAuthCookie(email, false);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Projects");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
return View(emp);
}
public ActionResult Logout()
{
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Login");
}
}
}
View:
<div class="container" style="margin-right:50%">
<div class="row">
<div class="col-md-12 col-md-offset-7" style="bottom:-250px">
<div class="panel panel-default" style="margin-right:15%">
<div class="panel-heading" style="padding-bottom:5%">
<center><h3 style="margin-right:80px">Login</h3></center>
#*</div>*#
#using (Html.BeginForm())
{
<div class="modal-body">
#Html.AntiForgeryToken()
<div class="form-horizontal" style="margin-right: 10%;">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-9">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control", type = "email", required = "required" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Password, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-9">
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { #class = "form-control", type = "password", required = "required" } })
#Html.ValidationMessageFor(model => model.Password, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div>
<input class="btn btn-primary pull-left col-lg-offset-1" type="submit" value="Login" style="margin-left:35%" />
</div>
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
Related
I am learning Authentication and Authorization in .net 5. This is what I have so far:
"Login.cshtml":
#model LoginViewModel
#{
ViewData["Title"] = "Login";
}
<h1>Login</h1>
<form method="post">
<div class="form-group">
<div class="input-group align-content-center justify-content-center">
<div class="d-flex flex-column">
#Html.TextBoxFor(user => user.Email, new { #class = "form-control", #placeholder = "email" })
#Html.TextBoxFor(user => user.PasswordHash, new { #class = "form-control", #placeholder = "password", #type = "password" })
<input class="btn btn-primary" type="submit" value="Login" />
</div>
</div>
</div>
</form>
"ActionsController":
[Route("/Login")]
public IActionResult Login()
{
return View();
}
[HttpPost]
[Route("/Login")]
public IActionResult Login([Bind] LoginViewModel model)
{
if (model.Email == "user#fmail.com" && model.PasswordHash == "1234")
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "Bar F Har"),
new Claim(ClaimTypes.Email, "user#fmail.com"),
new Claim("Age", "25")
};
var claimsIdentity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
HttpContext.SignInAsync(claimsPrincipal);
return Redirect("/"); // redirects to the page
}
else
{
return NotFound(); //works!!
}
}
I inspected the debugger. The only problem seems to be that the cookie is not being added:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.Cookie.Name = "Test.Cookie";
options.LoginPath = "/Login";
});
app.Authentication(); and app.Authorization(); have been placed correcty in that order after app.UseRouting();
What am I missing?
The solution to this was pretty simple:
Changing HttpContext.SignInAsync(claimsPrincipal); to HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal); did the trick.
Within a navbar menu, I'm creating a dropdown menu that contains the LoginPartial.
Here's the LoginPartial code:
<li class="nav-item dropdown">
<div class="dropdown-menu" aria-labelledby="LoginDropdownLink">
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = "../../Home/Account" }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.TextBoxFor(m => m.Email, new { #class = "input-text", #placeholder = "Email Address" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
#Html.PasswordFor(m => m.Password, new { #class = "input-text", #placeholder = "Password" })
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
<input type="submit" value="Log in" class="btn btn-default" />
}
</div>
And here's how I'm including it:
<ul class="nav navbar-nav navbar-right">
<li><a class="active" href="../../Home/Index">Home</a></li>
<li>Build A Box</li>
<li>Contact Us</li>
#Html.Action("LoginPartial", "Account")
</ul>
Here's the controller code:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
if (model.OrderId != null)
{
Orders order = db.Orders.Where(x => x.OrderId == model.OrderId).First();
order.UserId = User.Identity.GetUserId();
db.SaveChanges();
return RedirectToAction("Checkout1", "Home", new { model.OrderId });
}
else
{
return RedirectToAction("Index", "Account");
}
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
I put a breakpoint on the var result=... line, and found that the result is "Success." However, rather than loading the Account/Index page, I get redirected to the login page (with the return URL):
http://localhost:51511/Account/Login?ReturnUrl=%2FAccount
I'm including the Account/Index controller code for completeness. I added an [AllowAnonymous] attribute to it temporarily to see what would happen, and it did load (but the model contained no data, of course).
public ActionResult Index()
{
AccountIndexViewModel model = new AccountIndexViewModel();
var Current = User.Identity.GetUserId();
model.Orders = db.Orders.Where(x => x.UserId == Current).OrderByDescending(x=> x.Date).ToList();
List<string> OrderIds = db.Orders.Where(x => x.UserId == Current).Select(x => x.OrderId).ToList();
model.OrderDetails = db.OrderDetail.Where(x=> OrderIds.Contains(x.OrderId)).ToList();
return View(model);
}
I am creating a MVC application and I would like to pass data between views.
Here is my first view:
#model ClassDeclarationsThsesis.Models.AddGroupViewModel
#{
ViewBag.Title = "Add Groups";
}
<h2>Add Groups to subjects</h2>
#foreach (var user in Model.Users)
{
if (user.email.Replace(" ", String.Empty) == HttpContext.Current.User.Identity.Name)
{
if (user.user_type.Replace(" ", String.Empty) == 3.ToString() || user.user_type.Replace(" ", String.Empty) == 2.ToString())
{
using (Html.BeginForm("AddGroup", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create new groups.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#{
List<SelectListItem> listItems1 = new List<SelectListItem>();
}
#foreach (var subject in Model.Subjects)
{
listItems1.Add(new SelectListItem
{
Text = subject.name,
Value = subject.name,
Selected = true
});
}
#Html.LabelFor(m => m.subject_name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.subject_name, listItems1, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.qty, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.qty, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
}
if (user.user_type.Replace(" ", String.Empty) == 1.ToString())
{
<p>You do not have enough permissions to enter this page. Contact the administrator.</p>
}
}
}
And my controller for this:
public ActionResult AddGroup(AddGroupViewModel model)
{
var entities = new ClassDeclarationsDBEntities1();
var model1 = new AddGroupViewModel();
model1.Subjects = entities.Subjects.ToList();
model1.Users = entities.Users.ToList();
// set your other properties too?
if (ModelState.IsValid)
{
return RedirectToAction("AddGroupsQty", "Account");
}
return View(model1);
}
And what I would like to achieve is to pass chosen item from dropdown list and this qty variable to AddGroupsQty View. How do I do this? In my controller of AddGroupsQty i have just a simple return of view so far.
You can pass the values using querystring.
return RedirectToAction("AddGroupsQty", "Account",
new { qty=model.qty,subject=model.subject_name);
Assuming your AddGroupsQty have 2 parameters to accept the quantity and subject
public ActionResult AddGroupsQty(int qty,string subject)
{
// do something with the parameter
// to do : return something
}
This will make browser to issue a new GET request with the values in query string. If you do not prefer to do that, you can use a server side temporary persistence mecahnism like TempData
TempData["qty"]=model.qty;
TempData["subject"]= model.subject_name;
return RedirectToAction("AddGroupsQty", "Account");
And in your AddGroupsQty action,
public ActionResult AddGroupsQty()
{
int qty=0;
string subjectName=string.Empty;
if(TempData["qty"]!=null)
{
qty = Convert.ToInt32(TempData["qty"]);
}
if(TempData["subject"]!=null)
{
subjectName = TempData["subject"];
}
// Use this as needed
return View();
}
If you want to pass these values from the ADdGroupsQty action to it's view, you can use either a view model or ViewBag/ViewData.
I am developing a basic web site in ASP.NET MVC 5 (using visual studio 2013). The site will use Facebook for user authentication and for retrieving initial profile data.like stack over flow login with facebook..access user name,profile photo etc..plz any one help me relevant sample
startup.auth.cs
FacebookAuthenticationOptions fbao = new FacebookAuthenticationOptions();
fbao.AppId = "****";
fbao.AppSecret = "*****";
fbao.Scope.Add("email");
fbao.Scope.Add("user_birthday");
fbao.Scope.Add("user_hometown");
fbao.SignInAsAuthenticationType = Microsoft.Owin.Security.AppBuilderSecurityExtensions.GetDefaultSignInAsAuthenticationType(app);
app.UseFacebookAuthentication(fbao);
Account controller>>Externallogincallback
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
ClaimsIdentity fboa = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
//var email = ext.Claims.First(x => x.Type.Contains("emailaddress")).Value;
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
Account view model
public class ExternalLoginConfirmationViewModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
public string UserName { get; set; }
}
externalloginconfirmationview
<h4>Association Form</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<p class="text-info">
You've successfully authenticated with <strong>#ViewBag.LoginProvider</strong>.
Please enter a user name for this site below and click the Register button to finish
logging in.
</p>
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
Following are some nice tutorials that may help solve your issue
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
http://www.asp.net/mvc/overview/getting-started/aspnet-mvc-facebook-birthday-app
im reseting my password in mvc using email,the code looks fine i can send token to email.but when i click on link in email it doesn,t redirect me to the reset password page
here is my code in emailreset action
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
//SendSmsBusiness objap = new SendSmsBusiness();
RegisterBusiness reg = new RegisterBusiness();
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser();
user = reg.UserManager.FindByEmail(model.Email);
// 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
if (user != null)
{
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol:Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking here");
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
{
ModelState.AddModelError("", "The user does not exist");
return View();
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
i don,t know if my request.url.scheme is null because i do have a resetpassword page. here is its view.
#model Template.Model.ResetPasswordViewModel
#{
ViewBag.Title = "Reset password";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="panel panel-default">
<div class="panel-heading " style="background-color: green;"></div>
<div class="panel-body">
<div class="form-group">
#Html.LabelFor(m => m.Code, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Code, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Password, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
#section Scripts {
#System.Web.Optimization.Scripts.Render("~/bundles/jqueryval")
}
any help appreciated.
the resetpassword controller
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
RegisterBusiness reg = new RegisterBusiness();
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser();
user = reg.UserManager.FindByEmail(model.Email);
if (user == null)
{
ModelState.AddModelError("", "No user found.");
return View();
}
IdentityResult result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
else
{
AddErrors(result);
return View();
}
}
// If we got this far, something failed, redisplay form
return View(model);
}