Aspnetcore: IClaimsTransformation TransformAsync not working in asp.net core 3.0

Created on 29 Sep 2019  路  21Comments  路  Source: dotnet/aspnetcore

IClaimsTransformation TransformAsync not getting hit in netcoreapp3.0

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core '.3.0.0'
  2. Run this code '.'
 public class ClaimsExtender : IClaimsTransformation
    {

        public ClaimsExtender()
        {

        }

        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
             var x = "xxx";
        }
    }

When I Post a Request to my API, this TransformAsync method should always be hit.

This use to work in asp.net core 2.2

area-security

Most helpful comment

I am having the same issue and my user is authenticated. I am using Windows authorization and have the typical identity database structure. Users have claims and I have an IClaimsTransformation the adds the appropriate claims from the database to the ClaimsPrincipal.

In 2.2, as soon as the user signs in, the IClaimsTransformation is hit and the claims are added. In 3.0, the IClaimsTransformation is never hit.

I have messed with the order that the services are added, but this has had no impact.

The current order of my services is:
` services.AddControllersWithViews();

        services.AddDbContext<DataDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DataConnection")));

        services.AddDbContext<IDDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("IDConnection")));

        services.AddAuthentication(IISDefaults.AuthenticationScheme);

        string ApplicationName = Configuration.GetSection("ApplicationInformation")["AppDotName"];

        services.AddAuthorization(options =>
            {
                options.AddPolicy("IT", policy => policy.RequireClaim(ApplicationName, "IT"));
                options.AddPolicy("AppUser", policy => policy.RequireClaim(ApplicationName, "Application User - Entry Level"));
            });

        services.Configure<IISOptions>(Options =>
            Options.AutomaticAuthentication = true);

        services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<IDDbContext>();`

I have tried these is several combinations but the IClaimsTransformation is never hit.

Any suggestions?

I was facing the same problem. I just move app.UseAuthentication(); before app.UseRouting(); and then TransformAsync was called without problem.

All 21 comments

@EmpeRoar Can you show more code? How are you wiring it up? How did you wire up auth? Show your startup class please.

@davidfowl in my startup.cs

 services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.Authority = "xxx";
                options.Audience = "xxx";
            });
 services.AddAuthorization(options =>
            {
                options.AddPolicy("CanMove", policy => policy.Requirements.Add(new HasClaimRequirement("something")));
            });

 services.AddScoped<IClaimsTransformation, ClaimsExtender>();
 services.AddScoped<IAuthorizationHandler, HasClaimHandler>();

my claimsextender:

public class ClaimsExtender : IClaimsTransformation
    {

        public ClaimsExtender()
        {

        }

        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {   
           // I want to hit here!
            return principal;
        }
    }

my HasClaimHandler

public class HasClaimHandler : AuthorizationHandler<HasClaimRequirement>
    {
        public HasClaimHandler(AppIdentityDbContext applicationDbContext)
        {

        }

        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, HasClaimRequirement requirement)
        {
            // this is good , applicaiton hits here.
        }
    }

in startup configure.

  app.UseAuthentication();
 app.UseAuthorization();

This setup use to work in asp.net 2.2, but not working in asp.net 3.0

Can you show the entire startup class instead of the snippets. I need to see the order relative to everything else in the class.

```c#
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        var connection = Configuration.GetConnectionString("DefaultConnection");
        services.AddDbContext<AppIdentityDbContext>(
           options => options.UseSqlServer(
                           connection,
                           providerOptions => providerOptions.EnableRetryOnFailure()
                      ));

        services.AddTransient<IAppIdentityDbContext, AppIdentityDbContext>();
        services.AddTransient<IUserClaimStore<ApplicationUser>, ApplicationUserClaimStore>();

        services.AddIdentity<ApplicationUser, IdentityRole>(o =>
        {
            o.Password.RequireDigit = false;
            o.Password.RequireLowercase = false;
            o.Password.RequireUppercase = false;
            o.Password.RequireNonAlphanumeric = false;
            o.Password.RequiredLength = 6;
        })
        .AddEntityFrameworkStores<AppIdentityDbContext>()
        .AddUserManager<ApplicationUserManager>()
        .AddDefaultTokenProviders();

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.Authority = "xxx";
            options.Audience = "xxx";
        });


        services.AddAuthorization(options =>
        {
            options.AddPolicy("CanMove", policy => policy.Requirements.Add(new HasClaimRequirement("claw")));
        });

        services.AddScoped<IClaimsTransformation, ClaimsExtender>();
        services.AddScoped<IAuthorizationHandler, HasClaimHandler>();

        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
        services.AddScoped<IMediator, Mediator>();
        services.AddMediatR(typeof(MoveLeftCommandHandler).GetTypeInfo().Assembly);

        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo()
            {
                Contact = new OpenApiContact()
                {
                    Name = "xxx",
                    Url = new System.Uri("xxx"),
                    Email = "xxx"
                },
                Title = "xxx",
                Description = "",
                Version = "v1"
            });
        });

        services.AddCors(options =>
        {
            options.AddPolicy("AllowAllOrigins",
                builder =>
                {
                    builder.AllowAnyOrigin()
                           .AllowAnyHeader()
                           .AllowAnyMethod();
                });
        });

        services.AddSignalR();
        services.AddHttpContextAccessor();
        services.AddControllers();


    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseCors("AllowAllOrigins");
        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "Values Api V1");
        });

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapHub<ClawMachineHub>("/xxx");
        });
    }
}

```

