无法与新的dotnet核心Web应用程序共享身份验证cookie [英] Unable to share authentication cookie with new dotnet core web app

查看:87
本文介绍了无法与新的dotnet核心Web应用程序共享身份验证cookie的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

新问题

更新:由于这首先遇到了一些一般性挑战,并且从一个更具体的问题开始,我已经

Update: since this started with some general challenges and has since zeroed in on a more specific issue I've re-posted as a new question here.

我一直在关注

I have been following Microsoft's advice for sharing an authentication cookie issued by an ASP.NET web app with a separate dotnet core web app running on the same domain. Unfortunately the dotnet core app is not unprotecting the cookie as expected and I'm struggling to diagnose why.

我将尝试简化我所做的事情.在我做之前,我应该指出两个应用程序都将在同一路径下运行-我们将其命名为mydomain.com/path-这样,身份验证cookie的作用域将是该路径.还有很多其他的复杂性,因为我实际上是试图将其连接到一个旧的OIDC库中,但是我认为我遇到的主要问题是另一方面,我有一个相当轻量的dotnet核心应用程序试图使用相同的功能.会议.

I'll try to simplify what I've done. Before I do I should point out that both apps will run under the same path - let's call it mydomain.com/path - so the auth cookie will be scoped to that path. There's a lot of additional complexity because I'm actually trying to wire this into an old OIDC library, but I think the main issue I'm having is on the other side where I have a fairly lightweight dotnet core app trying to use the same session.

首先,在我的原始.NET应用程序(它是4.7.2)中,我正在使用 Microsoft.Owin.Security.Interop 库创建一个新的数据保护器:

First, in my original .NET app (it's 4.7.2) I'm creating a new data protector using the Microsoft.Owin.Security.Interop library:

var appName = "<my-app-name>";

var encryptionSettings = new AuthenticatedEncryptionSettings()
{
    EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
    ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
};

var interopProvider = DataProtectionProvider.Create(
    new DirectoryInfo(keyRingSharePath),
    builder =>
    {
        builder.SetApplicationName(appName);
        builder.SetDefaultKeyLifetime(TimeSpan.FromDays(365 * 20));
        builder.UseCryptographicAlgorithms(encryptionSettings);

        if (!generateNewKey)
        {
            builder.DisableAutomaticKeyGeneration();
        }
    });

return new DataProtectorShim(
    interopProvider.CreateProtector(
        "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
        appName,
        "v2"));

请注意,按照 CookieAuthenticationOptions 中的设置,< my-app-name> 也是cookie的名称.

Note that <my-app-name> is also the name of the cookie, as set in the CookieAuthenticationOptions.

keyRingSharePath 目前仅是我PC上的本地路径.第一次运行此命令时,我将 generateNewKey 设置为 true ,以确保在此路径上生成新密钥.此后,我保留此 false 来确保密钥被重新使用.

keyRingSharePath is for now just a local path on my PC. The first time I run this I have generateNewKey set to true to ensure a new key is generated at this path. Thereafter I leave this false to ensure that key is re-used.

我还根据以下文档使用此数据保护器分配了票证数据格式: new TicketDataFormat(dataProtector).

I also assign the ticket data format using this data protector as per the docs: new TicketDataFormat(dataProtector).

这可以正常工作,因为身份验证仍然有效,我什至可以通过使用上面创建的 TicketDataFormat 的实例并通过调用其 Unprotect 方法来验证数据保护验证cookie值并获取 ClaimsIdentity .

This works as expected in that authentication still works and I can even verify the data protection by using an instance of the TicketDataFormat created above and calling its Unprotect method with the auth cookie value and getting a ClaimsIdentity back.

接下来,我创建了一个简单的dotnet核心应用程序,该应用程序在与上述应用程序相同的域上运行.在 Startup 中,我添加了以下内容:

Next I've created a simple dotnet core app which runs on the same domain as the above app. In the Startup I've added this:

var primaryAuthenticationType = "<my-app-name>";
var cookieName = primaryAuthenticationType;

services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(keyRingSharePath))
    .SetDefaultKeyLifetime(TimeSpan.FromDays(365 * 20))
    .DisableAutomaticKeyGeneration()
    .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration()
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    })
    .SetApplicationName(primaryAuthenticationType);

services.ConfigureApplicationCookie(options => {
    options.Cookie.Name = cookieName;
    options.Cookie.Path = "/path";
});

