Custom user Login in ASP.NET MVC 4 - asp.net-mvc

I'm developing an ASP.NET MVC 4 Application, wherein I need every user to be redirected to his custom page upon login. The users are obtained from the UserProfile class which I have refactored into a separate class file. How do I modify the Redirect To method in the Login (post) Action in a ASP.NET MVC 4 Internet Project to get this functionality? Further how do I pass this data to a User controller that can display information related to this specific user.
I'm using simple Membership as it comes out of the box in an internet application template in ASP.NET MVC 4.

I'm guessing you're talking about this piece of code in the MVC4 template? I'm doing something very similar - upon login, I redirect the user to a page called Index.cshtml listed under the Account controller :
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, model.RememberMe))
{
return RedirectToAction("Index", "Account");
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError(string.Empty, LocalizedText.Account_Invalid_User_Or_Password);
return View(model);
}
For user specific data, why not just extend the UsersContext.cs class in the Classes folder, then use WebSecurity.CurrentUserId to retrieve the information that pertains to that user?
Extended UsersContext class :
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
public bool IsPromotional { get; set; }
public bool IsAllowShare { get; set; }
}
This is the Index() action on the Account controller that they get redirected to upon login. Here I just call the users context, new up an AccountModel that's bound to the Index.cshtml page, set those attributes in the model, then return the View with the model we've built :
public ActionResult Index()
{
//New up the account model
var account = new AccountModel();
try
{
//Get the users context
var CurrentUserId = WebSecurity.CurrentUserId;
var context = new UsersContext();
var thisUser = context.UserProfiles.First(p => p.UserId == CurrentUserId);
//Set the name
account.Name = thisUser.Name;
//Set the user specific settings
account.IsAllowShare = thisUser.IsAllowShare;
account.IsPromotional = thisUser.IsPromotional;
}
catch (Exception exception)
{
_logger.Error(exception, "Error building Account Model");
}
return View(account);
}
It may not be exactly what you're looking for, but that should get you moving in the right direction.

Related

MVC implement security to prevent accessing other users information

I am familiar with roles and authentication attributes in MVC, but as I am adding more and more information onto my database I think I ma going to run into a problem with primary keys being unencrypted or accessible.
I am using identity 2.1, so when a user is logged in I have access of their UserId and their CustomerID but my concern is that any user can go to /Customers/Delete/3 or any CustomerID and have access. Even if I created a GUID id or other encryption it could still be vulnerable to brute force attacks.
Is there a way in MVC to implement a check to only allow the current user to load pages that are related to them?
You can add extra field say "CreatedByUserId" to database table and when user access page check if CreatedByUserId matches with user id of logged in user or not.
You should be checking if the current logged in user has access to any of the information before you try and manipulate data. For example...
public async Task<HttpResponseMessage> DeleteCustomer(string customerId)
{
var appUser = await _authRepository.FindUser(User.Identity.GetUserName());
if(!_customerRepository.CanDeleteCustomer(appUser.Id, customerId){
return BadRequest();
}
// they have access so do what you need to do down here..
}
You can create a custom Authorize Attribute and a table in the database in which you store which user is allowed what Pages (Actions) or Controllers and then check that table while authorizing that whether the user is authorized for that Page/Controller. I have created an example for you in which I used Custom Authorize Attribute named MyAuthorizeAttribute and a database table named PageRoles.
Custom Authorize Attribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
readonly ApplicationDbContext _db = new ApplicationDbContext();
string _pageName;
public MyAuthorizeAttribute(string pageNameFromController)
{
_pageName = pageNameFromController;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var userId = httpContext.User.Identity.GetUserId();
var pageRoles = db.PageRoles.Where(m => m.UserId == userId);
foreach (var item in pageRoles)
{
if (item.PageName == _pageName && item.UserId == userId)
{
return base.AuthorizeCore(httpContext);
}
}
return false;
}
}
Model used:
public class PageRole
{
public int Id { get; set; }
public string UserId { get; set; }
public string PageName { get; set; }
public virtual ApplicationUser User { get; set; }
}
and then you will just have to use the attribute on your controllers just like you use Authorize attribute:
[MyAuthorize("Home")]
public class HomeController : Controller
{ }

