MVC5 ApplicationUser custom properties - asp.net

I am trying to get to grips with the new Membership system introduced in ASP.NET MVC 5 and I've come across a small issue which I am pretty sure you will be able to help me with.
I am going based off this tutorial and have introduced custom properties to ApplicationUser such as Name, Surname, DOB, etc.
However, instead of creating the user, I am trying to update the currently logged in one. I am looking at the controller method which is currently used to change password.
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
string userId = User.Identity.GetUserId();
bool hasLocalLogin = await IdentityManager.Logins.HasLocalLoginAsync(userId);
ViewBag.HasLocalPassword = hasLocalLogin;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasLocalLogin)
{
if (ModelState.IsValid)
{
IdentityResult result = await IdentityManager.Passwords.ChangePasswordAsync(User.Identity.GetUserName(), model.OldPassword, model.NewPassword);
if (result.Success)
{
return RedirectToAction("Manage", new { Message = "Your password has been changed." });
}
else
{
AddErrors(result);
}
}
}
else
{
// User does not have a local password so remove any validation errors caused by a missing OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
// Create the local login info and link it to the user
IdentityResult result = await IdentityManager.Logins.AddLocalLoginAsync(userId, User.Identity.GetUserName(), model.NewPassword);
if (result.Success)
{
return RedirectToAction("Manage", new { Message = "Your password has been set." });
}
else
{
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
How exactly would I go on about updating an ApplicationUser's Surname for example? Do I need to call the DbContext or?
I hope my question is clear.

Explore IdentityManager.Store.UserManagement and IdentityManager.Store.Users.
ApplicationUser cUser = (ApplicationUser) await IdentityManager.Store.Users.FindByNameAsync(HttpContext.User.Identity.Name, new System.Threading.CancellationToken());
cUser.Surname = "New Something";
IdentityResult result1 = await IdentityManager.Store.SaveChangesAsync();
Above code is an example only. Basically you need to explore the Store property of IdentityManage.

When we used the Users object of our database context we ran into other tracking errors. In our application, we would retrieve users as such
var user = UserManager.FindById(userId);
Edit the properties:
user.StorageName = "gooblygook";
//whatever other properties you would like to use
And then we would save it with the UserManager in the controller:
UserManager.Update(user);
This is currently a working solution for us.

Mark the Person object as virtual in your ApplicationUser definition. That worked for me.
public class ApplicationUser : IdentityUser
{
public virtual Person Person { get; set; }

Related

asp.net mvc multitenant database per tenant

I'm building an app for our company which needs to have separate database per client. App is for the usage of other multiple companies, so the app needs to identify the company name when the user logs in and make the users operate only within their company db.
I have it all set, but the problem is that the app is not able to handle 2 different databases simultaneously. When users from two different companies log in, the first users db gets changed to the db of the second user who is logged in! This is of course unacceptable. How can I make the app to use 2 dbs simultaneously?
I have one database which collects all app users and their company names and separate databases for each company. I also have a standard asp
below are my codes:
Login class and database initializer in account controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
bool ifDemo = false;
string demoPrefix = "demo.";
if (model.Email.StartsWith(demoPrefix))
{
ifDemo = true;
model.Email = model.Email.Substring(demoPrefix.Length);
}
SetDatabaseInitializerAndInitializeIt(ifDemo, model.Email);
// 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:
returnUrl = CheckFirstLogin(model.Email, returnUrl);
await OnSignInSuccess(model);
//FormsAuthentication.SetAuthCookie(model.Email, false);
return RedirectToLocal(returnUrl);
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);
}
}
public static void SetDatabaseInitializerAndInitializeIt(bool demoDB, string login)
{
Database.SetInitializer(new ApplicationUsersSeedData());
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Facility.Data.FacilityEntities,
Facility.Migrations.Configuration>());
// Check current users company name and set the right database.
// To use demo version of database add "demo." prefix to the login email e.g: demo.testspamu79#gmail.com
using (var domain = new Facility.Models.UserCommonDBContext())
{
domain.Database.Initialize(true);
}
UserCommonDBContext context = new Facility.Models.UserCommonDBContext();
var companyName = context.CommonUser.Where(x => x.CommonUserEmail == login).FirstOrDefault().CommonUserCompanyName;
if (demoDB)
companyName = companyName + "Demo";
using (var domain = new Facility.Data.FacilityEntities(companyName))
{
domain.Database.Initialize(true);
}
}
Dbcontext:
public partial class FacilityEntities : DbContext
{
public static string DbName;
public FacilityEntities() : base(string.IsNullOrWhiteSpace(DbName) ? "Demo" : DbName)
{
}
public FacilityEntities(string dbName) : base(dbName)
{
DbName = dbName;
}
as Tielson T. said in the comments, I got rid of static and stored db name in session, and now it works!

Insert Data Into another table during Registration in ASP.NET MVC5

I understand that ASP.NET MVC creates a default table "AspNetUser" for storing users information during registration. How do I get Some of those data such as "Username" and store it in a seperate table during the registration process.
Am trying to get the "username" supplied by the user which is by default stored in the "AspNetUser Table" and store it in the "Student Table" also during the registration process
Register action
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
StudentEntities db = new StudentEntities();
var student = new Student
{
Name = model.Username
};
db.Student.Add(Student);
db.SaveChanges();
var user = new ApplicationUser() { UserName = model.UserName };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Welcome", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
This doesn't seem to work
This doesn't seem to work
StudentEntities db = new StudentEntities();
var student = new Student
{
Name = model.Username
};
db.Student.Add(Student);
db.SaveChanges();
If you are using MVC5, it means you are using identity2. So It is better to use that to get user details. The best approach is doing this:
Add the following using statement.
using Microsoft.AspNet.Identity;
You can now get more methods from the HttpContext.User.Identity.
var UserName = User.Identity.GetUserName();
var UserId = User.Identity.GetUserId();
Be noted that identity usually uses email as username. But you can change it anyway.

ASP.NET Identity 2 Invalid no Two factor Provider Exists

For some reason EmailCode is not showing up in the valid two factor authentication providers. PhoneCode however did up until I removed it, now nothing shows up at all. I have debugged and it shows up under the UserManager, but for some odd reason GetValidTwoFactorProvidersAsync doesn't retrieve it. I've already attempted to manually add it by bypassing the method and retrieving the value manually, but then it throws the error message that the Microsoft.AspNet.Identity.EmailTokenProvider does not exist. I am at a loss to explain why this isn't working.
public async Task<ActionResult> SendCode(string returnUrl)
{
var userId = await SignInManager.GetVerifiedUserIdAsync();
if (userId == null)
{
return View("Error");
}
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl });
}
Identityconfig
manager.RegisterTwoFactorProvider("EmailCode", new Microsoft.AspNet.Identity.EmailTokenProvider<SystemUser>
{
Subject = "SecurityCode",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<SystemUser>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
Make sure you have a ConfirmedEmail for the User. You can look up the user in the database and look at the EmailConfirmed flag to see if it is set or not.
Since I can't see your codes and hence can't suggest a proper fix but I can tell you how I did it and all went okay. It came very handy and easy for me when I tried configuring the application for 2 way authentication using these links:
http://www.asp.net/identity/overview/features-api/two-factor-authentication-using-sms-and-email-with-aspnet-identity
http://typecastexception.com/post/2014/04/20/ASPNET-MVC-and-Identity-20-Understanding-the-Basics.aspx
Suggest you to try them if you haven't yet. Hope it helps!

Adding user data to manage page in MVC5

I've been following the Add Profile Data to User Class part of this tutorial to add more fields to my registration page in MVC 5. So far it's working fine and I have no issues. The problem is now I'm not sure on how that gets displayed on the Manage page where the user can see his profile info like changing his password. For example, I want to add on a first and last name on that page.
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on#ap
Here is a screenshot of the page I'm talking about:
http://puu.sh/dcMmj/82ddd8fc97.PNG
My project is what Visual Studio creates for you with the added first and last name in the registration page. Added this in the following Identity Model like in the tutorial
public class ApplicationUser : IdentityUser
{
public string FirstName{ get; set; }
public string LastName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
I'm thinking I need to add something here in the ManageController.cs but I'm not sure.
// GET: /Manage/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(User.Identity.GetUserId()),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(User.Identity.GetUserId()),
Logins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId()),
// I think it goes in here somewhere
};
return View(model);
}
You can replace your var model with this code:
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var model = new IndexViewModel {
HasPassword = HasPassword(),
PhoneNumber = user.PhoneNumber,
TwoFactor = user.TwoFactorEnabled,
Logins = await UserManager.GetLoginsAsync(user),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(user),
FirstName = user.FirstName
}
Now you can use model.FirstName in your View.