很显然, keyRingSharePath 具有与ASP.NET应用程序相同的值.我在 Startup ConfigureServices 方法中也有以下内容:

Obviously keyRingSharePath holds the same value as in the ASP.NET app. I also have the following in the ConfigureServices method in Startup:

app.UseAuthentication();
app.UseAuthorization();

已使用ASP.NET应用程序登录,然后切换到我的dotnet核心应用程序.但是不幸的是,在使用/path 下的路由调试任何控制器时,我发现 User.Identity.IsAuthenticated false .

Having signed in using the ASP.NET app I then switch to my dotnet core app. But unfortunately when debugging any controller with a route under /path I find that User.Identity.IsAuthenticated is false.

我还尝试过使用注入的 IDataProtectionProvider 实例,手动取消对cookie的保护,例如:

I've also tried unprotecting the cookie manually like this, using an injected instance of IDataProtectionProvider:

var protector = protectionProvider.CreateProtector(
    "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
    "<my-app-name>",
    "v2");

var ticketDataFormat = new TicketDataFormat(dataProtector);

var ticket = ticketDataFormat.Unprotect("<auth-cookie-value>");

return ticket?.Principal;

但是, ticket 被分配为null,我找不到任何方法来调试为什么它不会取消保护cookie值的原因.据我所知,这应该使用与我确认可以取消保护该Cookie时的ASP.NET应用程序相同的逻辑.

However, ticket is assigned null and I can't find any way to debug why it won't unprotect the cookie value. As far as I can tell this should use the same logic that my ASP.NET app used when I confirmed that I could unprotect that cookie.

任何指导将不胜感激.

更新1

我一直在尝试通过解构取消保护cookie的代码来解决问题.我已将以下代码添加到我的dotnet核心应用程序上的控制器:

I've been playing around a bit more by trying to deconstruct the code that unprotects the cookie. I've added the following code to a controller on my dotnet core app:

var formatVersion = 3;
var protector = _dataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "<my-app-name>", "v2");
var cookieValue = Request.Cookies["<my-app-name>"];
var cookieValueDecoded = Base64UrlTextEncoder.Decode(cookieValue);
var unprotectedBytes = protector.Unprotect(cookieValueDecoded);

