简体   繁体   中英

Share ASP.NET Owin Cookies with .NET Core Application whilst persisting keys to azure storage

We have a WebForms application built against ASP.Net Framework (4.7.2) that uses OWIN cookie authentication. As part of a migration towards .Net Core we would like to use the cookies within a .Net Core (2.1) API application.

The WebForms application runs in Azure and leverages DataProtectionStartup to hook into IServiceCollection the Machine Key file to use the PersistKeysToAzureBlobStorage method within Microsoft.AspNetCore.DataProtection as per the Microsoft documentation.

WebForms DataProtectionStartup

    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(blockblob)
        .SetApplicationName("OurAppName");

.Net Core API Startup

    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(blockblob)
        .SetApplicationName("OurAppName");

Both applications are happily running with the machine key generated and stored on blob storage.

Microsoft have documention that details how to share an OWIN cookie with a shared machine key file, using the DataProtectorShim from Microsoft.Owin.Security.Interop . The DataProtectionShim requires a DataProtectionProvider generated from the shared machine key, in the documentation this is referenced in both applications to create the cookie and uses the DataProtectionProvider.Create() method that takes the file location as an argument.

As we are using DataProtection with blob storage, we do not have this location. We have tried using DataProtectionProvider.Create() with just the application name on both applications in that it would use the blob storage key file. Unfortunately this does not create a cookie that works across both applications.

OWIN cookie authentication settings within OwinStartup :

    app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = 'Identity.Application',
            AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
            LoginPath = new PathString("/login.aspx"),
            CookieName = "AppCookieName",
            ExpireTimeSpan = 300,
            SlidingExpiration = true,

            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity =
                    SecurityStampValidator
                        .OnValidateIdentity<UserManager, User, int>(
                            validateInterval: TimeSpan.FromMinutes(30),
                            regenerateIdentityCallback: (manager, user) =>
                                manager.CreateIdentityAsync(user, 'Identity.Application'),
                            getUserIdCallback: (user) => user.GetUserId<int>())
            },
            TicketDataFormat = new AspNetTicketDataFormat(
                new DataProtectorShim(
                    DataProtectionProvider.Create("OurAppName")
                        .CreateProtector(
                            "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
                            'Identity.Application',
                            "v2")
                        )),

            CookieManager = new ChunkingCookieManager()
        });

And our .Net Core Startup setup for cookies:

    services.AddAuthentication("Identity.Application")
        .AddCookie("Identity.Application", options =>
        {
            options.Cookie.Name = "AppCookieName";
        });

Has anyone come across this scenario before, all the examples we have found only examples of the use of DataProtectionProvider.Create() used with machine key file location and have found no guidance for how to accomplish this with the PersistKeysToAzureBlobStorage method.

What i ended up doing is (ab)using the DI system (using the Microsoft.Extensions.DependencyInjection NuGet) to get the behaviour i wanted:

private static IDataProtector ConfigureDataProtection()
{
    // TODO this has to be wrong
    ServiceCollection services = new ServiceCollection();
    ConfigurationOptions configurationOptions = ConfigurationOptions.Parse("REDIS_CONNECTION_STRING_HERE");
    ConnectionMultiplexer rmp = ConnectionMultiplexer.Connect(configurationOptions);
    services.AddDataProtection()
        .SetApplicationName("APPNAME")
        .PersistKeysToStackExchangeRedis(rmp, "REDIS_DATA_PROTECTION_LIST_KEY");
    ServiceProvider provider = services.BuildServiceProvider();

    IDataProtectionProvider dataProtectionProvider = provider.GetRequiredService<IDataProtectionProvider>();
    return dataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Cookies" /* <- Auth Type to match the ASP.NET Core side, the rest is CRUCIAL and has to be the same purpose strings as internally used by ASP.NET Core */, "v2");
}

While my example uses Redis, I imagine the same would apply to the other implementations.

I was trying to do the same, Wanted to share cookie between New Asp.Net Core web application and Legacy WebForms project with Asp.Net Identity Authentication.

I hope this answer helps somebody who is trying to setup cookie authentication in Asp.Net WebForms with ability to store protection key some where other than shared directory path.

"DataProtectionProvider.Create" provides an action with IDataProtectionBuilder which can be used to overwrite key directory and store key in cloud (azure blob storage)

If webforms project has Azure.Extensions.AspNetCore.DataProtection.Blobs nuget package installed key could be stored in azure blob storage

eg

WebForms project identity cookie authentication setup.

app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                CookieName = $"SharedCookieName",
                CookieDomain = "SharedDomain",
                CookieSameSite = SameSiteMode.Lax,
                CookiePath = "/",
                CookieHttpOnly = true,
                ExpireTimeSpan = TimeSpan.FromMinutes(30),
                AuthenticationType = "Identity.Application",
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                        getUserIdCallback: (id) => (id.GetUserId<int>()))
                },

                // Note: (new DirectoryInfo(@"\")  dummy directory to satisfy DataProtectionProvider.Create and than overwrite key storage with PersistKeysToAzureBlobStorage
                TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(DataProtectionProvider.Create(new DirectoryInfo(@"\"),
                                                        (builder) =>
                                                        {
                                                            builder.SetApplicationName("ShareApplicationName")
                                                            .PersistKeysToAzureBlobStorage("AzureBlobConnectionString","ContainerName", "key.xml");
                                                        })
                                                        .CreateProtector(
                                                           $"SomeSharedPurposeName",
                                                           "Identity.Application",
                                                           "v2"))),

                CookieManager = new Microsoft.Owin.Security.Interop.ChunkingCookieManager()
            });
IDataProtectionBuilder has multiple extension methods to use blob storage differently. 

public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, Uri blobSasUri)

public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, Uri blobUri, TokenCredential tokenCredential)

public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, Uri blobUri, StorageSharedKeyCredential sharedKeyCredential)

public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, BlobClient blobClient)

Same approach could be used with Asp.Net Core while setting up application cookie.

services.ConfigureApplicationCookie(options => { });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM