asp.net-corecookiesclaims-based-identityws-federation

ASP.Net Core WSFederationAuth Login with Cookie - Site can't be reached


I'm upgrading an old site to ASP.NET Core 2.1, but still targeting .NET Framework 4.7.2 as the target framework.

We are trying to use WSFederation services to handle authentication as described in this Microsoft document. A separate site handles the actual log-in and then redirects back to our site at the /Federation end-point.

Our Startup.cs class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(authOptions =>
    {
        authOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        authOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        authOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
    })
    .AddWsFederation(wsOptions =>
    {
        wsOptions.Wtrealm = Configuration.GetValue<string>("WSFed:Realm");
        wsOptions.MetadataAddress = Configuration.GetValue<string>("WSFed:Metadata");
        wsOptions.CallbackPath = "/Federation";
    })
    .AddCookie(cookieOptions => 
    {
        cookieOptions.Cookie.SameSite = SameSiteMode.None;
    });

    //Fixes correlation error
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddHttpsRedirection(options =>
    {
        options.HttpsPort = 443;
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseDeveloperExceptionPage();

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

    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

We then added the "[Authorize]" attribute to our Home controller. When I run this, I get a "This site can't be reached" error. The webpage at https://localhost:44339/ might be temporarily down or it may have moved permanently to a new web address. ERR_HTTP2_PROTOCOL_ERROR

When I view the network tab in Chrome dev tools, I can see the cookies being passed around I can see the correct claim information getting passed along, so it looks like that part of it is working correctly.

Any ideas what might be going wrong?

Update

I added the event wsOptions.Events.OnSecurityTokenValidated and put a breakpoint in. I can see the claims values, so it looks like the authentication took place and that was successful, but I still get the ERR_HTTP2_PROTOCOL_ERROR error. If I remove the [Authorize] attribute from the controller, the site runs, but I obviously don't want to remove that attribute.

Update 2

It seems that the issue is that the Cookie size is too large. Currently investigating ways to resolve this.


Solution

  • The main issue appeared to be the cookie/claim size (having too many roles/AD groups).

    The ideal solution it seems would be to shrink the size of the cookie/claims, but this isn't realistic for us to do right now.

    I got around this by adding an OnSecurityTokenValidated event and within that concatenating the roles into one claim string value with a hard-coded separator between each one. This makes it so you have one claim value for Roles rather than a claim value per role, greatly reducing the size.

    .AddWsFederation(wsOptions =>
    {
        ...
        wsOptions.Events.OnSecurityTokenValidated = (context) =>
        {
            //Condense claim data (roles) - otherwise we get errors for too large of claims
            HashSet<Claim> filteredClaims = new HashSet<Claim>();
            HashSet<string> groupSet = new HashSet<string>();
            
            foreach(Claim claim in context.Principal.Claims)
            {
                if(claim.Type != ClaimTypes.Role)
                {
                    filteredClaims.Add(claim);
                }
                else
                {
                    groupSet.Add(claim.Value);
                }
            }
            filteredClaims.Add(new Claim("<insert new claim key here>", string.Join("zzzsep", groupSet)));
    
            List <ClaimsIdentity> filteredClaimsIdentity = new List<ClaimsIdentity>() { new ClaimsIdentity(filteredClaims, context.Principal.Identity.AuthenticationType) };
            ClaimsPrincipal newClaimsPrincipal = new ClaimsPrincipal(filteredClaimsIdentity);
            context.Principal = newClaimsPrincipal;
    
            return Task.FromResult(0);
        };
    })
    

    Then when actually getting the groups/roles from the claim, I split the string by the hard-coded separator to get an array of values.