Pass variables to Partial views without using Session variable in mvc

Basically i am handling two types of login. One is UserLogin, another One is Client login. I want to Separate these two. So i am passing session variable while login like the following,
// this is while login as a client
Session["LoginAs"] = "client";
//this is while login as a user
Session["LoginAs"] = "user";
But here one issue is there. Session is timed out within 2 minutes. If i increased in config file also same result only. So i don't want to pass these two into session. If any other option is available means it will be perfect.
I guess Sessions getting reset due to application recycling. You should try using StateServer session mode.
However, the correct way of doing this using model. I would recommend you do do that.
public class LoginModel {
public bool IsClient { get; set; }
public bool IsUser { get; set; }
}
public ActionResult Index()
{
LoginModel model = new LoginModel();
model.IsClient = true;
return View(model);
}
View
#model LoginModel
#if (Model.IsClient)
{
//Client Login
}
else
{
//User Login
}
UPDATED
Also, in order to identify login method in some other partial view.
[HttpPost]
public ActionResult Index(LoginModel model)
{
if (model.IsClient)
{
//Identify if Client Login and pass to some other partial view wich you need
OtherPartialViewModel m = new OtherPartialViewModel();
m.IsClient = true;
}
return View(model);
}

How to omit/prevent data from being sent to the POST method in the Controller in MVC

I have a view that is using a model and I am using that information to create a form.
I have three steps of the form that are optional or may not be shown.
The problem is that these hidden sections get posted along with the form data and break the business logic. (I have no control over the business logic)
So is there a way to tell the framework not to pass certain sections or fields? Perhaps VIA a class or something?
I know I could use AJAX to send certain sections as they are needed, but the site spec is to have them hidden and displayed as needed.
Although you could do this client-side, it won't stop malicious over-posting/mass assignment.
I suggest reading 6 Ways To Avoid Mass Assignment in ASP.NET MVC.
Excerpts:
Specify Included Properties only:
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
// ...
}
Specify Excluded Properties only:
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
Use TryUpdateModel()
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel(user, includeProperties: new[] { "FirstName" });
// ...
}
Using an Interface
public interface IUserInputModel
{
string FirstName { get; set; }
}
public class User : IUserInputModel
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel<IUserInputModel>(user);
// ...
}
Use the ReadOnlyAttribute
public class User
{
public string FirstName { get; set; }
[ReadOnly(true)]
public bool IsAdmin { get; set; }
}
Lastly, and the most recommended approach is to use a real ViewModel, instead a domain Model:
public class UserInputViewModel
{
public string FirstName { get; set; }
}
Show/Hide will not allow/disallow the value from being sent to the Controller.
Elements that are Disabled or just not editable will (99% of the time) be returned as null / minVal.
You can set the elements in the View as Disabled by using JQuery in the script:
$('#elementID').attr("disabled", true);
OR you could use a DOM command:
document.getElementById('elementID').disabled = "true";
So you can set the fields as both Disabled AND Hidden, so that it is neither displayed, nor populated. Then in your Controller you can just base the Business Logic on whether or not certain fields (preferable Mandatory fields, if you have any) are null.
You can check this in C# like this:
For a string:
if (string.IsNullOrWhiteSpace(Model.stringField))
{
ModelState.AddModelError("stringField", "This is an error.");
}
For a DateTime:
if (Model.dateTimeField == DateTime.MinValue)
{
ModelState.AddModelError("dateTimeField ", "This is an error.");
}
Just for interest sake, here is how you can Hide/Show elements on the View using JQuery:
$('#elementID').hide();
$('#elementID').show();

How to use MembershipUser.IsApproved for account confirmation