public class ClaimsExtender : IClaimsTransformation
{

    public ClaimsExtender()
    {

    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // I want to hit here. :exclamation:
        return principal;
    }
}`

`

@davidfowl any news re this?

I was having the same problem, services.AddAuthentication(IISServerDefaults.AuthenticationScheme); worked for me.

.net core 3.0 defaults to InProcess hosting model, ctrl-f "When hosting in-process, AuthenticateAsync" at this link: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.2

@EmpeRoar AddIdentity and AddAuthentication add IClaimsTransformation to IServiceCollection. You have to place services.AddScoped<IClaimsTransformation, ClaimsExtender>(); before services.AddIdentity<ApplicationUser, IdentityRole>.

Thanks for the replies guys, i鈥檒l check it out once im back to my keyboards.

@mkArtakMSFT it still didn't hit TransformAsync

`
public class ClaimsExtender : IClaimsTransformation
{

    public ClaimsExtender()
    {

    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // breakpoint here.
        return principal;
    }
}

`

@jrvermeer have you resolved your issue?

cc @HaoK can you take a look?

specifically, im using Auth0 as Authentication Provider. I'm not sure if it will matter?

How are you accessing the authenticated user, it looks like you have identity and jwt bearer, is your endpoint protected with anything more than [Authorize]?

Note: claims transformation only runs if authentication was successful, see: https://github.com/aspnet/AspNetCore/blob/master/src/Http/Authentication.Core/src/AuthenticationService.cs#L77

One thing you can try is directly calling authenticate async to see what you are getting back, inject IAuthenticationService and call AuthenticateAsync in the debugger to see what you are getting back

ahhh, I think that answers it. If the user is not authenticated then it wont pass in clamins transformation.

Im only using [Authorize] attriblue with Policy.

I am having the same issue and my user is authenticated. I am using Windows authorization and have the typical identity database structure. Users have claims and I have an IClaimsTransformation the adds the appropriate claims from the database to the ClaimsPrincipal.

In 2.2, as soon as the user signs in, the IClaimsTransformation is hit and the claims are added. In 3.0, the IClaimsTransformation is never hit.

I have messed with the order that the services are added, but this has had no impact.

The current order of my services is:
` services.AddControllersWithViews();

        services.AddDbContext<DataDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DataConnection")));

        services.AddDbContext<IDDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("IDConnection")));

        services.AddAuthentication(IISDefaults.AuthenticationScheme);

        string ApplicationName = Configuration.GetSection("ApplicationInformation")["AppDotName"];

        services.AddAuthorization(options =>
            {
                options.AddPolicy("IT", policy => policy.RequireClaim(ApplicationName, "IT"));
                options.AddPolicy("AppUser", policy => policy.RequireClaim(ApplicationName, "Application User - Entry Level"));
            });

        services.Configure<IISOptions>(Options =>
            Options.AutomaticAuthentication = true);

        services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<IDDbContext>();`

I have tried these is several combinations but the IClaimsTransformation is never hit.

Any suggestions?

I am having the same issue and my user is authenticated. I am using Windows authorization and have the typical identity database structure. Users have claims and I have an IClaimsTransformation the adds the appropriate claims from the database to the ClaimsPrincipal.

In 2.2, as soon as the user signs in, the IClaimsTransformation is hit and the claims are added. In 3.0, the IClaimsTransformation is never hit.

I have messed with the order that the services are added, but this has had no impact.

The current order of my services is:
` services.AddControllersWithViews();

        services.AddDbContext<DataDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DataConnection")));

        services.AddDbContext<IDDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("IDConnection")));

        services.AddAuthentication(IISDefaults.AuthenticationScheme);

        string ApplicationName = Configuration.GetSection("ApplicationInformation")["AppDotName"];

        services.AddAuthorization(options =>
            {
                options.AddPolicy("IT", policy => policy.RequireClaim(ApplicationName, "IT"));
                options.AddPolicy("AppUser", policy => policy.RequireClaim(ApplicationName, "Application User - Entry Level"));
            });

        services.Configure<IISOptions>(Options =>
            Options.AutomaticAuthentication = true);

        services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<IDDbContext>();`

I have tried these is several combinations but the IClaimsTransformation is never hit.

Any suggestions?

I was facing the same problem. I just move app.UseAuthentication(); before app.UseRouting(); and then TransformAsync was called without problem.

This still does not work. Order has had no positive effect. Although the IClaimsTransformation is instantiated, TransformAsync is never called. It seems like the default Authorization Service does not know to call TransformAsync. You can also follow #18040, although there hasn't been much help there.

Was this page helpful?
0 / 5 - 0 ratings