I have implemented a custom UserStoreManager and I add same custom field into ApplicationUser.
It's work fine.
Now, I have a question. How I can get the ApplicationUser custom field value from a Controller?
I find the UserId information calling User.identity.getuserid or User.identity.getuserName but how I can access to the ApplicationUser Field?
Thank you
You just pull the user from the database. Something like:
var user = UserManager.FindById(User.Identity.GetUserId());
You need to get ApplicationUser first for the current user.
var currentUser = UserManager.FindById(HttpContext.User.Identity.GetUserId());
var customField = currentUser.CustomField;
private AppUserManager UserManager
{
get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); }
}
Related
I using asp.net boilerplate.
I have auth with Bearer Token (JWT).
In the profile method, I need to get userId.
Here is the code of the method
[AbpAuthorize]
[HttpGet]
public async Task<IActionResult> GetProfileData()
{
var identity = (ClaimsIdentity)User.Identity;
}
Now if I using identity. I can get the only a name. How I can get userId from it?
I found the answer
just need to rewrite it like this
var identity = User.Identity.GetUserId();
public GetClaims (IHttpContextAccessor accessor) // contractor method
{
var currentUser = accessor.HttpContext.User;
var userId = currentUser.FindFirst("Id")?.Value;
}
Make a class for Claims, inject IHttpContextAccessor, and take whatever you want. of course you can inject IHttpContextAccessor on controller but, I do not suggest it.
In a Asp.net Core project, I´m using the code below to change user name and email. But the property "User" in the controller does not update, just after logoff.
obs.: This "User" property is already defined by the framework in controller bases class, and it is a "ClaimsPrincipal" type.
Controller
public async Task<IActionResult> Create(EditViewModel viewModel)
{
if (ModelState.IsValid)
{
var model = await _userManager.FindByIdAsync(CurrentUser.Id);
model.UserName = viewModel.UserName;
model.Email = viewModel.Email;
await _userManager.UpdateAsync(model);
//THIS ONE STILL HAS OLD VALUES ==============================
Console.WriteLine(_userManager.GetUserName(User));
//THIS ONE HAS NEWER VALUES ==================================
model = await _userManager.FindByIdAsync(CurrentUser.Id);
Console.WriteLine(model.UserName);
...
I´m using this "User" property in my views, to display user informations, like this:
View
#inject UserManager<ApplicationUser> UserManager
...
<p>Hello: #UserManager.GetUserName(User)</p>
This is likely because your current User is going to be stored in a fashion similar to a Claim (i.e. in memory) or Cookie to avoid frequent hits to the database and thus in order to pull the new values, you would need to log out and log back in to refresh these.
However, you could try following your code by calling the RefreshSignInAsync() method, which should handle this same behavior :
// Update your User
await _userManager.UpdateAsync(model);
// signInManager is an instance of SignInManager<ApplicationUser> and can be passed in via
// dependency injection
await signInManager.RefreshSignInAsync(existingUser);
Background:
I'm building more and more web applications where the designers / template makers decide that adding a "profile picture" and some other user-related data, of course only when someone is logged in.
As most ASP.NET MVC developers I use viewmodels to provide razor layouts with the information that I need shown, sourced from repositories et al.
It is easy to show a user name through using
HttpContext.Current.User.Identity.Name
What if I want to show information that's saved in my backing datastore on these pages? Custom fields in the ApplicationUser class like a business unit name or a profile picture CDN url.
(for sake of simplicity let's assume I use the Identity Framework with a Entity Framework (SQL database) containing my ApplicationUsers)
Question
How do you solve this:
Without poluting the viewmodel/controller tree (e.g. building a BaseViewModel or BaseController populating / providing this information?
Without having to roundtrip the database every page request for these details?
Without querying the database if a user is not logged in?
When you cannot use SESSION data (as my applications are often scaled on multiple Azure instances - read why this isn't possible here- I'm not interested in SQL caching or Redis caching.
I've thought about using partials that new their own viewmodel - but that would still roundtrip the SQL database every pageload. Session data would be safe for now, but when scaled up in azure this isn't a way either. Any idea what would be my best bet?
TLDR;
I want to show user profile information (ApplicationUser) on every page of my application if users are logged in (anon access = allowed). How do I show this info without querying the database every page request? How do I do this without the Session class? How do I do this without building base classes?
The best way with Identity is to use claims to store custom data about the user. Sam's answer pretty close to what I'm saying here. I'll elaborate a bit more.
On ApplicationUser class you have GenerateUserIdentityAsync method which used to create ClaimsIdentity of the user:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> 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
userIdentity.AddClaims(new[]
{
new Claim("MyApp:FirstName",this.FirstName), //presuming FirstName is part of ApplicationUser class
new Claim("MyApp:LastName",this.LastName),
});
return userIdentity;
}
This adds key-value pairs on the user identity that is eventually serialised and encrypted in the authentication cookie - this is important to remember.
After user is logged in, this Identity are available to you through HttpContext.Current.User.Identity - that object is actually ClaimsIdentity with claims taken from the cookie. So whatever you have put into claims on login time are there for you, without having to dip into your database.
To get the data out of claims I usually do extension methods on IPrincipal
public static String GetFirstName(this IPrincipal principal)
{
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal == null)
{
throw new DomainException("User is not authenticated");
}
var personNameClaim = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == "MyApp:FirstName");
if (personNameClaim != null)
{
return personNameClaim.Value;
}
return String.Empty;
}
This way you can access your claims data from your Razor views: User.GetFirstName()
And this operation is really fast because it does not require any object resolutions from your DI container and does not query your database.
The only snag is when the values in the storage actually updated, values in claims in the auth cookie are not refreshed until user signs-out and signs-in. But you can force that yourself via IAuehtenticationManager.Signout() and immediately sign them back in with the updated claims values.
You could store your extra information as claims. In your log in method fill your data to generated identity. For example if you are using Identity's default configuration you could add your claims in ApplicationUser.GenerateUserIdentityAsync() method:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> 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
userIdentity.AddClaims(new[]
{
new Claim("MyValueName1","value1"),
new Claim("MyValueName2","value2"),
new Claim("MyValueName2","value3"),
// and so on
});
return userIdentity;
}
And in your entire application you have access those information by reading current user claims. Actually HttpContext.Current.User.Identity.Name uses same approach.
public ActionResult MyAction()
{
// you have access the authenticated user's claims
// simply by casting User.Identity to ClaimsIdentity
var claims = ((ClaimsIdentity)User.Identity).Claims;
// or
var claims2 = ((ClaimsIdentity)HttpContext.Current.User.Identity).Claims;
}
I think the "how to" is a little subjective as there are probably many possible ways to go about this but I solved this exact problem by using the same pattern as HttpContext. I created a class called ApplicationContext with a static instance property that returns an instance using DI. (You could alter the property to generate a singleton itself as well if you aren't, for some reason, using DI.)
public interface IApplicationContext
{
//Interface
string GetUsername();
}
public class ApplicationContext : IApplicationContext
{
public static IApplicationContext Current
{
get
{
return DependencyResolver.Current.GetService<IApplicationContext>();
}
}
//appropriate functions to get required data
public string GetUsername() {
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
return HttpContext.Current.User.Identity.Name;
}
return null;
}
}
Then you just reference the "Current" property in your view directly.
#ApplicationContext.Current.GetUsername()
This would solve all of you requirements except #2. The database call may not add a significant enough overhead to warrant avoiding altogether but if you require it then your only option would be to implement some form of caching of the user data once it is queried the first time.
Simply implement ChildAction with caching and vary by loggedin user
I have created a class to handle membership user creation with custom fields.
I have done it based on this solutions:
How to assign Profile values?
Using ASP .NET Membership and Profile with MVC, how can I create a user and set it to HttpContext.Current.User?
namespace CCL
{
public static MemberProfile CurrentUser
{
get
{
if (Membership.GetUser() != null)
return ProfileBase.Create(Membership.GetUser().UserName) as MemberProfile;
else
return null;
}
}
}
And now I'm trying to use create the user and get the profile data:
if (Membership.GetUserNameByEmail(email) == null)
{
MembershipUser member = Membership.CreateUser(username, password, email);
Roles.AddUserToRole(username, "WebsiteUsers");
CCL.MemberProfile currentProfile = CCL.MemberProfile.CurrentUser;
bool exists = currentProfile != null;
Response.Write(exists.ToString());
}
but currentProfile is returning null.
So I'm unable to assign values from the form to my member custom properties which are handled by the properties set in the class :(
I don't get how I can make it working :(
Does anyone have some thoughts? Thanks
Suggestion 1:
Make sure that ProfileBase.Create returns something that can be cast to a "MemberProfile", otherwise if it can't then casting it will just return NULL.
Suggestion 2:
Make sure the context you are running in has a logged in user, so your call to Membership.GetUser() can find the current user object.
Other thoughts:
The ProfileBase.Create method assumes that the username you pass in is an authenticated user, I'm not sure on it's behavior when the user isn't authenticated..maybe it returns NULL?
I'm using the membership provider asp.net mvc 4.
I'd like to get a list of users and their roles without Roles.GetRolesForUser() for each user.
In my application, the business requirements state that a user will only ever be assigned one role.
What I'm currently doing:
[GridAction]
public ActionResult _GetUsers()
{
var users = Membership.GetAllUsers().Cast<MembershipUser>().Select(n => new AdminAccountEditModel
{
Role = Roles.GetRolesForUser(n.UserName).FirstOrDefault(),
IsApproved = n.IsApproved,
Email = n.Email,
UserName = n.UserName
}).ToList();
return View(new GridModel(users));
}
Very inefficient. How do I fix this?
Thanks.
In the past I've cheated somewhat when using the standard membership provider and have written a lot of complex queries directly against the tables in sql. What you're looking for is a simple join.
I just ended up using EF and linq to get the result.
[GridAction]
public ActionResult _GetUsers()
{
var users = from user in xcsnEntities.Users
select new
{
Role = user.Roles.FirstOrDefault().RoleName,
IsApproved = user.Membership.IsApproved,
Email = user.Membership.Email,
UserName = user.UserName
};
return View(new GridModel(users));
}