What is the fast way (best way) to get current user in ASP.NET Identity?
I wrote this code for getting current user but I don't know that is the best way or not?
public class MVCController : Controller
{
public User CurrentUser
{
get
{
string currentUserId = User.Identity.GetUserId();
User currentUser = DataContextFactory.GetDataContext().Users.FirstOrDefault(x => x.Id.ToString() == currentUserId);
return currentUser;
}
}
}
With Identity v2 the best way to get a user is to use the UserManager object.
var user = userManager.FindById(user.Identity.GetUserId());
Rather than a DB hit, we should add all required information in user claims during authentication and retrieve them from claims during next calls. It will be the fastest way to get commonly used user information and the same time we avoid any database calls. You can take a look on some claims helper methods Github link
Related
I am creating an application in that there is role-based module management and it is changed anytime by anytime.
Scenario:
If a user has access to create and view Employee than a user can do only create and view employees but in future admin change user's role from creating and view to view and delete than a user can only do that activity.
I try with [Authorize(Roles ="Staff")] but if admin change runtime than it isn't managed.
Can anyone look into this and get back to me?
This is a complicated question and there isn't a right answer but there are several ways to do it.
First I will assume you are using stateless auth using a claim based jwt the simplest way is writing your own Policy that will read user roles before every request, this is the simplest way to do it and fastest to implement.
internal class DatabaseRoles : IAuthorizationRequirement
{
public string Role { get; }
public DatabaseRoles(string role)
{
Role = role;
}
}
internal class DatabaseRolesHandler : AuthorizationHandler<DatabaseRoles>
{
private readonly UserManager<IdentityUser> userManager;
public DatabaseRolesHandler(UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager)
{
this.userManager = userManager;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, DatabaseRoles requirement)
{
//NOTE this is the out of the box implementation of roles and simple query to get the roles from the EF backed database. I would recoment makeing a custom privelages store for this and not using roles for this but access rights
var user = await userManager.FindByIdAsync(userManager.GetUserId(context.User));
if (await userManager.IsInRoleAsync(user, requirement.Role))
{
context.Succeed(requirement);
}
}
}
But this solution is not that performant because it requires a call to the database on every request. This is fine on small loads but might create problems in traffic.
The other way is to reevoke the all user tokens when the roles change but this is super complicated. I am sure if you create some fast access store for roles like redis there will be no issues to do the check on every call. Also I do not recommend creating your own user storage because it's a nightmare to maintain and keep up to date in regards to security standards.
If you are using Session/Cookie to store the logged in user details, you can empty the details whenever Admin make role changes. On every action you can check the role in Session/Cookie and move forward.
Now as soon as user clicks anywhere on the screen which hits the controller. The condition will be checked and the User will be logged out as the Session/Cookie object is empty.
I have a controller that returns data about users. I want to set the authorization such that an admin can access this controller and retrieve data for any user, and a non-admin user can access the controller and retrieve data for themselves.
I've ruled out using [Authorize (Roles = "Admin")] because this means users can't get their own data. So I've inserted the following logic into the controller action:
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.Name).Value;
var roles = _httpContextAccessor.HttpContext.User.FindAll(ClaimTypes.Role);
var query = roles.Select(r => r.Value).Contains("Admin");
Customer customer =await _context.Customers.FindAsync(id);
if (!(customer.EmailAddress == userId || query))
return Unauthorized();
This is roughly equivalent to this Stack Overflow answer, but for ASP.Net Core rather than MVC.
My question is, is there a way to do this with an Authorization Policy? Adding the RequireRole check is straightforward and covered in the MS Documentation as well as countless blogs, but I couldn't find or figure out a way to use a policy to check that the data the user is trying to access is their own.
I'm sure this isn't an uncommon requirement, is there a way to do this, or is what I'm currently doing OK? The only other approach I could think of was to have two separate endpoints, but both options seem inelegant.
The policy is for authorization , but either Admin or A normal user can access the controller , they are all authorized .
That is your custom logic to determine which data should be returned , that is nothing related to authorization . If you insist on using policy , you can put the logic to handler but that is nothing change when logic is in controller :
public class CustomerHandler : AuthorizationHandler<CustomerRequirement>
{
IHttpContextAccessor _httpContextAccessor = null;
public CustomerHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
CustomerRequirement requirement)
{
HttpContext httpContext = _httpContextAccessor.HttpContext;
//your logic
httpContext.Items["message"] = "ownData";
context.Succeed(requirement);
return Task.CompletedTask;
}
}
And read in controller so that you can know whether read his own data or all users' data :
var message = HttpContext.Items["message"];
In my option ,set two endpoints/function in your web api , one for admin , one for user is the clean way . In addition , that is your client app's responsibility to determine that current user wants to return his own data or all user's data . That seems not quite correct to send request to web api and let api to determine by logic . Webapi should include the clean functions/endpoint to map each request from client .
How do we get the current user, within an secure ApiController action, without passing the userName or userId as a parameter?
We assume that this is available, because we are within a secure action. Being in a secure action means that the user has already authenticated and the request has her bearer token. Given that WebApi has authorized the user, there may be a built in way to access the userId, without having to pass it as an action parameter.
In WebApi 2 you can use RequestContext.Principal from within a method on ApiController
You can also access the principal using the User property on ApiController.
So the following two statements are basically the same:
string id;
id = User.Identity.GetUserId();
id = RequestContext.Principal.Identity.GetUserId();
Hint lies in Webapi2 auto generated account controller
Have this property with getter defined as
public string UserIdentity
{
get
{
var user = UserManager.FindByName(User.Identity.Name);
return user;//user.Email
}
}
and in order to get UserManager - In WebApi2 -do as Romans (read as AccountController) do
public ApplicationUserManager UserManager
{
get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
}
This should be compatible in IIS and self host mode
None of the suggestions above worked for me. The following did!
HttpContext.Current.Request.LogonUserIdentity.Name
I guess there's a wide variety of scenarios and this one worked for me. My scenario involved an AngularJS frontend and a Web API 2 backend application, both running under IIS. I had to set both applications to run exclusively under Windows Authentication.
No need to pass any user information. The browser and IIS exchange the logged on user credentials and the Web API has access to the user credentials on demand (from IIS I presume).
Karan Bhandari's answer is good, but the AccountController added in a project is very likely a Mvc.Controller. To convert his answer for use in an ApiController change HttpContext.Current.GetOwinContext() to Request.GetOwinContext() and make sure you have added the following 2 using statements:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
In .Net Core use User.Identity.Name to get the Name claim of the user.
If you are using Asp.Identity UseManager, it automatically sets the value of
RequestContext.Principal.Identity.GetUserId()
based on IdentityUser you use in creating the IdentityDbContext.
If ever you are implementing a custom user table and owin token bearer authentication, kindly check on my answer.
How to get user context during Web Api calls?
Hope it still helps. :)
string userName;
string userId;
if (HttpContext.Current != null && HttpContext.Current.User != null
&& HttpContext.Current.User.Identity.Name != null)
{
userName = HttpContext.Current.User.Identity.Name;
userId = HttpContext.Current.User.Identity.GetUserId();
}
Or based on Darrel Miller's comment, maybe use this to retrieve the HttpContext first.
// get httpContext
object httpContext;
actionContext.Request.Properties.TryGetValue("MS_HttpContext", out httpContext);
See also:
How to access HTTPContext from within your Web API action
So I'm using SignalR, it's setup and working correctly on my Website.
Let's suppose user A logs in (I am using the Membership API). When A logs in I am calling the connection from .js located in my masterpage. That will assign this use a specific userId.
Let's say now user B logs in goes does some event and that event needs to notify user A from codebehind.
So what I am trying to do here is notify user B of use A's action from CodeBehind. How will user B know user A's ID and how does the whole thing work? I couldn't find help in the documentation as it does not go into that kind of stuff.
How can this be achieved? Thanks.
I realize this has already been answered, but there another option that folks might find helpful. I had trouble finding info on how to do this, so hopefully this helps someone else.
You can override the SignalR ClientID generation and make it use the membership UserID. This means you do not have to maintain a CleintID -> UserID mapping.
To do this, you create a class that implements the IClientIdFactory interface. You can do something like:
public class UserIdClientIdFactory : IClientIdFactory
{
public string CreateClientId(HttpContextBase context)
{
// get and return the UserId here, in my app it is stored
// in a custom IIdentity object, but you get the idea
MembershipUser user = Membership.GetUser();
return user != null ?
user.ProviderUserKey.ToString() :
Guid.NewGuid().ToString();
}
}
And then in your global.asax:
SignalR.Infrastructure.DependencyResolver.Register(typeof(IClientIdFactory), () => new UserIdClientIdFactory());
EDIT -- as nillls mentioned below, things have changed in the signalR version 0.4. Use ConnectionId rather than ClientId:
public class UserIdClientIdFactory : IConnectionIdFactory
{
public string CreateConnectionId(SignalR.Hosting.IRequest request)
{
// get and return the UserId here, in my app it is stored
// in a custom IIdentity object, but you get the idea
MembershipUser user = Membership.GetUser();
return user != null ?
user.ProviderUserKey.ToString() :
Guid.NewGuid().ToString();
}
}
And DependencyResolver has moved:
SignalR.Hosting.AspNet.AspNetHost.DependencyResolver.Register(typeof(IConnectionIdFactory), () => new UserIDClientIdFactory());
Hope this helps someone!
Your app needs to store a mapping of SignalR client (connection) IDs to user ids/names. That way, you can look up the current SignalR client ID for user B and then use it to send a message directly to them. Look at the chat sample app at https://github.com/davidfowl/JabbR for an example of this.
When I implement the RoleProvider class and call Roles.IsUserInRole(string username, string roleName), code execution first goes to the method 'GetRolesForUser(string username)'. Why is this? I don't want to iterate all roles when I am just looking for the single value of whether that user belongs in one role. Is this a limitation of .NET's role provider class or is there something I can do to control the execution of code a bit more?
Here's the calling code
if (Roles.IsUserInRole(CurrentUser.UserName, "Teacher")) {
And here's the implementation of IsUserInRole
public override bool IsUserInRole(string username, string roleName) { return true; }
But the code GetRolesForUser always gets implemented first:
public override string[] GetRolesForUser(string username) {
string[] roles = GetAllRoles();
List<string> userRoles = new List<string>();
foreach (string role in roles) {
if (IsUserInRole(username, role)) {
userRoles.Add(role);
}
}
return userRoles.ToArray();
}
The RoleProvider.IsUserInRole(username, password) is used for checking roles for a given user which is not the current loggon user(for current logon user, it also use the Principal.IsInRole instead). And for RolePrincipal, it always use the GetRolesForUser to cache the roles and do the role checking among the cached role list. (source)
can user Roles.Provider.IsUserInRole instead of Roles.IsUserInRole
There's a layer Microsoft's role provider solution that enables caching a user's roles in a cookie so it doesn't need to call the provider's GetRolesForUser method. I believe the cookie caching is part of the Roles class, so as long as you implement from the RoleProvider base class, it should be compatible. It's worth a look at the code in reflector to get an idea of how MS implements their own abstract classes, and what the static helper classes do (Roles and Membership)
Try adding cacheRolesInCookie="true" to the roleManager element in your config file, and see if the flow changes.
Since you're using your own implementation of a RoleProvider, you can also override the IsUserInRole method and provide your own implementation of checking if a user is in a role.
UPDATE: This block of code gets called inside the Roles.IsUserInRole method:
IPrincipal currentUser = GetCurrentUser();
if (((currentUser != null) && (currentUser is RolePrincipal)) && ((((RolePrincipal) currentUser).ProviderName == Provider.Name) && StringUtil.EqualsIgnoreCase(username, currentUser.Identity.Name)))
{
flag = currentUser.IsInRole(roleName);
}
else
{
flag = Provider.IsUserInRole(username, roleName);
}
The else block is what will call your custom provider's IsUserInRole method.
So the roles for your user have not yet been added to the Principal object. If you just haven't gotten around to that step yet, OK. If not, make sure you do that. It will make sure that every time you call Roles.IsUserInRole, or User.IsInRole that those functions will use an in-memory cache of the roles for the user (once loaded) instead of having to go to the database every time. (Although the base role provider and Roles manager class should take care of this for you.)
Can you verify the config file settings for the role provider? Also, what version of .net are you using? Are you manually managing the login process or are you using the .net login control? Have you implemented a custom Roles class? Or are you using the System.Web.Security.Roles?