How i can allow passing passwords that contain unsafe words - asp.net

I am working on an asp.net MVC4 web application, and for the login i am using form authentication that is connected to our AD using LDAP connection string, as follow.
The login Get action method:-
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
returnUrl = TempData["returnUrl"] != null ? TempData["returnUrl"].ToString() : String.Empty;
List<String> domains = new List<String>();
domains.Add("*****");
ViewBag.ReturnUrl = returnUrl;
ViewBag.Domains = domains;
return View();
}
The Login Post action method:-
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
MembershipProvider domainProvider;
domainProvider = Membership.Providers["TestDomain1ADMembershipProvider"];
if (ModelState.IsValid)
{
// Validate the user with the membership system.
if (domainProvider.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
List<String> domains2 = new List<String>();
domains2.Add("****");
ViewBag.Domains = domains2;
return View(model);
}
return RedirectToLocal(returnUrl);
}
List<String> domains = new List<String>();
domains.Add("****");
ViewBag.Domains = domains;
return View(model);
}
Now i have been using this system for 5 years and never face any issue with user login. but today a new user mentioned that when he tried to login to the system he will get an error "Error while processing your request.", and this error will be raised in our system if an unhandled exception is being raised. and i checked the IIS log and i can see that the user did not access the system.
so the only issue i can think of is that the user's password contain characters which MVC does not allow. for example i tried to type this password <script>#1234 along with my username, and instead of getting this message "The user name or password provided is incorrect." , i got the same exception "Error while processing your request.".. of course i can not ask the user to mention his password, but i am not sure how i can allow MVC to accept any password passed to it? as i can not think of any other problem...because usually when users login to the application they either; login successfully Or get "The user name or password provided is incorrect." , but i never face a problem where the user get an exception... which i were able to replicate by passing unsafe characters <script>#1234

In your LoginModel make add the AllowHtmlAttribute to the Password field. This will bypass the check against input like <script>.
[AllowHtml]
public string Password { get; set; }
Use it sparing, of course. MVC is trying to protect you from script injection attacks, so you should only disable this check in a few cases where you can be sure the value is handled safely. (In this case, passwords should never be displayed to the user, so even if there is HTML in it, it cannot inject HTML onto the page).
Alternatively, if you cannot alter the LoginModel class, you could try adding the ValidateInputAttribute to your Login POST method.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Login(LoginModel model, string returnUrl)

Related

Show only one Register error instead of two

I have an ASP.Net MVC site that uses ASP.Net Identity for managing users.
I use the default mechanism that the Email of the user is also his username. When a user wants to register with an Email which is already taken two error messages are shown:
Name myEmail#gmail.com is already taken.
Email 'myEmail#gmail.com' is already taken.
The structure of the Register form:
HttpPost Register Method of AccountController:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser {UserName = model.Email, Email = model.Email};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
string callbackUrl =
await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account");
return View("Info");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
How can I only show the Email Error message to the user?
Replace your AddErrors() method with
ModelState.AddModelError("", String.Format("{0} is already taken", model.Email))
or, if you have included #Html.ValidationMessageFor(m => m.Email) in the view and want the message to be associated with the form control, rather than in the summary
ModelState.AddModelError("Email", String.Format("{0} is already taken", model.Email))
Side note: You might also want to consider using a RemoteAttribute so that you get client side validation before submitting the form - refer How to: Implement Remote Validation in ASP.NET MVC (although you should also keep the code above in (the rare) case 2 users submit the same Email at the same time).

How to disable disabled account to use applicaton

ASP .NET MVC3 application uses login controller from MVC3 application template below. Controllers are decorated by [Authorize] attribute:
[Authorize]
public class CheckoutController : ControllerBase
User enters user name and password and click "remember me" checkbox. Authorization cookie is stored in browser and credentials are no more asked on subsequent accesses.
User can disabled by setting disabled fields in accounts database.
If such user tries to log on membership provider returns error and login fails.
However if user is already logged in, membership provider is not called. Disabled user can continue to use application.
How to fix this so that disabled user cannot use application ?
LogOn controller is basically from Visual Studio MVC3 sample application template:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (string.IsNullOrWhiteSpace(model.UserName) || string.IsNullOrWhiteSpace(model.Password))
{
ModelState.AddModelError("", "User or password not filled");
return View(model);
}
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Logon error");
}
return View(model);
}

