I am planning to implement MVC 5.0 ASP.Net identity in a brand new application. I referred to the Microsoft article http://go.microsoft.com/fwlink/?LinkID=317594 to add the customer profile information in a separate table other than the identity tables.
However according my requirement, I would like to store the customer profile information in a separate database in order to segregate the user identity information and customer profile information in database level. The Identity uses single data store while creating user and the profile information, whereas I need to set two different store for the user and profile information. Do Anyone have any suggestions on this?
You could simply write a custom UserStore class and extend default UserStore class. Consider this simple example:
public class ApplicationUser : IdentityUser
{
// other codes
// Add your extra profile information
// By Adding NotMapped attribute EF omits this and dose not puts in Identity's table
[NotMapped]
public Profile Profile { get; set; }
}
public class Profile
{
public int ID { get; set; }
public string ExtraData { get; set; }
// other properties
}
Now we need custom User Store to put and fetch data from 2 DB
public class MyUserStore : UserStore<ApplicationUser>
{
public MyUserStore(DbContext context)
: base(context)
{
// other implementation for second DB
}
public override Task CreateAsync(ApplicationUser user)
{
// save Profile object to separate DB
_mySecondDB.Save(User.Id, user.Profile);
return base.CreateAsync(user);
}
public override Task UpdateAsync(ApplicationUser user)
{
// same pattern as CreateAsync
}
public override Task DeleteAsync(ApplicationUser user)
{
// same pattern as CreateAsync
}
public override async Task<ApplicationUser> FindByIdAsync(string userId)
{
var user = await base.FindByIdAsync(userId);
user.Profile = _mySecondDB.FindProfileByUserId(userId);
return user;
}
public override Task<ApplicationUser> FindByNameAsync(string userName)
{
// same pattern as FindByIdAsync
}
}
Now you just need to inject your custom User Store in Identity pipeline. To do so change ApplicationUserManager.Create static method in App_Start\IdentityConfig.cs like this:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(
new MyUserStore(context.Get<ApplicationDbContext>()));
// other codes
}
Related
Can anyone provide a good resource for setting up profile fields (ie FirstName, LastName, et cetera) in ASP.NET Identity using C# in Web Forms? Everything I've been able to find is either MVC specific or doesn't address profiles.
You may use Identity Claims for storing profile information. This is not a very nice solution, because it generates a lot of joins when user gets from DB. But up to certain sizes of the users database, you will not see a difference in performance.
Some code for example:
public class ApplicationUser : IdentityUser
{
ppublic async Task<ClaimsIdentity> AssignUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.AddClaim(new Claim("FirstName", this.FirstName.ToString()));
return userIdentity;
}
// Custom filed
public long? FirstName { get; set; }
//and so on
}
namespace Extensions
{
public static class IdentityExtensions
{
public static string GetFirstName(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("FirstName");
return claim ?? string.Empty;
}
// and so on
}
}
I have a Core 2.0 project that has reached the point where I need to start linking users to the data I have. It started as a one user test bed that now requires me to have multiple per user.
User Model:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<Balance> Balances { get; set; }
}
Balance model:
public virtual ApplicationUser User { get; set; }
...
ApplicationDbContext.cs:
//Users to balances
builder.Entity<ApplicationUser>()
.HasMany(b => b.Balances)
.WithOne(u => u.User);
In my code, when creating a new Balance, I cannot figure out how to link my current logged in user:
var myNewBalance = new Balance();
myNewBalance.User = User;
^ boom
How do I relate Balances to the currently logged in user?
The User property you've highlighted in your question is a ClaimsPrincipal that belongs to the ControllerBase class. The User property on your Balance class is an ApplicationUser, which is not compatible with ClaimsPrincipal. You already know this, but I'm just making it explicit in order to explain what you need to do.
As you are using ASP.NET Core Identity here, you can use the UserManager class, which is available using dependency injection. UserManager contains a function, GetUserAsync, that can convert your ClaimsPrincipal into the corresponding ApplicationUser.
Here's some code:
public class SomeController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public SomeController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> SomeAction()
{
var myNewBalance = new Balance();
myNewBalance.User = await _userManager.GetUserAsync(User);
return View(); // etc, etc.
}
}
The key points are the use of the generic UserManager that uses ApplicationUser and then the invocation of GetUserAsync using the User (ClaimsPrincipal) property.
Currently have ApplicationUser class with some custom properties, like:
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public List<Content> Content { get; set; }
}
I'd like to get the current logged user with the list of related data (Content property).
In my controller, if I put:
Applicationuser user = await _userManager.GetUserAsync(HttpContext.User);
I get the logged user, but without any related data.
But, if I retrieve the current user using the ApplicationDbContext, like below, I can retrieve the related data:
ApplicationUser user = await _userManager.GetUserAsync(HttpContext.User);
ApplicationUser userWithContent = _context.Users.Include(c => c.Content).Where(u => u.Id == user.Id).ToList();
But this doesn't appear correctly for me!
Any idea?
checking the source code of [UserManager][1], GetUserAsync will end up calling FindByIdAsync, which will be provided by an IUserStore implementation. Looking the source code in the question, very likely using EFCore as the IUserStore implementation.
In case of EFCore, it was mention here that Find cannot be combined with include, so I guest what've you done to do eager loading in your question, may actually correct.
If you need those properties in every request, there's a better way to get them without query the database in each request.
You can store those properties as Claims by writing your own IUserClaimsPrincipalFactory<TUser> and reading them from HttpContext.User
public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
{
public CustomUserClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor)
: base(userManager, optionsAccessor)
{
}
protected async override Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
{
ClaimsIdentity identity = await base.GenerateClaimsAsync(user);
identity.AddClaim(new Claim("Name", user.Name));
for (int i = 0; i < user.Content.Count; i++)
{
string content = Newtonsoft.Json.JsonConvert.SerializeObject(user.Content[i]);
identity.AddClaim(new Claim("Content", content));
}
return identity;
}
}
Also you need to register it to service collection
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();
You can access these properties with this code
string name = httpContext.User.FindFirstValue("Name");
List<Content> contents = new List<Content>();
foreach (Claim claim in httpContext.User.FindAll("Content"))
{
Content content = Newtonsoft.Json.JsonConvert.DeserializeObject<Content>(claim.Value);
contents.Add(content);
};
I am using the repository and unit of work patterns and dependency injection to access the database with entity framework 5 in my web application. I have a User class from which Entity Framework generates a Code-First database.
public class User
{
public Guid UserId { get; set; }
public string UserName { get; set; }
.
.
.
public string LanguagePreference { get; set; }
public virtual List<Role> Roles { get; set; }
public virtual List<Branch> Branches { get; set; }
}
I have a UserService class that is used to Add or Update users. This class takes an IUserUnitOfWork as a parameter in the constructor and Unity injects a UserUnitOfwork. The IUserUserOfWork contains an IRepository<User>, an IRepository<Location> and an IRepository<Role>. These are set as Repository<T> by the DI bootstrapper. The IUserUnitOfWork sets up the different Repositories with the same entity framework DbContext. I did this as I was having issues updating the many-to-many relationships related to the User (Locations and Roles).
UserUnitOfWork:
public IRepository<Branch> BranchRepository {get; set;}
public IRepository<Role> RoleRepository { get; set; }
public IRepository<User> UserRepository { get; set; }
public DbContext Context { get; set; }
public UserUnitOfWork(DbContext context, ITransientErrorDetectionStrategy errorDetectionStrategy,RetryStrategy retryStrategy )
{
Context = context;
BranchRepository = new Repository<Branch>(context, errorDetectionStrategy, retryStrategy);
RoleRepository = new Repository<Role>(context, errorDetectionStrategy, retryStrategy);
UserRepository = new Repository<User>(context, errorDetectionStrategy, retryStrategy);
}
The Repository class then uses Entity Framework 5 to access the database.
Example of method from Repository.FirstOrDefault:
public virtual T FirstOrDefault(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
{
T result = null;
_retryPolicy.ExecuteAction(() =>
{
IQueryable<T> entities = GetHelper(filter, orderBy, includeProperties);
result = entities.FirstOrDefault();
});
return result;
}
And Update from Repository:
public virtual void Update(T entity)
{
if (_dbContext.Entry(entity).State == System.Data.EntityState.Detached)
{
_dbContext.Set<T>().Attach(entity);
_dbContext.Entry(entity).State = System.Data.EntityState.Modified;
}
}
So my problem now is that when I update the User it correctly updates the data in the database, and when I log out and log in the initial change works. However if I update again and log out and in the new change isn't picked up even though the database is updated.
I'm beginning to fear that the approach I've taken is incorrect, can someone tell me how to make sure that when I do an update Entity Framework will always get the latest version?
EDIT:
So I've created a Per Request Lifetime Manager like so:
public class PerHttpRequestLifetimeManager : LifetimeManager
{
private readonly object key = new object();
public override object GetValue()
{
if (HttpContext.Current != null &&
HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public override void RemoveValue()
{
if (HttpContext.Current != null)
HttpContext.Current.Items.Remove(key);
}
public override void SetValue(object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
In my DI bootstrapper I now setup my domain context like below:
container.RegisterType<DbContext, DomainContext>(new PerHttpRequestLifetimeManager());
It still doesn't appear to be working, am I missing something else or am I setting it up incorrectly?
EDIT 2:
Just to point out the architecture:
We have an MVC application which uses Angular JS to make ajax calls to a Web Api service layer. The Web Api has an ISomethingService injected into it. It is this ISomethingService that has the repositories injected into it. Would there be some confusion for the PerHttpRequestLifetimeManager since there is both an MVC and Web API project running?
EDIT 3:
An example of how I am saving the edited user:
We have a UserModel class that is used for communications between the ServiceLayer -> API -> UI layer and back. The User class is the one generated by Entity Framework code first. The EditUser method in the UserService takes in a UserModel.
I then user the _unitOfWork.UserRepository to get the corresponding database user
var editedUser = _unitOfWork.UserRepository.FirstOrDefault(x => x.UserId == userModel.UserId);
I map the fields from the userModel to the editedUser and I then call (in the UserService)
_unitOfWork.UserRepository.Update(editedUser)
and after
_unitOfWork.Save()
YET ANOTHER EDIT:
So I have edited a simple method that updates a single text field on the user table (Language Preference). I explicitly call the dispose method after the update to ensure I am disposing the method.
public void SetUserLanguagePreference(Guid userId, string language)
{
var user = _unitOfWork.UserRepository.FirstOrDefault(x => x.UserId == userId);
user.LanguagePreference = language;
_unitOfWork.UserRepository.Update(user);
_unitOfWork.Save();
_unitOfWork.Dispose();
}
UnitOfWork.Dispose() calls the dispose method of the repositories and the Dbcontext
The database updates correctly. However the behaviour is still incorrect. When I log out and in first it retrieves the correct value. When I change it again and log out and in again it doesn't update. This has been the pattern before, it get the first update after I log out and in, but if I change again and log out and in it doesn't pick it up.
Finally, not an edit but an answer! We use Claims based authentication and have a class that overrides the ClaimsPrinciple Authenticate method that is called whenever a user is authenticated.
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal.Identity.IsAuthenticated)
{
//Check here if the authenticated user has access to this system
//using the user repository and if so add more claims to the token
}
return base.Authenticate(resourceName, incomingPrincipal);
}
It was not possible to inject into this method using DI as it always went to the empty constructor (not sure why but that's the way it is).
So instead we were setting the repository in the empty constructor like so:
public PRAuthenticationManager()
{
_userRepository = DiBootstrapper.Container.Resolve<IRepository<User>>();
}
When the Authenticate method is called we check our database for a user with the claims attached to the ClaimsPrincipal. If we make a match we add new claims to the token which are then used for each call to the Web Api later. This repository was not being disposed (even if all the others were) and so when a user logged out and in they got data from that same context which had not been disposed from the last time the user logged in.
Three full days trying to find that one....
See if this helps: How do I get Entity Framework 5 to update stale data
I ran into the same problem, it doesn't refresh from the database if you already have the object in your ObjectContext, of course, this would only work on a per object basis, but that might be just what you need.
I have an object that contains all login data, that's in my controller (it was programmed before switching to MVC3).
I'm trying to add authorization to the site, so so far I have:
public LoginObject MyLoginObject
{
get;
set;
}
[CustomAuthorization()]
public ActionResult Index()
{
return View();
}
and
public class CustomAuthorization : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return true;
//should be return myLoginObject.IsLoggedIn;
}
}
Is there anyway to pass MyLoginObject into the AuthorizeAttribute class? If not could I at least pass in a boolean from the object that specifies if the user is authorized or not?
Edit: My solution based on Zonnenberg's advice.
public class LoginObject : IPrincipal // Now extends IPrincipal
{
... //old code
private class IdentityImpl : IIdentity
{
public string AuthenticationType
{
get;
set;
}
public bool IsAuthenticated
{
get;
set;
}
public string Name
{
get;
set;
}
}
public IIdentity Identity
{
get { return new IdentityImpl { AuthenticationType = "Custom Authentication", IsAuthenticated = this.IsLoggedIn, Name = this.Id}; }
}
}
Then I moved the instantiation of loginobject into CustomAuthorization
public override void OnAuthorization(AuthorizationContext filterContext)
{
// ... Set up LoginObject
filterContext.RequestContext.HttpContext.User = myLoginObject;
base.OnAuthorization(filterContext);
}
So now logging in, is done inside the authorization, and I can call User to access the login from the controller.
You can check wheter the user is logged in by using httpContext.User.Identity.IsAuthenticated.
To store more information you could use the httpContext.User object. You can write your own implementation of IPrincipal and IIdentity to store all kinds of login information.
Other option is to store login info in the Session.
How is your LoginObject instantiated?
If it's instantiated via a service or repository (ex. MyLoginObject = loginService.GetLogin() then you can move this call into the CustomAuthorization attribute.
If the logic is within the controller itself then this should be refactored into a service or repository depending on you solution architecture so that you can do the above.