ASP.NET_SessionId + OWIN Cookies do not send to browser

后端 未结 9 1252
误落风尘
误落风尘 2020-11-22 14:00

I have a strange problem with using Owin cookie authentication.

When I start my IIS server authentication works perfectly fine on IE/Firefox and Chrome.

I st

相关标签:
9条回答
  • 2020-11-22 14:14

    I faced the Similar Issue with Visual Studio 2017 and .net MVC 5.2.4, Updating Nuget Microsoft.Owin.Security.Google to lastest version which currently is 4.0.1 worked for me! Hope this Helps someone!

    0 讨论(0)
  • 2020-11-22 14:20

    Katana team answered to the issue Tomas Dolezar raised, and posted documentation about workarounds:

    Workarounds fall into two categories. One is to re-configure System.Web so it avoids using the Response.Cookies collection and overwriting the OWIN cookies. The other approach is to re-configure the affected OWIN components so they write cookies directly to System.Web's Response.Cookies collection.

    • Ensure session is established prior to authentication: The conflict between System.Web and Katana cookies is per request, so it may be possible for the application to establish the session on some request prior to the authentication flow. This should be easy to do when the user first arrives, but it may be harder to guarantee later when the session or auth cookies expire and/or need to be refreshed.
    • Disable the SessionStateModule - If the application is not relying on session information, but the session module is still setting a cookie that causes the above conflict, then you may consider disabling the session state module.
    • Reconfigure the CookieAuthenticationMiddleware to write directly to System.Web's cookie collection.
    app.UseCookieAuthentication(new CookieAuthenticationOptions
                                    {
                                        // ...
                                        CookieManager = new SystemWebCookieManager()
                                    });
    

    See SystemWebCookieManager implementation from the documentation (link above)

    More information here

    Edit

    Below the steps we took to solve the issue. Both 1. and 2. solved the problem also separately but we decided to apply both just in case:

    1. Use SystemWebCookieManager

    2. Set the session variable:

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
    
        // See http://stackoverflow.com/questions/20737578/asp-net-sessionid-owin-cookies-do-not-send-to-browser/
        requestContext.HttpContext.Session["FixEternalRedirectLoop"] = 1;
    }
    

    (side note: the Initialize method above is the logical place for the fix because base.Initialize makes Session available. However, the fix could also be applied later because in OpenId there's first an anonymous request, then redirect to the OpenId provider and then back to the app. The problems would occur after the redirect back to the app while the fix sets the session variable already during the first anonymous request thus fixing the problem before any redirect back even happens)

    Edit 2

    Copy-paste from the Katana project 2016-05-14:

    Add this:

    app.UseCookieAuthentication(new CookieAuthenticationOptions
                                    {
                                        // ...
                                        CookieManager = new SystemWebCookieManager()
                                    });
    

    ...and this:

    public class SystemWebCookieManager : ICookieManager
    {
        public string GetRequestCookie(IOwinContext context, string key)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
    
            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
            var cookie = webContext.Request.Cookies[key];
            return cookie == null ? null : cookie.Value;
        }
    
        public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
    
            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
    
            bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
            bool pathHasValue = !string.IsNullOrEmpty(options.Path);
            bool expiresHasValue = options.Expires.HasValue;
    
            var cookie = new HttpCookie(key, value);
            if (domainHasValue)
            {
                cookie.Domain = options.Domain;
            }
            if (pathHasValue)
            {
                cookie.Path = options.Path;
            }
            if (expiresHasValue)
            {
                cookie.Expires = options.Expires.Value;
            }
            if (options.Secure)
            {
                cookie.Secure = true;
            }
            if (options.HttpOnly)
            {
                cookie.HttpOnly = true;
            }
    
            webContext.Response.AppendCookie(cookie);
        }
    
        public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
    
            AppendResponseCookie(
                context,
                key,
                string.Empty,
                new CookieOptions
                {
                    Path = options.Path,
                    Domain = options.Domain,
                    Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                });
        }
    }
    
    0 讨论(0)
  • 2020-11-22 14:22

    Answers have been provided already, but in owin 3.1.0, there is a SystemWebChunkingCookieManager class that can be used.

    https://github.com/aspnet/AspNetKatana/blob/dev/src/Microsoft.Owin.Host.SystemWeb/SystemWebChunkingCookieManager.cs

    https://raw.githubusercontent.com/aspnet/AspNetKatana/c33569969e79afd9fb4ec2d6bdff877e376821b2/src/Microsoft.Owin.Host.SystemWeb/SystemWebChunkingCookieManager.cs

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        ...
        CookieManager = new SystemWebChunkingCookieManager()
        ...
    });
    
    0 讨论(0)
  • 2020-11-22 14:22

    The fastest one-line code solution:

    HttpContext.Current.Session["RunSession"] = "1";
    

    Just add this line before CreateIdentity method:

    HttpContext.Current.Session["RunSession"] = "1";
    var userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
    _authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = rememberLogin }, userIdentity);
    
    0 讨论(0)
  • 2020-11-22 14:33

    In short, the .NET cookie manager will win over the OWIN cookie manager and overwrite cookies set on the OWIN layer. The fix is to use the SystemWebCookieManager class, provided as a solution on the Katana Project here. You need to use this class or one similar to it, which will force OWIN to use the .NET cookie manager so there are no inconsistencies:

    public class SystemWebCookieManager : ICookieManager
    {
        public string GetRequestCookie(IOwinContext context, string key)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
    
            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
            var cookie = webContext.Request.Cookies[key];
            return cookie == null ? null : cookie.Value;
        }
    
        public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
    
            var webContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
    
            bool domainHasValue = !string.IsNullOrEmpty(options.Domain);
            bool pathHasValue = !string.IsNullOrEmpty(options.Path);
            bool expiresHasValue = options.Expires.HasValue;
    
            var cookie = new HttpCookie(key, value);
            if (domainHasValue)
            {
                cookie.Domain = options.Domain;
            }
            if (pathHasValue)
            {
                cookie.Path = options.Path;
            }
            if (expiresHasValue)
            {
                cookie.Expires = options.Expires.Value;
            }
            if (options.Secure)
            {
                cookie.Secure = true;
            }
            if (options.HttpOnly)
            {
                cookie.HttpOnly = true;
            }
    
            webContext.Response.AppendCookie(cookie);
        }
    
        public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
    
            AppendResponseCookie(
                context,
                key,
                string.Empty,
                new CookieOptions
                {
                    Path = options.Path,
                    Domain = options.Domain,
                    Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                });
        }
    }
    

    In your application startup, just assign it when you create your OWIN dependencies:

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        ...
        CookieManager = new SystemWebCookieManager()
        ...
    });
    

    A similar answer has been provided here but it does not include all of the code-base required to solve the problem, so I see a need to add it here because the external link to the Katana Project may go down and this should be fully chronicled as a solution here as well.

    0 讨论(0)
  • 2020-11-22 14:35

    Starting with the great analysis by @TomasDolezal, I had a look at both the Owin and the System.Web source.

    The problem is that System.Web has its own master source of cookie information and that isn't the Set-Cookie header. Owin only knows about the Set-Cookie header. A workaround is to make sure that any cookies set by Owin are also set in the HttpContext.Current.Response.Cookies collection.

    I've made a small middleware (source, nuget) that does exactly that, which is intended to be placed immediately above the cookie middleware registration.

    app.UseKentorOwinCookieSaver();
    
    app.UseCookieAuthentication(new CookieAuthenticationOptions());
    
    0 讨论(0)
提交回复
热议问题