ASP.net identity - external login - won't log out

In my application, all my authentication happens with Google - ie - all my users are Google Accounts.
I don't need users to need to register in my app, just sign in using a Google account. However, I do want to manage Roles for the users with ASP.net Identity (I think)
With that in mind, on successful external authentication, I create an ASP.net Identity user (if one doesn't exist)
So, I've got my ExternalLoginCallback as follows:
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var authenticationManager = Request.GetOwinContext().Authentication;
var loginInfo = await authenticationManager.GetExternalLoginInfoAsync();
//successfully authenticated with google, so sign them in to our app
var id = new ClaimsIdentity(loginInfo.ExternalIdentity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(id);
//Now we need to see if the user exists in our database
var user = UserManager.FindByName(loginInfo.Email);
if (user == null)
{
//user doesn't exist, so the user needs to be created
user = new ApplicationUser { UserName = loginInfo.Email, Email = loginInfo.Email };
await UserManager.CreateAsync(user);
//add the google login to the newly created user
await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
}
return RedirectToLocal(returnUrl);
}
Idea being, I can now manage users, add roles, check if users are in roles, etc....
Firstly, is this a sensible approach? Or have I over complicated it?
One issue I'm having, however, is with logging out of my application
My Logout action looks like:
public ActionResult LogOut()
{
HttpContext.GetOwinContext().Authentication.SignOut();
return RedirectToAction("Index", "Home");
}
My Index action is decorated with the [Authorize] attribute -
However, when I 'logout' - it redirects to Home.Index - but I still seem to be logged in?
According to this ASPNet Identity Work Item, this is by design, and you need to call directly to Google's API in order to log the user out.
completing the post Logout link with return URL (OAuth)
Here is a solution that work for me :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return Redirect("https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=https://[url-of-your-site]");
}

Create Cookie in MVC 3

How can i create a cookie step by step,
that stores the user login id and password when he/she clicks Remember Me? option
and i am planing to kill this cookie after certain time
Cookies are created the same way as they are in plain old ASP.NET, you just need to access the Response.
public ActionResult Login(string username, string password, bool rememberMe)
{
// validate username/password
if (rememberMe)
{
HttpCookie cookie = new HttpCookie("RememberUsername", username);
Response.Cookies.Add(cookie);
}
return View();
}
However, if you're using Forms Auth, you can just make your FormsAuth ticket cookie persistent:
public ActionResult Login(string username, string password, bool rememberMe)
{
// validate username/password
FormsAuthentication.SetAuthCookie(username, rememberMe);
return View();
}
You can read cookies like this:
public ActionResult Index()
{
var cookie = Request.Cookies["RememberUsername"];
var username = cookie == null ? string.Empty : cookie.Value; // if the cookie is not present, 'cookie' will be null. I set the 'username' variable to an empty string if its missing; otherwise i use the cookie value
// do what you wish with the cookie value
return View();
}
If you are using Forms Authentication and the user is logged in, you can access their username like this:
public ActionResult Index()
{
var username = User.Identity.IsAuthenticated ? User.Identity.Name : string.Empty;
// do what you wish with user name
return View();
}
It is possible to decrypt and read the contents of a ticket. You can even store small amounts of custom data in the ticket, if you need to. See this article for more info.

How to check/assign user's roles right after signing in?

For my application when the user logs in I need to check if they have any roles assigned to them and if they don't assign them a basic role. Also based on their role I have to redirect them to certain pages. But I can't find a way to check/set user's roles when they sign in.
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if(User.IsInRole("User"))
//This won't work becauser User is not populated yet. It will be populated only on next request for some reason that has to do with setting cookies
}
Any ideas how to work this out?
Some thing like this?
if(User.IsInRole(model.UserName, "User"))
Apparently Roles.IsUserInRole method allows to pass string as username, so it makes possible to check users membership without calling the User object.

Resources