Share authentication cookies between ASP.NET 4.x and ASP.NET Core
I've been working with ASP.NET 4.x and ASP.NET Core for a while, and I've always noticed some unusual differences between them. The migration process from ASP.NET 4.x to ASP.NET Core can be challenging, depending on how the source code is designed or organized, the size of the application, and the packages used. I'm going to present a technique for implementing a side-by-side migration process that ensures both applications continue to run. Let's begin with a simple ASP.NET MVC 4.8 application configured for forms authentication. Normally, the authentication cookie is created by calling FormsAuthentication.SetAuthCookie(username, rememberMe); after a successful login, and it is destroyed by calling FormsAuthentication.SignOut() upon logout. This setup is supported by a small configuration in the web.config file using the authentication tag. using Mvc4FormAuthentication.Models; using System.Web.Mvc; using System.Web.Security; namespace Mvc4FormAuthentication.Controllers { public class AccountController : Controller { public ActionResult Login() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(LoginViewModel model, string returnUrl) { if (!ModelState.IsValid) { return View(model); } var isAuthenticated = IsAuthenticateUser(model.Username, model.Password); if (isAuthenticated) { // the AUTH cookie is set here FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe); return Redirect(string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl); } ModelState.AddModelError("", "Invalid username or password."); return View(model); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Logout() { // the AUTH cookie is removed here FormsAuthentication.SignOut(); return RedirectToAction("Index", "Home"); } private bool IsAuthenticateUser(string username, string password) { return true; } } } Using browser DevTools, you can locate the ASP.NET authentication cookie generated during the forms authentication login process. Easy! However, to share the authentication cookie between the applications, we need to change the way this cookie is generated. First, remove the web.config configuration. Let's go back to the original code without the authentication tag. Now, let's install some OWIN packages. Microsoft.Owin.Host.SystemWeb Microsoft.Owin.Security.Interop Microsoft.Owin.Security.Cookies Next, create a startup class. using Microsoft.AspNetCore.DataProtection; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.Interop; using Owin; using System; using System.IO; [assembly: OwinStartup(typeof(Mvc4OwinAuthentication.Startup))] namespace Mvc4OwinAuthentication { public class Startup { const string APPLICATION_NAME = "SharedAuthCookieApp"; const string COOKIE_NAME = ".AspNet.ApplicationAuthCookie"; const string COOKIE_PATH = "/"; const string SHARING_FOLDER_PATH = @"C:\Sources\Tests\DotNet\SharedAuthCookie"; public void Configuration(IAppBuilder app) { var loginPath = new PathString("/Account/Login"); var chunkingCookieManager = new ChunkingCookieManager(); var dataProtectionProvider = DataProtectionProvider.Create( new DirectoryInfo(SHARING_FOLDER_PATH), builder => { builder.SetApplicationName(APPLICATION_NAME); } ).CreateProtector( "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Cookies", "v2" ); var dataProtectorShim = new DataProtectorShim(dataProtectionProvider); var aspNetTicketDataFormat = new AspNetTicketDataFormat(dataProtectorShim); var cookieAuthenticationOptions = new CookieAuthenticationOptions { // This should match the AuthenticationType you use when creating the ClaimsIdentity ("Cookies") AuthenticationType = CookieAuthenticationDefaults.AuthenticationType, CookieHttpOnly = true, CookieManager = chunkingCookieManager, CookieName = COOKIE_NAME, CookiePath = COOKIE_PATH, CookieSameSite = SameSiteMode.Lax, ExpireTimeSpan = TimeSpan.FromMinutes(20), LoginPath = loginPath, SlidingExpiration = true, TicketDataFormat = aspNetTicketDataFormat }; app.UseCookieAuthentication(

I've been working with ASP.NET 4.x and ASP.NET Core for a while, and I've always noticed some unusual differences between them.
The migration process from ASP.NET 4.x to ASP.NET Core can be challenging, depending on how the source code is designed or organized, the size of the application, and the packages used.
I'm going to present a technique for implementing a side-by-side migration process that ensures both applications continue to run.
Let's begin with a simple ASP.NET MVC 4.8 application configured for forms authentication.
Normally, the authentication cookie is created by calling FormsAuthentication.SetAuthCookie(username, rememberMe); after a successful login, and it is destroyed by calling FormsAuthentication.SignOut() upon logout. This setup is supported by a small configuration in the web.config file using the authentication tag.
debug="true" targetFramework="4.8" />
targetFramework="4.8" />
mode="Forms">
loginUrl="~/Account/Login" timeout="2880" requireSSL="true" path="/" />
using Mvc4FormAuthentication.Models;
using System.Web.Mvc;
using System.Web.Security;
namespace Mvc4FormAuthentication.Controllers
{
public class AccountController : Controller
{
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var isAuthenticated = IsAuthenticateUser(model.Username, model.Password);
if (isAuthenticated)
{
// the AUTH cookie is set here
FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe);
return Redirect(string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl);
}
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Logout()
{
// the AUTH cookie is removed here
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
private bool IsAuthenticateUser(string username, string password)
{
return true;
}
}
}
Using browser DevTools, you can locate the ASP.NET authentication cookie generated during the forms authentication login process.
Easy!
However, to share the authentication cookie between the applications, we need to change the way this cookie is generated.
First, remove the web.config configuration. Let's go back to the original code without the authentication tag.
debug="true" targetFramework="4.8" />
targetFramework="4.8" />
Now, let's install some OWIN packages.
- Microsoft.Owin.Host.SystemWeb
- Microsoft.Owin.Security.Interop
- Microsoft.Owin.Security.Cookies
Next, create a startup class.
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Interop;
using Owin;
using System;
using System.IO;
[assembly: OwinStartup(typeof(Mvc4OwinAuthentication.Startup))]
namespace Mvc4OwinAuthentication
{
public class Startup
{
const string APPLICATION_NAME = "SharedAuthCookieApp";
const string COOKIE_NAME = ".AspNet.ApplicationAuthCookie";
const string COOKIE_PATH = "/";
const string SHARING_FOLDER_PATH = @"C:\Sources\Tests\DotNet\SharedAuthCookie";
public void Configuration(IAppBuilder app)
{
var loginPath = new PathString("/Account/Login");
var chunkingCookieManager = new ChunkingCookieManager();
var dataProtectionProvider = DataProtectionProvider.Create(
new DirectoryInfo(SHARING_FOLDER_PATH),
builder =>
{
builder.SetApplicationName(APPLICATION_NAME);
}
).CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
"Cookies",
"v2"
);
var dataProtectorShim = new DataProtectorShim(dataProtectionProvider);
var aspNetTicketDataFormat = new AspNetTicketDataFormat(dataProtectorShim);
var cookieAuthenticationOptions = new CookieAuthenticationOptions
{
// This should match the AuthenticationType you use when creating the ClaimsIdentity ("Cookies")
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieHttpOnly = true,
CookieManager = chunkingCookieManager,
CookieName = COOKIE_NAME,
CookiePath = COOKIE_PATH,
CookieSameSite = SameSiteMode.Lax,
ExpireTimeSpan = TimeSpan.FromMinutes(20),
LoginPath = loginPath,
SlidingExpiration = true,
TicketDataFormat = aspNetTicketDataFormat
};
app.UseCookieAuthentication(cookieAuthenticationOptions);
}
}
}
Add a configuration to AntiForgeryToken on Global.asax in the Application_Start.
protected void Application_Start()
{
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
And finally, the AccountController should create the auth cookie.
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Mvc4OwinAuthentication.Models;
using System.Collections.Generic;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
namespace Mvc4OwinAuthentication.Controllers
{
public class AccountController : Controller
{
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = AuthenticateUser(model.Username, model.Password);
if (user != null)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username)
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationType);
var authManager = HttpContext.GetOwinContext().Authentication;
var authProperties = new AuthenticationProperties
{
IsPersistent = model.RememberMe
};
authManager.SignIn(authProperties, claimsIdentity);
return Redirect(string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl);
}
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Logout()
{
HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return RedirectToAction("Index", "Home");
}
private User AuthenticateUser(string username, string password)
{
return new User(username);
}
}
}
Here's our new cookie.