Get Current Principal as my Custom Application User in ASP.Net Core Identity - asp.net

In previous versions of ASP.NET, if I wanted to have a custom class as my current logged in user, what I did was: I let the FormsAuthentication module do its work, and then, in the PostAuthenticateRequest event I replaced the current Principal (HttpContext.Current.User) with my custom principal object that I fetched from the database (with some caching for performance).
How can I achieve the same in ASP.NET Identity? I have my own ApplicationUser (not the default that comes with the EntityFramework ASP.NET Identity) and my own UserStore.
In every authenticated request, I have the HttpContext.User as a ClaimsPrincipal object. Is there a way to replace that with my CustomClaimsPrincipal?
Is there another, better way, to retrieve the current ApplicationUser instance based on the current ClaimsPrincipal?

If you have your own IUserStore you can implement IUserClaimStore to customize the claims identity which is passed to the claims principal.
If you need to replace the default claims principal you should implement the IUserClaimsPrincipalFactory and pass your implementation to the SignInManager and register the configured manager to your owin context.
It should look like this along the lines.
(Assuming you are using ASP.NET Core Identity, for Identity v2 the interfaces and constructors may differ!)
class CustomClaimsFactory<TUser> : Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser>
where TUser : class
{
public Task<ClaimsPrincipal> CreateAsync(TUser user)
{
// create and return your custom principal
}
}
class OwinStartup
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(CreateSignInManager);
}
Microsoft.AspNetCore.Identity.SignInManager CreateSignInManager()
{
UserManager manager; // the manager that uses your custom IUserStore
IHttpContextAccessor accessor; // I don't know where to get it from...
var factory = new CustomClaimsFactory();
return new SignInManager(manager, accessor, factory, null, null, null);
}
}
For ASP.Net Core the OWIN-like startup configuration is done via dependency injection.

Related

Need advice of where to put custom user authorization in ASP.NET Core

I need advice of where to put custom user authorization code in ASP.NET Core. I am somewhat a ASP.NET Framework developer and normally I will add code to Global.asax as a session_onstart event to look up a SQL table where users profile are stored that is used to determine what they can view in the rest of the application. With Global.asax this is only cause once per user session, so what I would like to do is the same kind of approach in ASP.NET Core which I am kind of new to but need advice where that check should be done
I would like to do is the same kind of approach in ASP.NET Core which
I am kind of new to but need advice where that check should be done
Well, based on your description, in asp.net core you can achieve that in many ways. For instances, you could set in following places:
program.cs/startup.cs files
Using Middleware file
Using Action Filter
Let's, consider below example using action filter
Role and permissison:
First we are defining the role and the permission.
public enum Role
{
User,
Admin,
SuperAdmin
}
public enum Permission
{
Read,
Create,
Update,
Delete
}
Authorization On Action Filter:
public class AuthorizeActionFilter : IAuthorizationFilter
{
private readonly Role _role;
private readonly Permission _permission;
public AuthorizeActionFilter(Role item, Permission action)
{
_role = item;
_permission = action;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var isAuthorized = context.HttpContext.User.Claims.Any(c => c.Type == _role.ToString() && c.Value == _permission.ToString());
if (!isAuthorized)
{
context.Result = new ForbidResult();
}
}
}
Note: Check your user claim from the HttpContext if that containts either Admin or Read authorization.
Controller:
[Authorize(Role.User, Permission.Read)]
public IActionResult MemberList()
{
var memberList = _context.Members.ToList();
return View(memberList);
}
Output:
You even can implement that using Middleware. Asp.net 6 now providing couple of other mechanism now a days, you could have a look below official implementations as well.
Role-based authorization
Claims-based authorization
Policy-based authorization
Custom Action Filter

Is it possible to use HttpModule to authenticate for SignalR

