Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leonardochaia/e76f719ff43abb734e9d50cecd8f7ca9 to your computer and use it in GitHub Desktop.
Save leonardochaia/e76f719ff43abb734e9d50cecd8f7ca9 to your computer and use it in GitHub Desktop.
.Net Core Per Request Cookie Configuration
using System;
using AstonishingLab.MultiTenancy;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
public static class IServiceCollectionCookieExtensions
{
public static IServiceCollection ConfigurePerRequestApplicationCookie(this IServiceCollection services,
Action<HttpContext, CookieAuthenticationOptions, string> configAction)
{
if (configAction == null)
{
throw new ArgumentNullException(nameof(configAction));
}
services.Configure<SiteCookieAuthenticationConfiguration>(config =>
{
config.PerRequestAction = configAction;
});
services.AddSingleton<IOptionsMonitor<CookieAuthenticationOptions>, SiteCookieAuthenticationOptions>();
return services;
}
}
public class SiteCookieAuthenticationConfiguration
{
public Action<HttpContext, CookieAuthenticationOptions, string> PerRequestAction { get; set; }
}
}
using System;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
namespace AstonishingLab.MultiTenancy
{
/// <summary>
/// Inspired on Cloudscrib.
/// https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Core.Identity/SiteCookieAuthenticationOptions.cs
/// </summary>
public class SiteCookieAuthenticationOptions : IOptionsMonitor<CookieAuthenticationOptions>
{
private readonly SiteCookieAuthenticationConfiguration siteCookieAuthenticationConfiguration;
private readonly IHttpContextAccessor httpContextAccessor;
private readonly ILogger log;
private readonly IOptionsMonitorCache<CookieAuthenticationOptions> cache;
private readonly IOptionsFactory<CookieAuthenticationOptions> factory;
private readonly IEnumerable<IOptionsChangeTokenSource<CookieAuthenticationOptions>> sources;
internal event Action<CookieAuthenticationOptions, string> _onChange;
public SiteCookieAuthenticationOptions(
IOptionsFactory<CookieAuthenticationOptions> factory,
IEnumerable<IOptionsChangeTokenSource<CookieAuthenticationOptions>> sources,
IOptionsMonitorCache<CookieAuthenticationOptions> cache,
IHttpContextAccessor httpContextAccessor,
IOptions<SiteCookieAuthenticationConfiguration> siteCookieAuthenticationConfiguration,
ILogger<SiteCookieAuthenticationOptions> logger)
{
this.httpContextAccessor = httpContextAccessor;
log = logger;
this.factory = factory;
this.sources = sources;
this.cache = cache;
this.siteCookieAuthenticationConfiguration = siteCookieAuthenticationConfiguration.Value;
foreach (var source in this.sources)
{
ChangeToken.OnChange<string>(
() => source.GetChangeToken(),
(name) => InvokeChanged(name),
source.Name);
}
}
private CookieAuthenticationOptions ResolveOptions(string scheme)
{
var services = httpContextAccessor.HttpContext.RequestServices;
var options = new CookieAuthenticationOptions();
var cookieOptionsInitializer = services.GetRequiredService<IPostConfigureOptions<CookieAuthenticationOptions>>();
cookieOptionsInitializer.PostConfigure(scheme, options);
if (siteCookieAuthenticationConfiguration.PerRequestAction != null)
{
siteCookieAuthenticationConfiguration.PerRequestAction(httpContextAccessor.HttpContext, options, scheme);
}
return options;
}
public CookieAuthenticationOptions CurrentValue
{
get
{
return ResolveOptions(IdentityConstants.ApplicationScheme);
}
}
public CookieAuthenticationOptions Get(string name)
{
return ResolveOptions(name);
}
private void InvokeChanged(string name)
{
name = name ?? Options.DefaultName;
cache.TryRemove(name);
var options = Get(name);
if (_onChange != null)
{
_onChange.Invoke(options, name);
}
}
public IDisposable OnChange(Action<CookieAuthenticationOptions, string> listener)
{
log.LogDebug("onchange invoked");
var disposable = new ChangeTrackerDisposable(this, listener);
_onChange += disposable.OnChange;
return disposable;
}
internal class ChangeTrackerDisposable : IDisposable
{
private readonly Action<CookieAuthenticationOptions, string> _listener;
private readonly SiteCookieAuthenticationOptions _monitor;
public ChangeTrackerDisposable(SiteCookieAuthenticationOptions monitor, Action<CookieAuthenticationOptions, string> listener)
{
_listener = listener;
_monitor = monitor;
}
public void OnChange(CookieAuthenticationOptions options, string name) => _listener.Invoke(options, name);
public void Dispose() => _monitor._onChange -= OnChange;
}
}
}
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureApplicationCookie(options =>
{
options.SlidingExpiration = true;
});
services.ConfigurePerRequestApplicationCookie((context, options, scheme) =>
{
var tenant = // Get tenant from somewhere
options.Cookie.Name = $"AstonishingLab.{scheme}.Tenant{tenant.Id}";
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment