3
votes

I am trying out AD authentication with Blazor (Server-side and .net core 3.0 preview-6).

When I add @attribute [Authorize(Roles = "DomainUsers")] I get the error below.

I get the same error if I change to Policy. However if I only use [Authorize] I do not get an error.

This occurs when I click on a link in the menu. If I write the direct path in the browser I works as expected.

public Startup(IConfiguration config)
    {
        Configuration = config;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().AddNewtonsoftJson();
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddHttpContextAccessor();
        services.AddAuthentication();
        services.AddAuthorization();

        services.AddHttpClient();

        var appDB = Configuration.GetConnectionString("AppDB");
        services.Configure<CtApiSettings>(Configuration.GetSection("CtApiSettings"));

        services.AddDbContext<ApplicationContext>(o => o.UseSqlServer(appDB, builder =>
        {
            builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
        }));


        services.AddToaster(config =>
        {
            config.PositionClass = Defaults.Classes.Position.TopFullWidth;
            config.PreventDuplicates = false;
            config.NewestOnTop = false;
            config.ShowTransitionDuration = 500;
            config.VisibleStateDuration = 5000;
            config.HideTransitionDuration = 500;
        });

        // Setup HttpClient for server side in a client side compatible fashion
        services.AddScoped<HttpClient>(s =>
        {
            // Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
            var uriHelper = s.GetRequiredService<IUriHelper>();
            return new HttpClient
            {
                BaseAddress = new Uri(uriHelper.GetBaseUri())
            };
        });

        ActiveDirectoryModel adm = new ActiveDirectoryModel();
        Configuration.GetSection("AD").Bind(adm);
        services.Configure<ActiveDirectoryModel>(Configuration.GetSection("AD"));

        services.AddScoped<ExcelExportService>();
        services.AddScoped<IAreaService, AreaService>();
        services.AddScoped<IUserProvider>(x => new UserProvider(adm));
        services.AddScoped<IAdminService, AdminService>();
    }

    // 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();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

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

        app.UseEndpoints(endpoints =>
        {
            //endpoints.MapRazorPages();
            //endpoints.MapControllers();
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }

Error: System.ObjectDisposedException: Safe handle has been closed. Object name: 'SafeHandle'. at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success) at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success) at Interop.Advapi32.GetTokenInformation(SafeAccessTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, UInt32& ReturnLength) at System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeAccessTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, Boolean nullOnInvalidParam) at System.Security.Principal.WindowsIdentity.get_User() at System.Security.Principal.WindowsIdentity.b__51_0() at System.Security.Principal.WindowsIdentity.<>c__DisplayClass67_0.b__0(Object ) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location where exception was thrown --- at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Security.Principal.WindowsIdentity.RunImpersonatedInternal(SafeAccessTokenHandle token, Action action) at System.Security.Principal.WindowsIdentity.RunImpersonated(SafeAccessTokenHandle safeAccessTokenHandle, Action action) at System.Security.Principal.WindowsIdentity.GetName() at System.Security.Principal.WindowsIdentity.get_Name() at System.Security.Principal.WindowsIdentity.InitializeClaims() at System.Security.Principal.WindowsIdentity.get_Claims()+MoveNext()
at System.Security.Claims.ClaimsIdentity.HasClaim(String type, String value) at System.Security.Claims.ClaimsPrincipal.IsInRole(String role) at System.Security.Principal.WindowsPrincipal.IsInRole(String role) at Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.<>c__DisplayClass4_0.b__0(String r) at System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 predicate) at Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement) at Microsoft.AspNetCore.Authorization.AuthorizationHandler1.HandleAsync(AuthorizationHandlerContext context) at Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.HandleAsync(AuthorizationHandlerContext context) at Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.AuthorizeAsync(ClaimsPrincipal user, Object resource, IEnumerable1 requirements) at Microsoft.AspNetCore.Components.AuthorizeViewCore.IsAuthorizedAsync(ClaimsPrincipal user) at Microsoft.AspNetCore.Components.AuthorizeViewCore.OnParametersSetAsync() at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task) at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()

2
Are you sure the group name is correct? in my AD it is called "Domain users" (with a space) - Flores
And also.. "Domain users" is a special build-in group..which behaves differently, have you tried another (self created)? - Flores
I've tried different Roles now (ex admin and another we created). I still get the same error. The strange thing is that I don't get the error if I go directly to the page. Bu if I click in the menu first then I get the error. - Fred

2 Answers

1
votes

According to the issue tracker on github:

Currently the built-in internal default FixedAuthenticationStateProvider assumes the authentication state is fixed for the lifetime of the circuit, as its name implies. However this isn't adequate for Windows authentication, as the WindowsPrincipal is connected to underlying OS services and can't continue to be used if the original HTTP request has completed. Trying to call things like IsInRole will throw if the principal has already been disposed

The fix has been merged to master and will be released with asp.net core 3.0.0-preview8

UPDATE: seems to be resolved, see here to upgrade to preview8 and fix breaking changes.

0
votes

I have the same problem - Blazor application with .Net Core 3.0-preview 6.

I'm using a custom AuthorizationHandler along with Identity Framework. The error is thrown inside HandleRequirementAsync when trying to read the current user's name with context.User.Identity.Name.