I am developing an application that uses an HttpModule to perform custom authentication and authorization. My problem is that the user Identity set in the HttpModule is not accessible in the SignalR context objects.
I do the following in my HttpModule BeginRequest handler after custom authentication logic:
var userClaims = new List<Claim>();
userClaims.Add(new Claim(ClaimTypes.NameIdentifier, <some id>));
userClaims.Add(new Claim(ClaimTypes.Name, <some name>));
userClaims.Add(new Claim(ClaimTypes.Email, <da email>));
userClaims.Add(new Claim(ClaimTypes.Authentication, "true"));
var id = new ClaimsIdentity(userClaims);
var principal = new ClaimsPrincipal(new[] { id });
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
I thought that this would absolutely make everything hereafter behave as though the request was authenticated, however this is not the case.
I have created a SignalR AuthorizeAttribute class to handle the authentication that looks like this:
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class CustomAuthAttribute : AuthorizeAttribute
{
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
if (HttpContext.Current.Request.Path.StartsWith("/signalr/connect"))
{
var test = (ClaimsPrincipal)HttpContext.Current.User;
var test2 = (ClaimsPrincipal)Thread.Current.Principal;
}
return true;
}
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubContext, bool appliesToMethod)
{
var test = (ClaimsPrincipal)hubContext.Hub.Context.User;
return true;
}
}
So my plan was to access the hubContext.Hub.Context.User var from within the AuthorizeHubMethodInvocation method to do any custom authorization I needed. However this just contains the default WindowsPrincipal.
If I look into the AuthorizeHubConnection call (which is actually a regular HTTP request and not a websocket call), I see that the HttpContext.Current object also does not have the User set as it should.
I do see that I can access the HttpContext.Current.Items collection. I presume I could use this to toss the Principal from the module to the SignalR context, but I'm not sure that is what I'm supposed to do.
Is it best to simply rewrite the HttpModule as OWIN middleware? It looks like I'll have to change stuff anyways when/if we update to ASP.NET 5; there's nothing like MS products to give you job security.
I forgot I posted this question a while ago. I ended up explaining my solution in a comment on the MS article Authentication and Authorization for SignalR Hubs. After trying to implement OWIN middleware for auth I found I would have to do some goofy config to run all modules for all requests, which is inefficient. I couldn't figure out how to run just the Auth OWIN middleware component for all requests so I abandoned that approach and stuck with my HttpModule. Here is a summary of my solution for SignalR auth posted on the page linked above:
1) Create a AuthorizeAttribute class like indicated in the article:
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class CustomAuthAttribute : AuthorizeAttribute
2) Decorate your Hub class with the auth class you created. The naming convention appears to be (SomeName)Attribute for the auth class itself and (SomeName) for the hub decoration.
[CustomAuth]
public class ServerWebSocket : Hub
3) Instead of overriding the "UserAuthorized" method as shown in the docs, override the following methods (I got this from some other SO post, but I can't find it right now):
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubContext, bool appliesToMethod)
In order to actually authorize users I catch SignalR connection requests in my HttpModule and set an item in the HttpContext Items collection like so:
if (req.Path.StartsWith("/signalr/connect") || req.Path.StartsWith("/signalr/reconnect"))
{
var user_info = doFullAuth(<some token>);
HttpContext.Current.Items.Add("userDat", user_info);
}
This is actually set up so that connect requests will be completely rejected in the HttpModule if the user doesn't have permission. So I actually don't implement the SignalR auth method "AuthorizeHubConnection" at all. But in the "AuthorizeHubMethodInvocation" method I access the user data by calling HttpContext.Current.Items that was set on the original connect request and do custom logic to determine if a method can be accessed by the user.
This is the best way I can figure to get it to work if you want to authenticate every request to protect static files and such.

ASP.Net Identity 2 Using a custom data access layer like Enterprise Library

