ASP Net MVC Core - Load User Data From Active Directory when User browses Any Page - asp.net-core

Here is my development environment:
Intranet Website
Active Directory Authentication/Authorization
Asp Net Core
I am trying to get the data stored in Active Directory attributes when a user enters firstly to any page in our application. All users rights and permissions, employeeid, studentid, etc.... are stored in AD Attributes and Security Groups. Some Attributes need to be displayed on the website too.
Let's say my website got the following urls...
http://mysite/Home/Index
http://mysite/Student/Index
http://mysite/Student/MyJobs
http://mysite/Staff/Applications
etc....
Any users can go onto some areas/urls of the website freely from other Intranet portals and I don't know where should I write the code to fulfill that criteria. The problem is that, there is no specific entry point to the application like http://mysite/Login or Authenticate, etc. If there is, I could load all users details and rights from AD on that single entry point.
In MVC5 era, I used Custom Global Authorize Attribute and put it on the BaseController is inherited from all other controllers to load that AD data. I put the AD's data into Session on the first hit and use the Static Class to display on Views and use in Controllers. But when I did some research in MVC Core, some say that it's outdated and I should use the Authorize Policy instead of custom Authorize Attributes.
Getting the data from Active Directory is already achieved by using my old webservices and we don't need to worry about .Net core not supporting AD yet.
I looked at the tutorials about Policy and saw something about Claims and Custom User Managers. I couldn't decide which one I should use to load data from Active Directory to the object (probably Scoped Object DI) which lasts for the whole user's session.
Should I load the data onto claims attributes
Eg...
var claims = new List<Claim>();
claims.Add(new Claim("UserName", "John.Smith", ClaimValueTypes.String, Issuer));
claims.Add(new Claim("RefNo", "02343001", ClaimValueTypes.String, Issuer));
claims.Add(new Claim("Email", "MyEmail#email.com", ClaimValueTypes.String, Issuer));
Or Should I write customized SignInManager and IdentityUser?
Eg...
public class ApplicationUser : IdentityUser
{
public string RefNo { get; set; }
public string Email { get; set; }
}
Is there anywhere I could put my code to check AD and load data?
And should I store the data in that Claimed Object rather than using Session Data?
Could you guys please advise me? Feel free to criticize if I miss anything and my idea is not working.

You're right in saying there's no System.DirectoryServices yet (it's on the backlog, I promise) so there are a couple of places to do this.
If you're already using Integrated Authentication you have SIDs for group membership, which are resolved when you call IsInRole(), so you can use role based membership (rather than Claims based) to solve basic authentication problems.
However if you want to support a forms based mechanism then you should look at using the cookie middleware, raw, to at least give you a simple login, calling your web service to validate your login. You could query your API in the controller code, and write an identity cookie. This cookie automatically encrypted and signed, so it can't be tampered with.
The problem comes when you want roles, and attributes. If you head down the cookie route you might be tempted to put all of those as claims in the identity before writing the identity out as a cookie. This might work, provided there are not too many - cookies have a maximum size (browser dependent, but under 4k usually). You can used chunked cookies, but there's a performance impact here. Instead you might use a reference cookie, where you put in a reference to another store where the actual fully populated identity is stored, be it session, redis or something else.
Then in the claims transformation middleware you can pull the reference out, go to your store, and rehydrate the identity.
I'd honestly avoid trying to merge all of this into ASP.NET Identity. That's mean to be the sole source for user information in an application, and in your case that's not true. Your sole source should be AD.
There's also a port of Novell's ldap library to core, which should stand in nicely for DirectoryServices should you want to avoid your web services approach.

Related

How to use IdentityServer4 to authenticate local user from local database?

I'm creating SSO solution for multiple existing projects. Most of the applications already use same database, so same users and I use these users in IdentityServer. But there is one app which has its own users DB and login screen. Users from this table are used in one of the tables in this app (FK).
My idea is to leave existing users DB as is. Add a column MasterUserGuid to Users table that will contain "master" user Guid (so the user that IdentityServer uses for authentication) and implement following flow:
User opens the app and is not signed in
User is redirected to IdentityServer and uses global credentials
User is redirected back to the app which gets global user GUID from claims and authenticates local user (with this GUID in the MasterUserGuid column), instead of using global user
The problem is that I don't know how to implement step 3 or if it's even possible/supported in IdentityServer4. At the moment I'm redirected to IdentityServer, am authenticated and redirected back, but then the app tries to use this external user.
While researching I've read that users should be in one table, so maybe this approach is totally wrong and it would be better to remove local users and break FK for mentioned table and do some manual migration of users.
Is the scenario described in the steps I provided possible and sane?
You need to adjust your application to authenticate via IdentityServer first. Remove all the ASP.NET Core Identity logic related to registration, login etc. assuming all of that will be done on IdentityServer side. Then implement the instance of IClaimsTransformation which will either replace your current ClaimsPrincipalor add additional identities to it if needed with the claim values you want (populated from local database). Here is the example:
public class MyClaimsTransformer : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var claims = new List<Claim>();
claims.Add(new Claim(...)); // put here your claim type and value
var identity = new ClaimsIdentity(claims);
principal.AddIdentity(identity);
return principal;
}
}
Then register your claims transformer in IOC in Startup.ConfigureServices method:
services.AddTransient<IClaimsTransformation, MyClaimsTransformer>();

IdentityServer4 + Asp.Net Core Identity - Map Identity to application database user