Roles Create New DataBase When I Add a user to a role in MVC 5

i create my own database and add user identity table to this by change the connection string.
now my connection string is this:
when i create a new user it worked well.
but when i change the Register(RegisterViewModel model) in RegisterControler to add a user to a role like this code:
public async Task Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//add user to member role******************
if (!Roles.RoleExists("Member"))
Roles.CreateRole("Member");
Roles.AddUserToRole(model.Email, "Member");
//*******************************************
await SignInAsync(user, isPersistent: false);
// 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
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
user registerd but dont add to member Role! and it seem there is another connection string for Roles! beacause whene run this code ASPNETDB.MDF created in App_Data!
Please help me to solve this problem
In order to create roles in asp.net identity, you need to use AspNetRoleManager same as you are currently using AspNetUserManager.
The AspNetUserManager may looks like below.
public class AspNetRoleManager : RoleManager<IdentityRole, string>
{
public AspNetRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{
}
public static AspNetRoleManager Create(IdentityFactoryOptions<AspNetRoleManager> options, IOwinContext context)
{
return new AspNetRoleManager(new RoleStore<IdentityRole, string, IdentityUserRole>(context.Get<YourDataContext>()));
}
}
Then you need to register AspNetRoleManager in the owin startup. Same like the AspNetUserManager.
app.CreatePerOwinContext<AspNetRoleManager>(AspNetRoleManager.Create);
After that you can use it inside the controller to create roles.
var roleManager = HttpContext.GetOwinContext().Get();
// Check for existing roles
var roleManager = HttpContext.GetOwinContext().Get<AspNetRoleManager>();
var roleExists = await roleManager.RoleExistsAsync("Member");
if (!roleExists)
{
var role = new IdentityRole();
role.Name = "Member";
var result = roleManager.CreateAsync(role);
}
Then add new role to the user.
var user = await UserManager.FindByEmailAsync(model.Email);
var roleRsult = UserManager.AddToRole(user.Id, roleName);

Resources