I am new to asp.net identity (ver. 2) and am about to start implementing it one of our MVC projects using the Claims based authorization mechanism instead of role based. I having been going through this link where what I understand from what is written is that I need to inherit Microsoft.AspNet.Identity.UserManager and create a CustomUserManager class and override its methods and then implement Microsoft.AspNet.Identity.EntityFramework.IUserStore to be consumed by my CustomUserManager class at the very least. There are other interfaces that I think were designed to be implemented for certain specific conditions like in my case the IUserClaimStore since I want to go with Claims based authorization. The reason mentioned is that I can change the store at a later date incase I want to change my persistence mechanism.
My questions are:
Since I am never going to change the persistence mechanism, is it really required that I implement all those classes and interfaces?
Going through the sample code the most important methods seem to be the following two code blocks:
Identity Creation and save to session (code in DoLogin Method)
// over simplified user object creation
UserPoco userObject= MyDAL.GetUserDatabyLoginDetails(username,password);
//identity created
var identity = CustomImplementationOfCreateIdentity(userObject, DefaultAuthenticationTypes.ApplicationCookie);
//saved to session
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
CheckAccess
public class AppClaimsAuthManager: ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
var resource = context.Resource.First().Value;
var action = context.Action.First().Value;
//bool retVal = context.Principal.HasClaim("MyAction", "SampleResource");
bool retVal = context.Principal.HasClaim(action, resource);
bool baseRetVal= base.CheckAccess(context);
return retVal;
}
}
which is then used in controller methods like so
[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "delete", Resource = "SomeResource")]
public ActionResult ClaimsBasedActionMethod()
{
return View();
}
Does it really matter how my user object is created via the CustomManager and CustomUserstore class implementations? Once the user name and password is verified and claims fetched from DB and my userObject created, I should be good to go right? I want this data to be fetched my service layer using enterprise library which I don't want to clog up with all identity framework related references.
Thoughts?

MVC Custom Membership and Role Provider context lifetime issue

I'm having problems with custom membership within MVC 4 I keep getting a context lifetime related error when I do a ajax call to get a partial result from the server(controller), the error is always {"The provider has been closed"} or {"There is already an open DataReader associated with this Command which must be closed first."} the error always lands within the custom RoleProvider.
I will try to explain the current setup im using.
I have inherited from the Membership and RoleProvier and overridden all the methods like so
public class CustomRoleProvider : RoleProvider
{
private IAccountService _accountService;
public CustomRoleProvider()
{
_accountService = new AccountService();
}
public override string[] GetRolesForUser(string username)
{
return _accountService.GetRolesForUser(username);
}
}
The Membership provider is implemented in the same way the IAccountService above is the service layer that deals with all user accounts & roles all the service layer classes implement a base service class called ServiceBase that creates the DB context
public class ServiceBase
{
protected Context Context;
protected ServiceBase() : this("Context") {}
protected ServiceBase(string dbName)
{
IDatabaseInitializer<Context> initializer = new DbInitialiser();
Database.SetInitializer(initializer);
Context = new Context(dbName);
}
}
The Controller that has the ajax to made to it
[Authorize(Roles = "Administrator,Supplier")]
public class AuctionController : Controller
{
private IAuctionService _service;
public AuctionController()
{
_service = new AuctionService();
}
public AuctionController(IAuctionService service)
{
_service = service;
}
[CacheControl(HttpCacheability.NoCache), HttpGet]
public ActionResult RefreshAuctionTimes(int auctionId)
{
return PartialView("_AuctionTimer", BusinessLogic.Map.ConvertAuction(_service.GetAuction (auctionId)));
}
}
The problem only started when I added the [Authorize(Roles = "Administrator,Supplier")] attribute to the controller that handled the ajax call, I know this is the lifetime of the DbContext being for the life of the app and the controllers service layer being destroyed and recreated on every post but I'm not sure of the best way to handle this, I have used this setup before but with DI and Windsor and never had this problem as the IOC was controlling the context.
Would it be best to create the providers its own DB context or is the conflict between the 2 providers and really they need to share the same db context?
Any help would be great thanks
The problem is exactly what you're suspecting. Is due to the fact that you're creating a single instance of the DbContext and therefore you're having connection issues. If you use it with an IOC/DI schema, you're going to fix it. The other option is to manually handle the connections.
An example of how to do this using Ninject as IOC container is here
They need to share the same context in order for the problem to stop.
I would suggest you create your service layer class on each call to GetRolesForUser:
public override string[] GetRolesForUser(string username)
{
return new AccountService().GetRolesForUser(username);
}

