Variable cookie path with ASP.NET Identity

2020-02-12 08:13发布

问题:

We migrated a multitenant MVC application from ASP.NET Membership Provider to ASP.NET Identity.

This is my Startup.Auth.cs (simplified):

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity =
                    SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, Identity, int>(
                        TimeSpan.FromMinutes(30),
                        (manager, user) =>
                            manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie),
                        clIdentity => clIdentity.GetUserId<int>())
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}

In our multitenant application, each tenant has its own 'slug' (e.g. http://example.com/tenant1/ and http://example.com/tenant2/)

However, currently, the cookies are stored in the root. This causes security issues as users from tenant1 are automatically logged in on the website from tenant2.

How can we make the CookiePath (in CookieAuthenticationOptions) variable so that it changes depending on the tenant?

回答1:

I fixed this issue with a lot of help from dampee.

The CookiePath in the CookieAuthenticationOptions object is evaluated only once: at application startup. The easiest solution (workaround) was to create a derived CookieAuthenticationProvider that overrides ResponseSignIn and ResponseSignOut. They both have an argument called context which has a property called CookiePath. Modify this property in both of these methods to change the CookiePath. You can also use the class I created.

Then all you have to do is replace the CookieAuthenticationProvider in the CookieAuthenticationOptions with the one you just created.

This works for the ApplicationCookie. The ExternalSignInCookie doesn't matter that much since it is used only temporarily while signing in with an external login.



回答2:

Improving on SamuelDebruyn's own solution, I found you can pass the path from the SignIn call to the provider using an AuthenticationProperties object. This way, instead of extracting the path from the request context as his gist shows, you can pass it explicitly from the source:

// method inside web api controller
private void SignIn(string name, string cookiePath)
{
    var claims = new[] { new Claim(ClaimTypes.Name, name) };
    var identity = new ClaimsIdentity(claims, "ApplicationCookie");

    var options = new AuthenticationProperties();
    options.Dictionary["CustomCookiePath"] = cookiePath;

    var authManager = Request.GetOwinContext().Authentication;
    authManager.SignIn(options, identity);
}

// Startup.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    Provider = new CustomCookieProvider()
});

// custom provider
public class CustomCookieProvider : CookieAuthenticationProvider
{
    public override void ResponseSignIn(CookieResponseSignInContext context)
    {
        context.CookieOptions.Path = context.Properties.Dictionary["CustomCookiePath"];
        base.ResponseSignIn(context);
    }
}


回答3:

You can use a custom ICookieManager to dynamically return the cookie value to the CookieAuthenticationProvider based on whatever is in the request, to do this you would still maintain the CookiePath as "/" and then leave it up to the ICookieManager to return (or write) the cookie however you want. The CookieManager is an option on the CookieAuthenticationOptions. I blogged about this here: http://shazwazza.com/post/owin-cookie-authentication-with-variable-cookie-paths/