12

I have a webpage which uses multiple URLS for the same application:

for example: *.MyWebPage.com.au *.YourWebPage.com.au

So it will use subdomains on multiple urls. The problem is I need to allow for the user to be authenticated on all subdomains of the url which they have logged into.

For example, if they login via www.mywebpage.com.au the cookie needs to be set for *.mywebpage.com.au or if they login via www.yourwebpage.com.au the cookie should be *.yourwebpage.com.au.

Most of the documentation in allowing subdomains for ASP.NET core identity points to the startup.cs (or startup.auth.cs) file and entering something like this:`

app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                CookieDomain = "mywebpage.com.au"
            });`

this will not work for me because I dont want a fixed domain, I just want to allow for all the users to have access to all the subdomains for the url they have signed in at. I can obviously get their url at the time of login via the request, but I need to dynamically set the cookiedomain at this point.

Michael
  • 8,229
  • 20
  • 61
  • 113
  • I'm not absolutely certain; but, for this kind of case -- I'm almost positive you will have to derive from, or create your own cookie middleware. – Svek May 28 '17 at 14:12
  • This idea of using SaasKit to allow multi-tenant ASP.NET Core pipelines might be a possible solution http://benfoster.io/blog/aspnet-core-multi-tenant-middleware-pipelines – Svek May 28 '17 at 14:27

4 Answers4

16

What I didnt realise when I started was the difference between Identity and CookieAuthentication. Since I was using Identity

        app.UseIdentity();

app.UseCookieAuthentication was not the solution.

I finally found my solution by implementing ICookieManager.

Here is my solution:

in Startup.cs:

    services.AddIdentity<ApplicationUser, IdentityRole>(options =>
        {
            options.Password.RequireDigit = false;
            options.Password.RequiredLength = 5;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireLowercase = false;
            options.Password.RequireUppercase = false;
            options.Cookies.ApplicationCookie.CookieManager = new CookieManager(); //Magic happens here
        }).AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

now in a class I have called CookieManager.cs:

public class CookieManager : ICookieManager
{
    #region Private Members

    private readonly ICookieManager ConcreteManager;

    #endregion

    #region Prvate Methods

    private string RemoveSubdomain(string host)
    {
        var splitHostname = host.Split('.');
        //if not localhost
        if (splitHostname.Length > 1)
        {
            return string.Join(".", splitHostname.Skip(1));
        }
        else
        {
            return host;
        }
    }

    #endregion

    #region Public Methods

    public CookieManager()
    {
        ConcreteManager = new ChunkingCookieManager();
    }

    public void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options)
    {

        options.Domain = RemoveSubdomain(context.Request.Host.Host);  //Set the Cookie Domain using the request from host
        ConcreteManager.AppendResponseCookie(context, key, value, options);
    }

    public void DeleteCookie(HttpContext context, string key, CookieOptions options)
    {
        ConcreteManager.DeleteCookie(context, key, options);
    }

    public string GetRequestCookie(HttpContext context, string key)
    {
        return ConcreteManager.GetRequestCookie(context, key);
    }

    #endregion
dcg
  • 4,187
  • 1
  • 18
  • 32
Michael
  • 8,229
  • 20
  • 61
  • 113
  • For anyone who implements htis, you will also need to handle the deletecookie event, adding options.Domain = RemoveSubdomain(context.Request.Host.Host) – Michael Jun 01 '17 at 14:53
  • I was looking for a solution that dynamically set a different cookie domain for different accounts, this solution works, nice work! – Leon Jan 06 '22 at 08:56
  • Is there a risk CookieOptions options coming in to AppendResponseCookie is a singleton or is it always per HTTP request? – Johan Kronberg Jan 30 '23 at 07:44
5

In addition to @michael's solution:

  • ICookie: ICookie Interface is an abstraction layer on top of http cookie object, which secures the data.
  • ICookieManager: Cookie Manager is an abstraction layer on top of ICookie Interface. This extends the Cookie behavior in terms of <TSource> generic support, Func<TResult>.This is implemented by DefaultCookieManager class. ICookie Interface is a depedenacy of this class.
  • Usage of CookieManager:

    1. Add CookieManager in startup Configure Service.
    2. Access the CookieManager API.
    3. And the source code is available on git by Nemi Chand.
King Reload
  • 2,780
  • 1
  • 17
  • 42
2

How many main domains are there? If there are not too many, you can add several CookieAuthenticationOptions. Like this:

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "mywebpage.com.au",
            CookieDomain = "mywebpage.com.au",
        });
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "yourwebpage.com.au",
            CookieDomain = "yourwebpage.com.au",
        });

If there are too many main domains, you will need to write your own cookie provider.

VHao
  • 732
  • 7
  • 7
1

Addition to @michael's answer: How to "handle the deletecookie event, adding options.Domain = RemoveSubdomain(context.Request.Host.Host)": just add

options.Domain= RemoveSubdomain(context.Request.Host.Host);

before

    ConcreteManager.DeleteCookie(context, key, options);

in

CookieManager.DeleteCoockie(..){..};

And don't forget to call CookieManager.DeleteCoockie on logout!

PS Also, if you need to be able to login both on subdomain.example.com and on example.com - you need to modify AppendResponseCookie(..){..}, or you will get only TLD (.com/.ru etc) here