5

I have an Asp .net mvc app that connects to an Identity server 4 identity server. When i released the app I was fased with this error.

upstream sent too big header while reading response header from upstream

Which i have tracked to this upstream sent too big header while reading response header from upstream

I can not alter the config and sys admin has stated that we need to make the headers smaller.

enter image description here

After looking at that i would have to agree that these headers are a bit extensive.

Startup.cs in app

services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
            {

                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.Authority = Configuration["ServiceSettings:IdentityServerEndpoint"];
                options.RequireHttpsMetadata = true;
                options.ClientId = Configuration["ServiceSettings:ClientId"];
                options.ClientSecret = Configuration["ServiceSettings:secret"];
                options.Scope.Add("testapi");
                options.ResponseType = "code id_token";
                options.SaveTokens = true;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.Events = new OpenIdConnectEvents()
                {
                    OnRemoteFailure = ctx =>
                    {
                        _logger.LogCritical($"Remote Faliure: {ctx.Failure}");
                        ctx.HandleResponse();
                        return Task.FromResult(0);
                    }
                };
            });

I have been looking all over and i cant seem to find a way of limiting the size of this huge header.

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • You need to store authentication info on server, not in cookie. Here is a sample code showing how to do that: https://github.com/aspnet/Security/tree/dev/samples/CookieSessionSample (key part is `.AddCookie(o => o.SessionStore = new MemoryCacheTicketStore())`) – Evk May 24 '18 at 09:29
  • 2
    Strange my code comes directly from the Identiy server 4 tutorials. – Linda Lawton - DaImTo May 24 '18 at 10:09
  • I don't mean you are doing something wrong, storing everything in cookie is also an option. But in your case size of stored information is too big, so you have to use another option - store that on server. – Evk May 24 '18 at 10:11
  • Any clue why its so big? Its not a problem in our other projects. – Linda Lawton - DaImTo May 24 '18 at 10:12
  • Usually it's big because given user has a lot of claims (or claims with big values). All those claims are serialized in cookie, so there it is. – Evk May 24 '18 at 10:13
  • 1
    If i run this locally it works fine. This user is also able to login on another app that uses an implicit grant as apposed to this one which is hybrid. (Grumble about claim size) – Linda Lawton - DaImTo May 24 '18 at 10:53
  • By "works fine" you mean cookie size is small? – Evk May 24 '18 at 11:08
  • The other apps that we have are implicit grant type and use angular JS. If they were building cookies this big then Kong would be kicking them out as well so i have to assume they are not. Why is it doing cookies anyway why not just return an id token as normal. – Linda Lawton - DaImTo May 24 '18 at 11:25
  • 1
    We got the same problem (with .NET Framework client, but I assume that the reason is the same in .NET Core) - the Microsoft middle-ware and the encryption coming with it, is creating this huge cookie. The number of claims didn't matter (or at least not significantly). We ended up doing as Evk suggested - storing the authentication info in database and issuing our own cookie. We shrink the cookie from around 3k to less then 0.5k and the problem disappeared. – m3n7alsnak3 May 24 '18 at 15:32
  • 2
    @m3n7alsnak3 how does this effect running stateless? – Linda Lawton - DaImTo May 25 '18 at 06:19
  • @Evk want to add that as an answer and i will accept it? – Linda Lawton - DaImTo May 29 '18 at 07:38
  • @Evk do you know how this would work with MSAL? – japes Sophey Feb 10 '20 at 11:35

1 Answers1

6

By default, cookie contains all relevant information, encrypted, so when call to your api is made - all you need to do is decrypt cookie and use that information.

However, often storing everything in a cookie itself is not desirable, especially if there is a lot of relevant information. Cookie is sent with every request to your api, and if it's big - a lot of (the same) information is sent back and forth basically for nothing. Plus as you see yourself - size of cookie might become a limiting factor in some environments.

So instead of sending all information in cookie itself - you can store that information somewhere else, for example in server memory, and place only identifier for that information in cookie itself.

You can configure store for session like this:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(o => o.SessionStore = new MemoryCacheTicketStore());

And you can find sample MemoryCacheTicketStore implementation at asp.net examples on github:

public class MemoryCacheTicketStore : ITicketStore
{
    private const string KeyPrefix = "AuthSessionStore-";
    private IMemoryCache _cache;

    public MemoryCacheTicketStore()
    {
        _cache = new MemoryCache(new MemoryCacheOptions());
    }

    public async Task<string> StoreAsync(AuthenticationTicket ticket)
    {
        var guid = Guid.NewGuid();
        var key = KeyPrefix + guid.ToString();
        await RenewAsync(key, ticket);
        return key;
    }

    public Task RenewAsync(string key, AuthenticationTicket ticket)
    {
        var options = new MemoryCacheEntryOptions();
        var expiresUtc = ticket.Properties.ExpiresUtc;
        if (expiresUtc.HasValue)
        {
            options.SetAbsoluteExpiration(expiresUtc.Value);
        }
        options.SetSlidingExpiration(TimeSpan.FromHours(1)); // TODO: configurable.

        _cache.Set(key, ticket, options);

        return Task.FromResult(0);
    }

    public Task<AuthenticationTicket> RetrieveAsync(string key)
    {
        AuthenticationTicket ticket;
        _cache.TryGetValue(key, out ticket);
        return Task.FromResult(ticket);
    }

    public Task RemoveAsync(string key)
    {
        _cache.Remove(key);
        return Task.FromResult(0);
    }
}
Evk
  • 98,527
  • 8
  • 141
  • 191