Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cookie-based Forms Authentication Across MVC 5 and ASP.NET Core applications

I have used forms authentication across different sites already, even between different versions of .NET, but now we're looking into starting a new project in ASP.NET 5 (MVC 6) ASP.NET Core and would like to use cookie-based forms authentication across both. The login is done in "old" MVC 5 application.

Is some cross-applications configuration for cookie-based forms authentication even possible or supported with current version of ASP.NET 5? Could this be implemented on MVC6 ASP.NET Core side using FormsAuthenticationModule or can it play along somehow with the new authentication middleware? Any other suggestions?

like image 968
the berserker Avatar asked Oct 31 '22 01:10

the berserker


1 Answers

I have been beating my head over this same problem for the last few days... but I have solved it... (it seems to be holding up)

This is for converting windows and later forms Authentication to forms Authentication for MVC5 and MVC6 so hopefully you can change enough code to make it work for you... I plan to change some parts when I re-write the login scripts. (and this is alpha so will be making some changes!)

I put the following code in our MVC5 Intranet Site to grab the Roles for windows Authentication

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        // Get current windows Identity to get the roles out of it
        WindowsIdentity ident = WindowsIdentity.GetCurrent();

        string[] roles = new string[ident.Groups.Count];
        int i = 0;

        // get the groups from the current Identity
        foreach (var g in ident.Groups)
        {

            roles[i] = g.Translate(typeof(System.Security.Principal.NTAccount)).Value.ToString();
            i++;
        }

        // join into a single string  the roles that the user is a member of 
        string roleData = String.Join(";", roles) ;

        // create the forms ticket that all MVC5 sites with the same machine key will pick up.
        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, ident.Name, DateTime.Now, DateTime.Now.AddMinutes(30), false, roleData, "/");
        string encTicket = FormsAuthentication.Encrypt(ticket);


        // add the user name first from the Principle and add Windows as this will come from Windows Auth
        roleData = ident.Name + ";" + "Windows;" + roleData;

        //use machine key to encrypt the data
        var encTicket2 = MachineKey.Protect(System.Text.Encoding.UTF8.GetBytes(roleData),
            "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",
            "ApplicationCookie", "v1");

        //create a new cookie with a base64string of the encrypted bytes
        HttpCookie hc2 = new HttpCookie("cookie1", Convert.ToBase64String(encTicket2));
        hc2.Domain = ".domain.com";
        hc2.Expires = DateTime.Now.AddHours(8);
        Response.Cookies.Add(hc2);

        // NOTE: The name of the HttpCookie must match what the FormsAuth site expects.
        HttpCookie hc = new HttpCookie("cookie2", encTicket);
        hc.Domain = ".domain.com";
        hc.Expires = DateTime.Now.AddHours(8);
        Response.Cookies.Add(hc);
        // Ticket and cookie issued, now go to the FormsAuth site and all should be well.
        Response.Redirect("http://www.yoursite.com");
    }

this will create to a windows Authentication Ticket in both the forms and MVC6 method.

The string for MVC6 will look something like "John.Doe;Windows;Admin"

Then in the MVC6 startup file I have put the following code into the configure section...

        app.Use(async (context, next) =>
        {
            Logger _logger = new Logger("C:\\\\Logs\\Log.txt");
            try
            {

                var request = context.Request;
                var cookie = request.Cookies.Get("cookie1");
                var ticket = cookie.ToString();

                ticket = ticket.Replace(" ", "+");

                var padding = 3 - ((ticket.Length + 3)%4);
                if (padding != 0)
                    ticket = ticket + new string('=', padding);

                var bytes = Convert.FromBase64String(ticket);
                bytes = System.Web.Security.MachineKey.Unprotect(bytes,
                    "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",
                    "ApplicationCookie", "v1");

                string ticketstring = System.Text.Encoding.UTF8.GetString(bytes);

                var ticketSplit = ticketstring.Split(';');

                var claims = new Claim[ticketSplit.Length];

                var OriginalIssuer = "";

                for (int index = 0; index != ticketSplit.Length; ++index)
                {

                    if (index == 0)
                    {
                        claims[index] = new Claim(ClaimTypes.Name, ticketSplit[index], "Windows");
                    }
                    else if (index == 1)
                    {
                        OriginalIssuer = ticketSplit[1];
                    }
                    else
                    {
                        claims[index] = new Claim(ClaimTypes.Role,ticketSplit[0], OriginalIssuer);
                    }
                }

                var identity = new ClaimsIdentity(claims, OriginalIssuer, ClaimTypes.Name,ClaimTypes.Role);

                var principal = new ClaimsPrincipal(identity);

                _logger.Write(principal.Identity.Name);

                context.User = principal;
                _logger.Write("Cookie End");
                await next();
            } catch (Exception ex)
            {
                _logger.Write(ex.Message);
                _logger.Write(ex.StackTrace);
            }
        });

This then takes the cookie and creates a new claims identity from it. I've only just finished the logic to get it working so I'm sure it can be tidied up... Just thought I would get it to you so you can see if you can get some ideas about it.

like image 63
Kisbys Avatar answered Nov 09 '22 06:11

Kisbys