Search code examples
asp.net-coresessionsession-cookies

What is .NET Core doing to the Key value returned by my custom ITicketStore implementation before it's sent to the browser?


I implemented my own ITicketStore implementation in .NET Core which handles storing session cookies in a redis database. My keys are a simple guid:

    public async Task<string> StoreAsync(AuthenticationTicket ticket)
    {
        var log = new StringWriter();
        var guid = Guid.NewGuid();
        var key = "MyCustomCache"+ guid.ToString();
        await RenewAsync(key, ticket);
        return key;
    }
    public Task RenewAsync(string key, AuthenticationTicket ticket)
    {
        var options = new DistributedCacheEntryOptions();
        var expiresUtc = ticket.Properties.ExpiresUtc;
        if (expiresUtc.HasValue)
        {
            options.SetAbsoluteExpiration(expiresUtc.Value);
        }
        byte[] val = SerializeToBytes(ticket);
        _cache.Set(key, val, options);
        return Task.FromResult(0);
    }

However in the browser when I check the value of the cookie it seems to be encoded/encrypted somehow (rather than the guid I generated):

screenshot of guid

When the cookie is passed to my application I notice that its been transformed back to the original value I created in StoreAsync:

public Task<AuthenticationTicket> RetrieveAsync(string key)
{
    // Key passed in here will not be the value in the image above. Instead it will be what was 
    // generated in StoreAsync
    AuthenticationTicket ticket;
    byte[] bytes = null;
    bytes = _cache.Get(key);
    ticket = DeserializeFromBytes(bytes);
    return Task.FromResult(ticket);
}

What exactly is .NET Core doing to my keys to encrypt/encode the key? Will this affect my ability to load balance my application? I know that in the default session storage mechanism .NET Core encrypts the session cookies using a dynamic key per machine.


Solution

  • The authentication cookie is being encrypted with the default data protection.

    Here is where the key is added to the cookie (source):

    if (Options.SessionStore != null)
    {
        if (_sessionKey != null)
        {
            await Options.SessionStore.RemoveAsync(_sessionKey);
        }
        _sessionKey = await Options.SessionStore.StoreAsync(ticket);
        var principal = new ClaimsPrincipal(
            new ClaimsIdentity(
                new[] { new Claim(SessionIdClaim, _sessionKey, ClaimValueTypes.String, Options.ClaimsIssuer) },
                Options.ClaimsIssuer));
        ticket = new AuthenticationTicket(principal, null, Scheme.Name);
    }
    
    var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding());
    

    When switching to a SessionStore, the session key is just added as a claim and then the cookie is protected.