Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Enable IClaimsTransformer to be specified in DI instead of only options #863

@jburbach

Description

@jburbach

I'm not sure whether to post this here or in the EF repo, but I figured I'd start here.

ASP.NET Core RC2 WebAPI/SPA using windows auth, and I'm attempting to do a very basic claims transformation by looking the ID up in my database with my DBContext and adding claims I find there to the windows principal. I am using a repository pattern with EF, all registered as Scoped lifetime, the dbcontext should as well by default, so all methods are sharing the same dbcontext on each request. I get a lot of random EF errors on my _users.FindByUsername() method that say things like

Invalid attempt to call ReadAsync when reader is closed.
InvalidOperationException: BeginExecuteReader requires an open and available Connection. The connection's current state is open.
InvalidOperationException: The connection was not closed. The connection's current state is connecting.

I also get this error at startup, but then can refresh the page and get my SPA like I should:

InvalidOperationException: An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point.

I think this is related to the transform running async and competing with other threads trying to access the db at the same time? I get these errors randomly under load, nothing consistent. If I take the DB lookup out of my claimstransform, zero errors. I tried making my user lookup async and awaiting it, which lessens the amount of errors but doesn't completely solve the issue.

My question is, is what I'm doing not recommended? Should I not be trying to use EF with a ClaimsTransform, or should I have a separate context just for this user lookup? Do I need to scope my dbcontext/repositories differently in DI? I'm curious what the envisioned recommended pattern is for something like this, and if this is possibly an EF Core issue that should work but doesn't.

Apologies if there is something obvious here I'm missing, I've looked through all the documentation I can find for best practices and didn't find anything that recommends something different.

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using MyApp.Models.Repositories.Interfaces;
using Microsoft.AspNetCore.Authentication;

namespace MyApp.Authorization
{
    public class ClaimsTransformer : IClaimsTransformer
    {
        private readonly IUserRepository _users;

        public ClaimsTransformer(IUserRepository users)
        {
            _users = users;           
        }

        public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
        {
            System.Security.Principal.WindowsIdentity windowsIdentity = null;

            foreach (var i in context.Principal.Identities)
            {
                //windows token
                if (i.GetType() == typeof(System.Security.Principal.WindowsIdentity))
                {
                    windowsIdentity = (System.Security.Principal.WindowsIdentity)i;
                }
            }

            if (windowsIdentity != null)
            {
                //find user in database
                var appUser = _users.FindByUsername(windowsIdentity.Name);

                if (appUser != null)
                {

                    //add all claims from security profile
                    foreach (var p in appUser.SecurityProfile.Permissions)
                    {
                        ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(p.Permission, "true"));
                    }

                }

            }

            return await Task.FromResult(context.Principal);
        }
    }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions