Trying to use the WebSecurity and if my user loses, needs to resend, or otherwise changes their email, their does not seem to be any way to reset your confirmation token via the WebSecurity classes.
How do I reset the confirmation token for WebSecurity in Asp.NET MVC?
There doesn't seem to be support for this in the WebSecurity type. I was forced to query the database directly. Since I was using EF my code looked like the following:
public string GetConfirmationToken(string email)
{
using (var db = new DbContext())
{
var tsqlQuery = string.Format("SELECT [ConfirmationToken] FROM [webpages_Membership] WHERE [UserId] IN (SELECT [UserId] FROM [UserProfile] WHERE [Email] LIKE '{0}')", email);
return db.Database.SqlQuery<string>(tsqlQuery).First();
}
}
Related
I have a web app that allows a user to pay for a holiday booking.
The user will log in using a combination of a unique booking ID and a booking password.
Is there a simple way I can authenticate the user once they have successfully logged in. Is it a good idea to use the .net session object for this purpose?
The app wont be load balanced so I don't need to worry about storing session across different machines.
I would like to not have to use .Net membership as I don't want to create any extra tables in the database.
If you are able to retrieve the user, I guess this is possible. Below is the way I would try:
public ActionResult UrlAuth(string bookingId, string bookingPass)
{
var userManager=HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
// Retrieve the user by the give booking values (Custom part in your app
string userId= _bookingRepository.GetUserIdByBooking(bookingId, bookinggPass);
var user = userManager.FindById(userId);
if (user != null)
{
var userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties { IsPersistent = false }, userIdentity );
// authentication succeed do what you want
return Redirect("Wherever");
}
ModelState.AddModelError("", "Invalid booking values");
return View();
}
Here's a good general article with custom authentication for MVC
http://www.ryadel.com/en/http-basic-authentication-asp-net-mvc-using-custom-actionfilter/
http://www.codeproject.com/Articles/1005485/RESTful-Day-sharp-Security-in-Web-APIs-Basic
MVC 5 with Dapper and User < int >
I followed ASP.Net's MVC 5 Guidance for Google OAuth2
I set up UseGoogleAuthentication in Startup.ConfigureAuth
Clicking the Google login button all seems to work, until the user tries to finalize to connection to the web app in
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
In the basic template of code created, when I created the project, the function creates a user object and tries to create the User:
var user = new PortalUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user);
Then I get the lovely error:
Source: .Net SqlClient Data Provider
Target: Void OnError(System.Data.SqlClient.SqlException, Boolean, System.Action`1[System.Action])
Type: SqlException
Message: Cannot insert the value NULL into column 'PasswordHash', table 'WebApplication1.dbo.AspNetusers'; column does not allow nulls. INSERT fails.
The statement has been terminated.
So, how am I supposed to pass a password hash, from an External Login?
Or - do I allow nulls in the table?
Or - is there some aspect of the UserManager / UserStore / PortalClaimsIdentityFactory that I'm not fulfilling (overriding) because I'm using User < int > User keys?
I have implemented the ForgotPassword (with token reset) into my MVC 5 application. We are in production. Although this works in majority of the cases, many of our end-users are of older age and get confused when they cannot login and need a reset. So in those situations, I am considering giving one of our admin staff the ability to reset a user's password and giving them the new password on the phone. The data is not that sensitive.
I tried this:
public ActionResult ResetPassword()
{ UserManager<IdentityUser> userManager =
new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var user = userManager.FindByEmail("useremail.samplecom");
userManager.RemovePassword(user.Id);
userManager.AddPassword(user.Id, "newpassword");
}
I get a cryptic error stating Invalid Column EMail, Invalid Column Email Confirmed ......
I also tried the userManager.ResetPassword(), but abandoned that idea because it needs a token reset. I want to bypass it.
What am I not seeing?
Thanks in advance.
I also tried the userManager.ResetPassword(), but abandoned that idea because it needs a token reset. I want to bypass it.
How about you just generate the token and pass it to the Reset routine ?
var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var code = await userManager.GeneratePasswordResetTokenAsync("username");
var result = await userManager.ResetPasswordAsync("username", code, "your new password");
if (!result.Succeeded)
{
//password does not meet standards
}
The idea here is you are just emulating/bypassing the usual routine of sending the token to the client (via email) and having the link that they click on call ResetPasswordAsync
I'm not completely sure if this will work in your implementation but I use the following code with success in a use case which has basically the same requirements as yours. The difference is that I'm not letting any user reset it's own password. This is always the task of an admin.
I'm bypassing the ApplicationUserManager and edit the information directly in the table, using just Entity Framework.
// I created an extension method to load the user from the context
// you will load it differently, but just for completeness
var user = db.LoadUser(id);
// some implementation of random password generator
var password = General.Hashing.GenerateRandomPassword();
var passwordHasher = new Microsoft.AspNet.Identity.PasswordHasher();
user.PasswordHash = passwordHasher.HashPassword(password);
db.SaveChanges();
You have to get the user from the database and generate the code not by username :
public async Task<Unit> ResetPassword(string userName, string password)
{
if (!string.IsNullOrWhiteSpace(userName))
{
var returnUser = await _userManager.Users.Where(x => x.UserName == userName).FirstOrDefaultAsync();
var code = await _userManager.GeneratePasswordResetTokenAsync(returnUser);
if (returnUser != null)
await _userManager.ResetPasswordAsync(returnUser, code, password);
}
return Unit.Value;
}
I am building ASP.NET MVC 4 application. I use Simple Membership provider to manage authentication and authorization within the system. What are the ways of changing the password in this approach. I found a ChangePassword method which takes three parameters, including original password, to operate.
Is there any other way to override/change the password for the user without actually knowing original password?
ChangePassword is used when a user wants to change their password - and the current password is their evidence to allow this to happen (think Change Password Screen).
I think the most direct way to do this is to call WebSecurity.GeneratePasswordResetToken() and pass the result into WebSecurity.ResetPassword, along with the new password.
var token = WebSecurity.GeneratePasswordResetToken("UserName");
var result = WebSecurity.ResetPassword(token, "NewPassword");
There is a detailed article on how to implement password reset/change with SimpleMembership in MVC 4 here. It also includes source code you can download.
This examples uses email to send a URL to the user to click on for password reset. This is more secure than just having the user enter the old password and new password directly on the website because it is another verification of the user. This alleviates the scenario where someone gets a hold of the user password and locks them out by changing the password. This also allows the user to reset the password in the case where they have forgotten the password.
The code to send the email with the link would look something like this.
[AllowAnonymous]
[HttpPost]
public ActionResult ResetPassword(ResetPasswordModel model)
{
string emailAddress = WebSecurity.GetEmail(model.UserName);
if (!string.IsNullOrEmpty(emailAddress))
{
string confirmationToken =
WebSecurity.GeneratePasswordResetToken(model.UserName);
dynamic email = new Email("ChngPasswordEmail");
email.To = emailAddress;
email.UserName = model.UserName;
email.ConfirmationToken = confirmationToken;
email.Send();
return RedirectToAction("ResetPwStepTwo");
}
return RedirectToAction("InvalidUserName");
}
This creates an email that has a link to a Web API that accepts the token as the id that is passed in. When they click on the link it hits this method.
[AllowAnonymous]
public ActionResult ResetPasswordConfirmation(string Id)
{
ResetPasswordConfirmModel model = new ResetPasswordConfirmModel() { Token = Id };
return View(model);
}
This action gets the token from the query string and puts it in the ResetPasswordConfirmationModel that is passed to the view which allows the user to enter the new password. The new password is entered twice to make sure they entered it correctly, which is validate on the page. When they submit this information they are taken to the POST version of this action which actually resets the password.
[AllowAnonymous]
[HttpPost]
public ActionResult ResetPasswordConfirmation(ResetPasswordConfirmModel model)
{
if (WebSecurity.ResetPassword(model.Token, model.NewPassword))
{
return RedirectToAction("PasswordResetSuccess");
}
return RedirectToAction("PasswordResetFailure");
}
I would like to setup a multi-tenant ASP.NET MVC app. Ideally, this app would have a route with {tenant}/{controller}/{action}/{id}, each tenant representing an logical instance of the app (simply independent multi-user accounts)
The fine grained details how do that are still quite unclear to me. Any guide available to setup such multi-tenant scheme with ASP.NET MVC?
I am currently working on a similar project using ASP.Net MVC, Forms Authentication and the SQL providers for Membership/Roles/Profile. Here is the approach I am taking:
Register the default route as `{tenant}/{controller}/{action}/{id}
Change the default behavior of the FormsAuthenticationService that comes with the standard MVC template. It should set the UserData of the authentication ticket to include the tenant name (from your route).
public void SignIn(string userName, bool createPersistentCookie, string tenantName)
{
var ticket = new FormsAuthenticationTicket(1, userName, DateTime.Now, DateTime.Now.AddMinutes(30),
createPersistentCookie, tenantName);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
HttpContext.Current.Response.AppendCookie(cookie);
}
In your global.asax file to do some tenant security checking and allow partioning of users between tenants in one membership database
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
//Since this method is called on every request
//we want to fail as early as possible
if (!Request.IsAuthenticated) return;
var route = RouteTable.Routes.GetRouteData(new HttpContextWrapper(Context));
if (route == null || route.Route.GetType().Name == "IgnoreRouteInternal") return;
if (!(Context.User.Identity is FormsIdentity)) return;
//Get the current tenant specified in URL
var currentTenant = route.GetRequiredString("tenant");
//Get the tenant that that the user is logged into
//from the Forms Authentication Ticket
var id = (FormsIdentity)Context.User.Identity;
var userTenant = id.Ticket.UserData;
if (userTenant.Trim().ToLower() != currentTenant.Trim().ToLower())
{
//The user is attempting to access a different tenant
//than the one they logged into so sign them out
//an and redirect to the home page of the new tenant
//where they can sign back in (if they are authorized!)
FormsAuthentication.SignOut();
Response.Redirect("/" + currentTenant);
return;
}
//Set the application of the Sql Providers
//to the current tenant to support partitioning
//of users between tenants.
Membership.ApplicationName = currentTenant;
Roles.ApplicationName = currentTenant;
ProfileManager.ApplicationName = currentTenant;
}
Partition each tenants data. Here are two options:
4a. Use a separate database for each tenant. This provides the best data security for your tenants. In the shared membership database, add a table that is keyed on unique appid for each tenant and use this table to store and retrieve the connection string based on the current tenant.
4b. Store all data in one database and key each table on the unique tenant id. This provides slightly less data security for your tenants but uses only one SQL Server license.
You will prob find these links useful.