Here's what I want to achieve with ASP.Net MVC 3 :
Before a user can actively use my website, I want them to fill a form and give a few details. Unless they have done so, they get redirected to the form website.
I thought this would be feasible by registering the account with the IsApproved-flag set to false, but actually such users cannot login at all (Membership.ValidateUser(model.UserName, model.Password) always returns false).
Is there any mechanism in forms authentication that supports this scenario? Or will I have to track this in a custom database table?
Thanks in advance!
EDIT : Thanks to MystereMan (read his reply first) and a few other great posts here on SO, here's the ActionFilter (Attribute and its usage):
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class RedirectUserInRoleAttribute : ActionFilterAttribute
{
public string RoleName { get; set; }
public string TargetControllerName { get; set; }
public string TargetActionName { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MembershipUser user = Membership.GetUser();
if (user != null)
{
string[] roles = Roles.GetRolesForUser(user.UserName);
if (roles.Any(x => x == RoleName))
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", TargetActionName);
redirectTargetDictionary.Add("controller", TargetControllerName);
filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
}
}
[Authorize]
[RedirectUserInRole(RoleName = "Pending", TargetControllerName = "Home", TargetActionName = "Index")]
public ActionResult ChangePassword()
{
//...
}
[Authorize]
[RedirectUserInRole(RoleName = "Member", TargetControllerName = "Home", TargetActionName = "Index")]
public ActionResult Confirm()
{
// show confirm form here!
}
I think the easiest solution would be to create a "Pending" membership role. When you first create the account, assign them to the "Pending" role. Then, you can create a Action filter that will redirect all users in the Pending role to your form.
EDIT:
A few gotchas to watch out for:
Make sure you reassign them to a different role after they've completed the form
Make sure you don't redirect them on the form action itself, or it's post action (otherwise you will be in an infinite loop)
You may want to prevent users in other roles from navigating to the form.
You have to track that information by yourself. You can create an own table as you suggested and link it to the aspnet_users table or add a column to the aspnet_users table.

Using NerdDinner as base reference, how to perform data access tasks in controller?

I am trying to follow the Nerd Dinner MVC application as a base to learn the correct way to develop MVC applications.
I have created Interfaces and Repositories as the reference code suggests and am using Entity Framework for data access.
If I want to insert data when a user registers into a table [dbo].[Users], I do not have a controller for Users, how do I do it?
AccountController.cs
[HandleError]
public class AccountController : BaseController
{
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email);
if (createStatus == MembershipCreateStatus.Success)
{
// TODO: Enter record into [Users] get reference to [Aspnet_UserId]
// How do I do this??
//FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
}
If I create a UsersController to display views based on the Users table, how would I then add a new record when the user is registering?
I have a separate table [Users] that I wish to populate when a new user registers adding the [Aspnet_UserId] Guid.
You don't need to have a controller for each table in your database. In the code above, the MembershipService is the code that is actually creating the record (via the Repository for users).
The controllers should represent various areas and groups of functionality your website provides. While in many cases, you might have a controller with View, Create, and Update actions that do relate to a specific entity, that does relate to a specific database table, that isn't and shouldn't always be the case.
If it makes sense to have a UsersController because you want to view a list of users, or a specific users profile, that's fine, but the form for creating a user doesn't have to be a part of that controller. Having it be a part of a membership, or admin, or account, or registration controller is ok too.
Update
I'll try to provide you sample code of how I would expect the code to look. But you might have something else in mind, which is fine too, there's no true single way to do these things.
In your code above, I'm not sure what your MembershipService class is doing. It appears there is a static method on it that does something related to User Creation. I would expect that your MembershipService class should be calling your UserRepository to actually do the user creation. But you probably wouldn't want a static class/method for this.
public class MembershipCreationResult
{
public User Member { get; private set; }
public MembershipCreateStatus MembershipCreateStatus { get; private set; }
public MembershipCreationResult(User user, MembershipCreateStatus status)
{
Member = user;
MembershipCreateStatus = status;
}
public bool Success
{
get { return MembershipCreateStatus == MembershipCreateStatus.Success; }
}
}
public class MembershipService
{
public IUserRepository { get; private set; }
public MembershipService(IUserRepository userRepository)
{
UserRepository = userRepository;
}
public MembershipCreateResult CreateUser(string name, string password, string email)
{
User member = UserRepository.Create(name, password, email);
bool status = member != null ? MembershipCreateStatus.Success : MembershipCreateStatus.Failure;
return new MembershipCreationResult(status, member)
}
}
I haven't taken a very close look at the NerdDinner sample, and I haven't used the ASP.NET membership provider, but the concept I have outlined above should work. If MembershipService does something way different from what I have outlined, then you could create a new service to wrap the functionality and leave the existing MembershipService alone.

Resources