How to obtain a list of Users from ASP.NET Identity?

Edit: This question is outdated
The Identity Framework was a moving target at the moment I asked this. The authors changed quite a few things and they have decoupled several others, making everything easier.
Have a look at the Asp.NET Identity Sample project on github.
I'm creating a small application that requires user management. Registration is not allowed, instead there is a super user that will create and modify login information.
I'm using the new ASP.NET Identity membership system, and sure enough, creating users and adding roles is easy and intuitive.
Now, my question: How to obtain a list of users using the AuthenticationIdentityManager class that is used by the generated AccountController class? I couldn't find a way to access the user list from my controller.
(By the way, the new name "Identity" may sound awesome to some people but it is a pain to search for.)
Edit: If I try to do this
ApplicationDbContext UsersContext = new ApplicationDbContext();
UsersContext.Users.ToList(); // Exception
I get an exception Invalid column name 'Discriminator'. The definition of ApplicationDbContext is generated automatically by the new application wizard:
using Microsoft.AspNet.Identity.EntityFramework;
namespace Cobranzas.Models
{
public class ApplicationUser : User
{
}
public class ApplicationDbContext : IdentityDbContextWithCustomUser<ApplicationUser>
{
}
}
So my guess is that Discriminator column is for telling apart ApplicationUser from User. However, it does not exists in my database (which was created automatically by the application.)
I found out that I wasn't using the derived ApplicationUser object for anything, so I just went ahead and changed all uses of it for plain old User. Then I just changed ApplicationDbContext definition for the following:
public class ApplicationDbContext : IdentityDbContext<
User, UserClaim, UserSecret, UserLogin,
Role, UserRole, Token, UserManagement>
{
}
And now I can access the user list:
UsersContext = new ApplicationDbContext();
...
UsersContext.Users.ToList();
However, I think this will come back and haunt me in the future (I'll probably need to add more fields to User) so probably I'll have to use the same approach as in this question:
Get all role names in ASP.NET MVC5 Identity system
Edit: Since I got the need to add a new property, I had to revert my changes. So I went ahead and did a line by line comparison with the ASP.NET Identity Sample Project, and found out that the generated project had the following line:
IdentityManager = new AuthenticationIdentityManager(new IdentityStore());
while the Sample application had included the database context in the constructor. So I added it in my constructor, recreated the database and the problem went away.
IdentityManager = new AuthenticationIdentityManager(new IdentityStore(new ApplicationDbContext()));
Create ASP .NET MVC5 project by default
Create ASP .NET Identity tables properly and change connection string as well.
To get users just do the following test
A. Go to AccountController
B. Create any dummy method and put there
var context = new ApplicationDbContext();
var allUsers = context.Users.ToList();
For RTM, you will have to drop down to your DbContext or whatever your specific store implementation has to enumerate all users. In the next release, we will most likely be adding an optional IQueryable Users/Roles method on the Manager classes that stores can implement to expose IQueryables for both users and stores.
using System.Linq;
using System.Data;
using System.Data.Entity;
var db = new ApplicationDbContext();
var Users = db.Users.Include(u => u.Roles);
If we can use the following type of Constructor in Identity AccountController.
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
then we can directly used UserManager object to get user list like
var userList= UserManager.Users.ToList();
You can do this by explicitly setting right types:
var userStore = new UserStore<IdentityUser>();
var userManager = new UserManager<IdentityUser>(userStore);
IQueryable<IdentityUser> usersQuery = userManager.Users;
List<IdentityUser> users = usersQuery.ToList();
Imoports:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Linq;
using System.Collections.Generic;

Resources