Skip to main content

ASP.NET Core 2.1 Windows Authentication with DB Roles for Authorization Part 2

If you haven't read Part 1 of this post, here is a link: Part 1
I was really stuck on this issue, and I posted the previous section on my blog as well as a post on stack overflow, but in the end, I came up with a working solution. When using windows authentication, the ClaimIdentity had a RoleClaimType of groupsid which meant it could take a SID or String that was associated with an AD group and convert it back and forth, but since I was injecting roles from the database, there was no SID associated with them, and I needed the RoleClaimType to be role instead of groupsid. 
The ClaimIdentity had a RoleClaimType of "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid"
and it needed to be a RoleClaimType of "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
Since this is a read-only property, I changed the TransformAsync method to create a new ClaimsPrincipal instead of trying to add database roles to the existing claims. My application doesn't require any of the AD groups, so it only uses windows for Authentication. The code below seems to work.
public class MyClaimsTransformer : IClaimsTransformation
    {
        private readonly IUnitOfWorkSecurity _unitOfWork;

        public MyClaimsTransformer(IUnitOfWorkSecurity unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        // Each time HttpContext.AuthenticateAsync() or HttpContext.SignInAsync(...) is called the claims transformer is invoked. So this might be invoked multiple times. 
        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
            var identity = principal.Identities.FirstOrDefault(x => x.IsAuthenticated);
            if (identity == null) return principal;

            var user = identity.Name;
            if (user == null) return principal;

            //Get user with roles from repository.
            var dbUser = _unitOfWork.UserInformations.GetUserWithRoles(user);

            var claims = new List<Claim>();

            //The claim identity uses a claim with the claim type below to determine the name property.
            claims.Add(new Claim(@"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", user, "Name"));

            //todo: We should probably create a cache for this
            // Get User Roles from database and add to list of claims.
            foreach (var role in dbUser.UserInformationUserRoles.Select((r=>r.UserRole)))
            {
                claims.Add(new Claim(ClaimTypes.Role, role.Name));
            }

            var newClaimsIdentity = new ClaimsIdentity(claims,"Kerberos","", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
            
            var newClaimsPrincipal = new ClaimsPrincipal(newClaimsIdentity);

            return new ClaimsPrincipal(newClaimsPrincipal);
        }  
    }
I have tested this in both my views and controllers and it works. I will probably want to add a user cache of some sort because this code makes a database call with every authentication request. Other than that, it seems like a pretty clean solution. If any of my readers have a better suggestion, please let me know in the comments.

EDIT:
I have created a solution and posted it on my github. https://github.com/jkarnopp/AspNetCoreWindowsAuthExample

Comments

  1. Hi Jim, any chance you could share the Visual Studio Soulution with us?

    ReplyDelete
    Replies
    1. Hello, sorry I didn't see your comment until just now. I will have to create a new solution with this in their, but I can do that, and I will post it soon.

      Delete
  2. Hi Jim, many, many thanks for this post :)

    I've been looking but cannot seem to track down the IUnitOfWorkSecurity. Where does it live?

    cheers

    Matt

    ReplyDelete
  3. Hi Jim! Thank you so much for posting this! Did you get a chance to post the solution files? TIA! :)

    ReplyDelete
    Replies
    1. Hello May, thanks for reminding me. I meant to do that a while ago and forgot. Here is a link to my github with the solution: https://github.com/jkarnopp/AspNetCoreWindowsAuthExample

      Delete
    2. Thank you! You've made my day! :)

      Delete

Post a Comment

Popular posts from this blog

Asp.Net Core with Extended Identity and Jwt Auth Walkthrough

File Backups to Dropbox with PowerShell

Dynamic Expression Builder with EF Core