I am trying to implement an IdentityServer4 with Asp.Net Core Identity.
I want to use IdentityServer4 as centralized authentication/authorization point for APIs using always the same identity.
So the idea is to store the Asp.Net Core Identity stuff in an SQL Database which serves as the identity store.
The question now is how to map the centralized identity to application specific data.
I want to use same identity user in several applications, but in each application the user has other related entities, roles etc.
I read through the documentation of IdentityServer4 but could not find anything related to a proposed structure.
As I understood, you somehow map the identity id to your local application user.
Basic data like firstname etc are stored in the centralized identity store and application specific data is stored in the application specific database. So you would not save firstname etc in the application specific db right?
In the every request where you need user specific data will query the identity server to get information/claims?
What about registration process?
Does anybody have a clear structure setup which could be used to understand the whole setup?
(Separation like Asp.Net Identity Provider, IdentityServer4, Protected Api.)
So you would not save firstname etc in the application specific db
right?
Yes, User specific properties should go into the user profile and should be saved in the user store(database) of IdentityServer. Application specific user data should be stored in the application store.
In the every request where you need user specific data will query the
identity server to get information/claims?
Not necessarily, The user specific data can be included in identity token as claims. The claims will then be stored in a cookie as authentication ticket. For each request, those claims(stored in cookie/s) are available via User property of the controller
var identity = (ClaimsIdentity)User.Identity;
IEnumerable<Claim> claims = identity.Claims;
You can store and query application related user data stored against a user Id(sub claim can be used as a user Id).
If you need a lot of user-specific data in an application it is not optimal to include everything in identity-token and it is unlikely that you will need those for each request. So when you need extra information you can query UserInfo endpoint of identity server. Just include basic information you need to identify a user in your identity token.
What about the registration process?
Registration is a completely separate workflow that there is no involvement with identity server. You just need to save the user to identity store(probably using asp.net identity). Of course, you can host the registration controllers along with identity server, so that identity-related stuff is physically on the same server. You can also just write to IdentityServer user store from anyplace you host your registration process(e.g. A separate admin UI, or From a workflow involving email verification, manual approval etc...)
To customize what you store in Asp.net Core Identity, you need to use
services.AddIdentity<ApplicationUser, ApplicationRole>. ApplicationUser and ApplicationRole are extending IdentityUser and IdentityRole. This way you can make it store any extra info you want.
Then to return the extra info you need to create a ProfileService that implements IProfileService. With this service, you can add any extra information to claim tokens.
You need to register this service as
services.AddSingleton<IProfileService, ProfileService>();
builder.AddAspNetIdentity<ApplicationUser>().AddProfileService<ProfileService>();
You can register the user with extra info like below:
var user = new ApplicationUser
{
UserName = Username,
Email = email,
ExtraInfo1 = "Hello",
ExtraInfo2 = "World"
};
await _userManager.CreateAsync(user, "SomePassword");
With OpenId you have default set of claims associated with user. So any client application can access those claims. Make sure each client has openid and profile scopes assigned to them. Otherwise client application not able to access the users basic details.
In Asp.Net Core application you can access those claims in controller using User property.

Moving to use Claims Identity in .Net Core

In our current system (.net 4.5) to handle user authentication throughout the app we have created our own IIdentity and IPrincipal objects. So on every request we decrypt an attribute in the cookie (stored at login) and using this we check the cache for the user object (which also provides us with the client object). If the user is in the cache, great we then set the HttpContextBase.User and the Thread.CurrentPrincipal to be our own version of IPrincipal. If the user isn't in the cache then we get them from the db and set the above.
This works really well as we then have a BaseController with 2 properties, one for the current user and one for the current client. These are accessed by casting User.Identity to our own IIdentity.
We are now looking to move our app over to .netcore but we are not sure how to achieve the same as above given that we don't seem to be able to replicate it. We are currently using cookie middleware with ASP Identity (Shown Here), we are setting some claims (id, name etc) but we aren't sure how to or where we should be checking the cache and setting some properties on every request.

From where knows [authorize] the roles / users (ASP.NET MVC 5)

I'm a newbie to asp.net mvc, so I created a simple internet application from the template. I added some user and some roles and connected them (in database). Then I added [authorize(Roles = "MyRole")] and everything works fine. Can anyone tell me from where authorize takes the information about users and roles and so on? Where is the magic that wired that up? (As I said: simple application from template mvc 5 "internet application")
There's not really any magic here. Once you've authenticated, a principal is registered and filled with some of the basic information for the user, including any roles they're associated with. This information ultimately comes from your database of course, but how the authorization layer retrieves that and implements the principal from it is low-level and dependent ultimately on the authentication provider being used (Membership, Identity, Windows Auth, etc.).
Regardless, the Authorize attribute merely looks at the roles on the principal and if there's a match, allows the action to proceed. Otherwise, it does a redirect, usually to the sign in page of the application, or returns a 401 Not Authorized, depending on whether the user is authenticated or anonymous.

Where to keep data about an authenticated user?

I am still pretty new to ASP.NET Web API. I am currently working on the authentication part of a new application based on Web API, which is developed using some libraries/kinda framework of the company.
There is already some MVC application - they are using forms based authentication and they are not using the IPrincipal to store information about the user, rather a unity based approach, keeping data in a custom IUser object (basically kept on the session).
The Web API application is going to be stateless (no session), just that I am going to add some user related information in the authentication cookie (retrieved per request in the Application_PostAuthenticateRequest).
I am a bit undecided to keep this user related data in a custom implementation of IPrincipal (as I noticed to be a practice) or use the current approach of other applications in the company utilizing an IUser - served by Unity, using a per request lifetime manager.
Which do you consider to be the better approach?
If you're keeping track of Users per session, try using Singleton classes, if you're about to make a log of the users that entered the session, write it down in a textfile like a whitelist.