using (MemoryStream memoryStream = new MemoryStream(unprotectedBytes))
{
    using (GZipStream gzipStream = new GZipStream((Stream)memoryStream, CompressionMode.Decompress))
    {
        using (BinaryReader reader = new BinaryReader((Stream) gzipStream))
        {
            if (reader.ReadInt32() != formatVersion) return (AuthenticationTicket) null;
            string authenticationType = reader.ReadString();

            string str1 = ReadWithDefault(reader, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
            string roleType = ReadWithDefault(reader, "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
            int length = reader.ReadInt32();
            Claim[] claimArray = new Claim[length];
            for (int index = 0; index != length; ++index)
            {
                string type = ReadWithDefault(reader, str1);
                string str2 = reader.ReadString();
                string valueType = ReadWithDefault(reader, "http://www.w3.org/2001/XMLSchema#string");
                string str3 = ReadWithDefault(reader, "LOCAL AUTHORITY");
                string originalIssuer = ReadWithDefault(reader, str3);
                claimArray[index] = new Claim(type, str2, valueType, str3, originalIssuer);
            }
            ClaimsIdentity identity = new ClaimsIdentity((IEnumerable<Claim>)claimArray, authenticationType, str1, roleType);
        }
    }
}

大多数此代码来自 Microsoft.Owin.Security,Version = 3.0.1 中的 Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer .因此,它本质上是原始ASP.NET应用程序中使用的保护逻辑的逆转,并且可以正常工作.它以 ClaimsIdentity 结尾,该代码与在另一个应用程序上进行身份验证的帐户匹配.这告诉我,应用程序之间的密码配置和密钥是匹配的.

Most of this code comes from Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer in Microsoft.Owin.Security, Version=3.0.1. It is therefore essentially a reversal of the protection logic that is used in the originating ASP.NET app, and it works fine. It ends with a ClaimsIdentity that matches the account which authenticated on the other app. This tells me that the cryptography config and keys are matched between the apps.

因此,内置代码无法在两侧保护身份验证cookie,因此它们之间还必须存在其他区别.但是我不清楚如何诊断差异在哪里.我的假设是,我在dotnet核心方面错过了一些使cookie身份验证具有互操作性的东西.

So there must be some other difference between the built-in code that unprotects the authentication cookie on both sides. But I'm unclear about how to diagnose where the difference is. My assumption is that I've missed something on the dotnet core side which makes the cookie authentication interoperable.

更新2

还有更多的挖掘,我认为这可以归结为数据序列化器格式版本.在我的dotnet核心应用程序中,如果我深入研究 TicketDataFormat ,我会看到它使用了 TicketSerializer.Default ,它是 IDataSerializer< AuthenticationTicket> 的实现,具有硬编码的 5 FormatVersion . TicketSerializer 顶部还有一条评论说:

Having dug around a bit more I think this comes down to the data serializer format version. In my dotnet core app if I dig into the TicketDataFormat I see it uses TicketSerializer.Default which is an implementation of IDataSerializer<AuthenticationTicket> that has a hard-coded FormatVersion of 5. There's also a comment at the top of TicketSerializer saying:

必须与Microsoft.Owin.Security.Interop.AspNetTicketSerializer保持同步

This MUST be kept in sync with Microsoft.Owin.Security.Interop.AspNetTicketSerializer

但是,您可以在上面的UPDATE 1中看到,当我从ASP.NET Web应用程序中提取一些序列化逻辑时,它正在使用格式版本3.请注意,此应用程序正在使用< Microsoft.Owin.Security,Version = 3.0.1.0 附带的code> TicketDataFormat ,并且该程序集具有 TicketSerializer ,其中包含硬编码的 FormatVersion 3 .

However you can see in my UPDATE 1 above that when I ripped out some of the serialization logic from the ASP.NET web app, it is working with a format version of 3. Note that this app is using the version of TicketDataFormat that comes with Microsoft.Owin.Security, Version=3.0.1.0 and that assembly has a TicketSerializer with hard-coded FormatVersion of 3.

那么,如何确保这些序列化器在两侧都兼容?

So, how can I keep ensure these serializers are compatible on both sides?

更新3

意识到我是一个综合工具,并且缺少Microsoft文档的关键部分.在上面我说:

Realised I'm a total tool and was missing a key part of the Microsoft docs. Above I state this:

我还根据以下文档使用此数据保护器分配了票证数据格式: new TicketDataFormat(dataProtector).

好吧,实际上我应该一直在使用 Microsoft.Owin.Security.Interop 库提供的 AspNetTicketDataFormat 类型.更正此问题后,我现在可以使用以下内容在我的dotnet核心应用中获取索赔主体:

Well, actually I should have been using the AspNetTicketDataFormat type provided by the Microsoft.Owin.Security.Interop library. Having corrected this I can now obtain a claim principal in my dotnet core app using the following:

var dataProtector = _dataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "<my-app-name>", "v2");

var ticketDataFormat = new TicketDataFormat(dataProtector);

var ticket = ticketDataFormat.Unprotect(cookieValue, "");

在这里我可以看到 ticket.Principal.Identity 填充了我从cookie中获得的身份.

Here I can see ticket.Principal.Identity populated with my identity from the cookie.

但是,我仍然无法使我的应用程序处于经过身份验证的状态.我显然仍然无法正确连接cookie身份验证中间件.我的 Startup 仍然看起来像我原始帖子中的第二个代码块.如果有人可以帮助,感觉就像是最后的障碍.

However, I still can't get my app in an authenticated state. I'm clearly still not wiring up the cookie authentication middleware correctly. My Startup still just looks like the second code block in my original post. Feels like the final hurdle if anyone can help.

推荐答案

最终,当我到达Update 3时,我已经进行了足够长的时间,以至于该会话已经过期,但是我仍在使用该会话进行测试.因此,我的Cookie被拒绝的原因是会话过期,而不是编码问题.我在将跟踪级别的日志记录添加到应用程序后就发现了这一点,并且有了答案,从控制台盯着我看!万一帖子中描述的过程使其他任何人受益,都将保留在这里.

Eventually by the time I reached Update 3 I had been working on this long enough that the session had expired but I was still using that session to test. So the reason my cookie was being rejected was due to session expiry, not some coding issue. I discovered this once I added trace-level logging to the app and there was the answer, staring at me from the console! Will leave this here in case the process described in the post benefits anyone else.

这篇关于无法与新的dotnet核心Web应用程序共享身份验证cookie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