ASP.NET_SessionId + OWIN Pliki cookie nie są wysyłane do przeglądarki

145

Mam dziwny problem z uwierzytelnianiem cookie Owin.

Kiedy uruchamiam, uwierzytelnianie serwera IIS działa doskonale w IE / Firefox i Chrome.

Zacząłem testować z uwierzytelnianiem i logować się na różnych platformach i napotkałem dziwny błąd. Sporadycznie framework / IIS Owin po prostu nie wysyła żadnych plików cookie do przeglądarek. Wpiszę nazwę użytkownika i hasło, które są poprawne, kod działa, ale żaden plik cookie nie jest w ogóle dostarczany do przeglądarki. Jeśli zrestartuję serwer, zacznie działać, to w pewnym momencie spróbuję się zalogować i ponownie pliki cookie przestają być dostarczane. Przechodzenie przez kod nic nie robi i nie powoduje żadnych błędów.

 app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Active,
            CookieHttpOnly = true,
            AuthenticationType = "ABC",
            LoginPath = new PathString("/Account/Login"),
            CookiePath = "/",
            CookieName = "ABC",
            Provider = new CookieAuthenticationProvider
               {
                  OnApplyRedirect = ctx =>
                  {
                     if (!IsAjaxRequest(ctx.Request))
                     {
                        ctx.Response.Redirect(ctx.RedirectUri);
                     }
                 }
               }
        });

W ramach procedury logowania mam następujący kod:

IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
                            authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

var authentication = HttpContext.Current.GetOwinContext().Authentication;
var identity = new ClaimsIdentity("ABC");
identity.AddClaim(new Claim(ClaimTypes.Name, user.Username));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.User_ID.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Role, role.myRole.ToString()));
    authentication.AuthenticationResponseGrant =
        new AuthenticationResponseGrant(identity, new AuthenticationProperties()
                                                   {
                                                       IsPersistent = isPersistent
                                                   });

authenticationManager.SignIn(new AuthenticationProperties() {IsPersistent = isPersistent}, identity);

Aktualizacja 1: Wygląda na to, że jedną z przyczyn problemu jest to, że dodając elementy do sesji, zaczynają się problemy. Session.Content["ABC"]= 123Wydaje się, że dodanie czegoś prostego, na przykład, powoduje problem.

Oto co mogę zobaczyć: 1) (Chrome) Kiedy loguję się, otrzymuję identyfikator ASP.NET_SessionId + mój plik cookie uwierzytelniania. 2) Przechodzę do strony, która ustawia sesję. Zawartość ... 3) Otwórz nową przeglądarkę (Firefox) i spróbuj się zalogować, ale nie otrzymuje ona identyfikatora ASP.NET_SessionId ani pliku cookie uwierzytelniania 4) Podczas gdy pierwsza przeglądarka ma ASP.NET_SessionId i nadal działa. W chwili, gdy usuwam ten plik cookie, ma ten sam problem, co wszystkie inne przeglądarki, na których pracuję, na adresie IP (10.xxx) i localhost.

Aktualizacja 2: Wymuś utworzenie ASPNET_SessionIdpierwszego na mojej stronie login_load przed uwierzytelnieniem za pomocą OWIN.

1) zanim uwierzytelnię się w OWIN, tworzę losową Session.Contentwartość na mojej stronie logowania, aby uruchomić ASP.NET_SessionId 2) następnie uwierzytelniam się i wykonuję kolejne sesje 3) Inne przeglądarki wydają się teraz działać

To jest dziwne. Mogę tylko stwierdzić, że ma to coś wspólnego z ASP i OWIN myślącymi, że są w różnych domenach lub czymś w tym rodzaju.

Aktualizacja 3 - Dziwne zachowanie między nimi.

Zidentyfikowano dodatkowe dziwne zachowanie - limit czasu sesji Owin i ASP jest inny. Widzę, że moje sesje Owin są dłużej żywe niż moje sesje ASP dzięki jakimś mechanizmom. Tak więc podczas logowania: 1.) Mam sesję uwierzytelniania opartą na plikach cookie 2.) Ustawiłem kilka zmiennych sesji

Moje zmienne sesji (2) „umierają”, zanim zmienna sesji pliku cookie owin wymusza ponowne zalogowanie, co powoduje nieoczekiwane zachowanie w całej mojej aplikacji. (Osoba jest zalogowana, ale tak naprawdę nie jest zalogowana)

Zaktualizuj 3B

Po pewnym czasie zobaczyłem na stronie kilka komentarzy, w których stwierdzono, że limit czasu uwierzytelniania „formularzy” i limit czasu sesji muszą się zgadzać. Myślę, że normalnie te dwa są zsynchronizowane, ale z jakiegoś powodu nie są zsynchronizowane.

Podsumowanie obejść

1) Zawsze najpierw utwórz sesję przed uwierzytelnieniem. Zasadniczo utwórz sesję podczas uruchamiania aplikacjiSession["Workaround"] = 0;

2) [Eksperymentalne], jeśli utrzymujesz pliki cookie, upewnij się, że limit czasu / długość OWIN jest dłuższy niż sessionTimeout w pliku web.config (w testowaniu)

Piotr Stuliński
źródło
1
Potwierdza, że ​​dodanie wywołania sesji do ActionResult Login i ActionResult ExternalLogin rozwiązało ten problem. Jestem pewien, że potrzebny jest tylko jeden, ale mam oba na miejscu.
Scott
Dziękuję! ... Dodanie sesji do zewnętrznego logowania naprawiło to za mnie ... to jest magia voodoo ... zmarnowałem już 6 godzin, aby znaleźć ten problem ...
xdev

Odpowiedzi:

159

Napotkałem ten sam problem i prześledziłem przyczynę implementacji hostingu OWIN ASP.NET. Powiedziałbym, że to błąd.

Jakieś tło

Moje ustalenia są oparte na tych wersjach zestawu:

  • Microsoft.Owin, wersja = 2.0.2.0, kultura = neutralna, PublicKeyToken = 31bf3856ad364e35
  • Microsoft.Owin.Host.SystemWeb, wersja = 2.0.2.0, kultura = neutralna, PublicKeyToken = 31bf3856ad364e35
  • System.Web, wersja = 4.0.0.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a

OWIN używa własnej abstrakcji do pracy z odpowiedziowymi plikami cookie ( Microsoft.Owin.ResponseCookieCollection ). Ta implementacja bezpośrednio otacza kolekcję nagłówków odpowiedzi i odpowiednio aktualizuje nagłówek Set-Cookie . Host OWIN ASP.NET ( Microsoft.Owin.Host.SystemWeb ) po prostu opakowuje System.Web.HttpResponse i jego kolekcję nagłówków. Więc kiedy nowy plik cookie jest tworzony przez OWIN, nagłówek odpowiedzi Set-Cookie jest zmieniany bezpośrednio.

Ale ASP.NET używa również własnej abstrakcji do pracy z plikami cookie odpowiedzi. Jest to widoczne dla nas jako właściwość System.Web.HttpResponse.Cookies i implementowane przez zapieczętowaną klasę System.Web.HttpCookieCollection . Ta implementacja nie zawija bezpośrednio nagłówka odpowiedzi Set-Cookie, ale wykorzystuje pewne optymalizacje i kilka wewnętrznych powiadomień, aby zamanifestować swój zmieniony stan w obiekcie odpowiedzi.

Następnie występuje punkt późny w okresie istnienia żądania, w którym HttpCookieCollection zmieniony stan jest testowany ( System.Web.HttpResponse.GenerateResponseHeadersForCookies () ), a pliki cookie są serializowane do nagłówka Set-Cookie . Jeśli ta kolekcja jest w określonym stanie, cały nagłówek Set-Cookie jest najpierw czyszczony i odtwarzany z plików cookie przechowywanych w kolekcji.

Implementacja sesji ASP.NET wykorzystuje właściwość System.Web.HttpResponse.Cookies do przechowywania swojego pliku cookie ASP.NET_SessionId. Istnieje również pewna podstawowa optymalizacja w module stanu sesji ASP.NET ( System.Web.SessionState.SessionStateModule ) zaimplementowana za pomocą statycznej właściwości o nazwie s_sessionEverSet, co jest dość oczywiste. Jeśli kiedykolwiek zapiszesz coś do stanu sesji w swojej aplikacji, ten moduł wykona trochę więcej pracy dla każdego żądania.


Wróćmy do naszego problemu z logowaniem

Dzięki tym wszystkim elementom można wyjaśnić swoje scenariusze.

Przypadek 1 - Sesja nigdy nie została ustawiona

Właściwość System.Web.SessionState.SessionStateModule , s_sessionEverSet ma wartość false. Identyfikatory sesji nie są generowane przez moduł stanu sesji, a stan kolekcji System.Web.HttpResponse.Cookies nie jest wykrywany jako zmieniony . W takim przypadku ciasteczka OWIN są poprawnie wysyłane do przeglądarki i logowanie działa.

Przypadek 2 - sesja została użyta gdzieś w aplikacji, ale nie zanim użytkownik spróbuje się uwierzytelnić

Właściwość System.Web.SessionState.SessionStateModule , s_sessionEverSet ma wartość true. Identyfikatory sesji są generowane przez SessionStateModule , ASP.NET_SessionId jest dodawany do kolekcji System.Web.HttpResponse.Cookies, ale jest usuwany później w czasie życia żądania, ponieważ sesja użytkownika jest w rzeczywistości pusta. W tym przypadku stan kolekcji System.Web.HttpResponse.Cookies jest wykrywany jako zmieniony, a nagłówek Set-Cookie jest najpierw czyszczony, zanim pliki cookie zostaną serializowane do wartości nagłówka.

W tym przypadku pliki cookie odpowiedzi OWIN są „tracone”, a użytkownik nie jest uwierzytelniany i zostaje przekierowany z powrotem na stronę logowania.

Przypadek 3 - sesja jest używana przed próbą uwierzytelnienia przez użytkownika

Właściwość System.Web.SessionState.SessionStateModule , s_sessionEverSet ma wartość true. Identyfikatory sesji są generowane przez SessionStateModule , ASP.NET_SessionId jest dodawany do System.Web.HttpResponse.Cookies . Ze względu na wewnętrzną optymalizację w System.Web.HttpCookieCollection i System.Web.HttpResponse.GenerateResponseHeadersForCookies () nagłówek Set-Cookie NIE jest najpierw usuwany, ale tylko aktualizowany.

W tym przypadku zarówno pliki cookie uwierzytelniania OWIN, jak i pliki cookie ASP.NET_SessionId są wysyłane w odpowiedzi i logowanie działa.


Bardziej ogólny problem z plikami cookie

Jak widać problem jest bardziej ogólny i nie ogranicza się do sesji ASP.NET. Jeśli hostujesz OWIN za pośrednictwem Microsoft.Owin.Host.SystemWeb i Ty / coś bezpośrednio korzysta z kolekcji System.Web.HttpResponse.Cookies, jesteś zagrożony.

Na przykład to działa i oba pliki cookie są poprawnie wysyłane do przeglądarki ...

public ActionResult Index()
{
    HttpContext.GetOwinContext()
        .Response.Cookies.Append("OwinCookie", "SomeValue");
    HttpContext.Response.Cookies["ASPCookie"].Value = "SomeValue";

    return View();
}

Ale tak się nie dzieje i OwinCookie jest „zagubione” ...

public ActionResult Index()
{
    HttpContext.GetOwinContext()
        .Response.Cookies.Append("OwinCookie", "SomeValue");
    HttpContext.Response.Cookies["ASPCookie"].Value = "SomeValue";
    HttpContext.Response.Cookies.Remove("ASPCookie");

    return View();
}

Oba przetestowano z VS2013, IISExpress i domyślnego szablonu projektu MVC.

Tomas Dolezal
źródło
7
Spędziłem kilka dni próbując debugować i rozwiązać ten problem w naszym środowisku testowym. Jedyne obejście, jakie znalazłem, jest takie samo, jak sugerowałeś (ustawienie sesji przed uwierzytelnieniem użytkownika). Zgłosiłem problem do katanaproject ... katanaproject.codeplex.com/workitem/197 , więc może ktoś tam skomentuje.
Tomas Dolezal,
11
To dość poważna wada, zwłaszcza, że ​​spakowali się w szablonie vs2013.
Piotr Stuliński
2
Dla każdego, kto chce to dokładniej zbadać, stworzyłem projekt testowy pod adresem github.com/Neilski/IdentityBugDemo
Neilski
1
Wystąpił ten problem z powodu użycia Controller.TempData, który używa sesji jako magazynu zapasowego. Może łatwo odtworzyć brak możliwości logowania, jeśli plik cookie ASP_NET.SessionId nie istnieje z poprzedniego żądania.
kingdango
2
Wreszcie! Cóż to za dziwna sprawa. Dziękuję Ci. Jest to nadal problem ponad dwa lata po napisaniu tej odpowiedzi.
Spivonious
43

Rozpoczynając od świetnej analizy autorstwa @TomasDolezal, przyjrzałem się zarówno źródłu Owin, jak i System.Web.

Problem polega na tym, że System.Web ma własne główne źródło informacji o plikach cookie, a nie jest to nagłówek Set-Cookie. Owin wie tylko o nagłówku Set-Cookie. Obejściem problemu jest upewnienie się, że wszystkie pliki cookie ustawione przez Owin są również ustawione w HttpContext.Current.Response.Cookieskolekcji.

Stworzyłem małe oprogramowanie pośredniczące ( źródło , nuget ), które robi dokładnie to, które ma być umieszczone bezpośrednio nad rejestracją oprogramowania pośredniczącego plików cookie.

app.UseKentorOwinCookieSaver();

app.UseCookieAuthentication(new CookieAuthenticationOptions());
Anders Abel
źródło
1
Spróbuje. Ponieważ po asp.net Identity 2.2.0-alpha1 zacząłem mieć problemy nie tylko z logowaniem, ale także z wylogowaniem użytkownika (użytkownik nie wyloguje się po wylogowaniu kliknij | ogólnie, w przypadku gdy zostawiłem stronę otwartą na jakiś czas bez robienia czegokolwiek |) .. A po ustawieniu sesji tuż przed zalogowaniem się użytkownika rozwiązałem problem z logowaniem, ale problem z wylogowaniem nie ustąpi .. Dzięki za twój wysiłek .. Swoją drogą, czy jest coś co powinienem zrobić poza instalacją paczka?
wooer
Musisz go aktywować app.UseKentorCookieMiddlewareSaver();w Startup.Auth.cs. Powinien również obsługiwać usuwanie plików cookie podczas wylogowywania.
Anders Abel
Dziękuję bardzo Anders Abel, zarówno logowanie, jak i wylogowanie działają teraz dobrze. Ale kod w powyższym komentarzu musi zostać zmieniony (ponieważ postępowałem zgodnie z nim :) bez żadnego sukcesu), aby był: app.UseKentorOwinCookieSaver()i być może zawarty w oryginalnej odpowiedzi, jak na stronie GitHub pakietu .
wooer
1
Dzięki za zauważenie niewłaściwego dokumentu. W rzeczywistości zostało to już naprawione na stronie GitHub, ale zaktualizowałem również moją odpowiedź tutaj.
Anders Abel
@AndersAbel Próbuję dodać rejestracje Meetup dla tego projektu github: github.com/owin-middleware/OwinOAuthProviders ''. Dodałem Asanę dopiero niedawno i nie miałem żadnych problemów, ale z jakiegoś powodu w przypadku Meetup metoda await AuthenticationManager.GetExternalLoginInfoAsync () na koncie // ExternalLoginCallback zwraca wartość null. Niestety, Twój pakiet NuGet nie rozwiązał mojego problemu. Zastanawiałem się, czy miałeś trochę czasu na omówienie ze mną, ponieważ być może będziesz w stanie lepiej rozwiązać problem i przejść do swojego projektu.
Anthony Ruffino
42

Krótko mówiąc, menedżer plików cookie .NET wygra z menedżerem plików cookie OWIN i nadpisze pliki cookie ustawione w warstwie OWIN . Poprawka polega na użyciu klasy SystemWebCookieManager, dostarczonej jako rozwiązanie w projekcie Katana tutaj . Musisz użyć tej lub podobnej klasy, co zmusi OWIN do korzystania z menedżera plików cookie .NET, aby nie było niespójności :

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),
            });
    }
}

Podczas uruchamiania aplikacji po prostu przypisz ją podczas tworzenia zależności OWIN:

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

Podano podobną odpowiedź, ale nie obejmuje ona całej bazy kodu wymaganej do rozwiązania problemu, więc widzę potrzebę dodania jej tutaj, ponieważ zewnętrzny link do projektu Katana może zostać uszkodzony i powinno to być w pełni opisane jako rozwiązanie również tutaj.

Alexandru
źródło
dzięki, jego praca mnie, ale także wyczyść całą sesję, wywołując to ControllerContext.HttpContext.Session.RemoveAll (); w funkcji externallogincallback
adnan
Czy dotyczy ASP.NET Webforms 4.6.1 ? Moja aplikacja internetowa używaASP.NET Webforms, OWIN, ADFS
Kiquenet,
@Kiquenet Czy Twoja aplikacja internetowa używa plików cookie OWIN? W takim razie tak.
Alexandru,
W kodzie Startup.ConfigureAuthmamy app.UseCookieAuthenticationi app.UseWsFederationAuthenticationwreszcie app.UseStageMarker
Kiquenet
@Alexandru Możesz rozważyć edycję, mój zespół napotkał ten błąd i był rzadki i losowy, ukrywał się przed nami poprzez środowiska DEV i UAT. Ten cytat z Twojej odpowiedzi nie był dla nas odpowiedni: „Menedżer plików cookie .NET zawsze wygrywa”. Łatwo byłoby to znaleźć i naprawić, gdyby pliki cookie OWIN były zawsze nadpisywane, żadne z naszych programów pośredniczących OIDC nie zniknęłoby z naszych stacji roboczych deweloperów. Ale losowość oznaczała, że ​​błąd dotarł do produkcji przez 2 dni, zanim uderzył w nas na dużą skalę (połowa naszych wewnętrznych zastosowań nie mogła zalogować się przez AAD). Masz ochotę usunąć słowo „zawsze” z Twojej odpowiedzi?
yzorg
17

Zespół Katana odpowiedział na problem podniesiony przez Tomasa Dolezara i opublikował dokumentację dotyczącą obejść :

Obejścia dzielą się na dwie kategorie. Jednym z nich jest ponowna konfiguracja System.Web, aby uniknąć używania kolekcji Response.Cookies i nadpisywania plików cookie OWIN. Innym podejściem jest rekonfiguracja składników OWIN, których dotyczy problem, tak, aby zapisywały pliki cookie bezpośrednio w kolekcji Response.Cookies System.Web.

  • Upewnij się, że sesja została ustanowiona przed uwierzytelnieniem: Konflikt między plikami cookie System.Web i Katana występuje na żądanie, więc aplikacja może mieć możliwość ustanowienia sesji na żądanie przed przepływem uwierzytelniania. Powinno to być łatwe do zrobienia, gdy użytkownik pojawi się po raz pierwszy, ale później może być trudniej zagwarantować, że sesja lub pliki cookie uwierzytelniania wygasną i / lub będą wymagać odświeżenia.
  • Wyłącz moduł SessionStateModule - jeśli aplikacja nie polega na informacjach o sesji, ale moduł sesji nadal ustawia plik cookie, który powoduje powyższy konflikt, możesz rozważyć wyłączenie modułu stanu sesji.
  • Skonfiguruj ponownie CookieAuthenticationMiddleware, aby zapisywał bezpośrednio do zbioru plików cookie System.Web.
app.UseCookieAuthentication(new CookieAuthenticationOptions
                                {
                                    // ...
                                    CookieManager = new SystemWebCookieManager()
                                });

Zobacz implementację SystemWebCookieManager z dokumentacji (link powyżej)

Więcej informacji tutaj

Edytować

Poniżej czynności, które podjęliśmy, aby rozwiązać problem. Zarówno 1., jak i 2. rozwiązały problem również osobno, ale na wszelki wypadek zdecydowaliśmy się zastosować oba:

1. Użyj SystemWebCookieManager

2. Ustaw zmienną sesji:

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;
}

(uwaga na marginesie: powyższa metoda Initialize jest logicznym miejscem dla poprawki, ponieważ base.Initialize udostępnia sesję. Jednak poprawkę można zastosować później, ponieważ w OpenId najpierw jest anonimowe żądanie, a następnie przekierowanie do dostawcy OpenId, a następnie z powrotem do aplikacji. Problemy wystąpiłyby po przekierowaniu z powrotem do aplikacji, podczas gdy poprawka ustawia zmienną sesji już podczas pierwszego anonimowego żądania, rozwiązując w ten sposób problem, zanim jeszcze nastąpi jakiekolwiek przekierowanie)

Edytuj 2

Kopiuj i wklej z projektu Katana 14.05.2016:

Dodaj:

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

...i to:

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),
            });
    }
}
thomius
źródło
Uważam, że ta odpowiedź jest znacznie prostsza i łatwiejsza do rozwiązania problemu. Dzięki - Może przemówiłem zbyt nagle. To nie rozwiązało mojego problemu.
JCS
@JCS Zawiera kroki, które podjęliśmy, aby rozwiązać problem. Czy dowiedziałeś się, czy Twój problem był powiązany?
thomius
Używam Web Api 2 + Owin middleware + redis cache do zarządzania sesjami w celu uwierzytelnienia. Próbowałem użyć SystemWebCookieManager i nie rozwiązało to problemu, który miałem, gdzie nie ustawiano plików cookie uwierzytelniania. Użycie „UseKentorOwinCookieSaver” rozwiązało problem, ale nie przepadam za dodatkowymi zewnętrznymi zależnościami ...
JCS
Czyszczenie sesji działało dla mnie. Nie jest wymagana zewnętrzna zależność. Włącz to ControllerContext.HttpContext.Session.RemoveAll();do ExternalLogin()działania, zanim zadzwonisz ChallengeResult(). Nie wiem, czy to najlepsze rozwiązanie, ale jest najprostsze.
Alisson
1
@chemitaxis jasne, po prostu zwróć uwagę, że ?.(operator warunkowy zerowy) działa tylko w C # 6.
Alisson
5

Odpowiedzi zostały już dostarczone, ale w owinie 3.1.0 istnieje klasa SystemWebChunkingCookieManager, której można użyć.

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()
    ...
});
jonmeyer
źródło
Czy to nadal problem w wersji 3.1.0?
cyberconte
1
Tak, nadal jest to problem w wersji 3.1.0 i potrzebowałem tego menedżera plików cookie, ponieważ domyślnym nadal jest ChunkingCookieManager.
jonmeyer
może być używany gdzie? i jak?
Simon_Weaver,
@jonmeyer thanks. Myślę, że wczoraj przegapiłem rozróżnienie między SystemCCM i CCM, więc na pewno to
sprawdzę
nawet po dodaniu powyższej linii nie działa dla mnie. Używam wersji 3.1.0. Zwykle jestem w stanie zalogować się po raz pierwszy, ale po wylogowaniu nie pozwala mi się zalogować.
Mitin Dixit,
3

Jeśli sam ustawiasz pliki cookie w oprogramowaniu pośredniczącym OWIN, użycie OnSendingHeaderswydaje się rozwiązać problem.

Na przykład użycie poniższego kodu owinResponseCookie2zostanie ustawione, mimo że owinResponseCookie1nie jest:

private void SetCookies()
{
    var owinContext = HttpContext.GetOwinContext();
    var owinResponse = owinContext.Response;

    owinResponse.Cookies.Append("owinResponseCookie1", "value1");

    owinResponse.OnSendingHeaders(state =>
    {
        owinResponse.Cookies.Append("owinResponseCookie2", "value2");
    },
    null);

    var httpResponse = HttpContext.Response;
    httpResponse.Cookies.Remove("httpResponseCookie1");
}
Appetere
źródło
3

Podobny problem napotkałem w przypadku programu Visual Studio 2017 i .net MVC 5.2.4 , aktualizacja Nuget Microsoft.Owin.Security.Google do najnowszej wersji, która obecnie jest 4.0.1, zadziałała! Mam nadzieję, że to komuś pomoże!

Ikram Shah
źródło
1
Uratowałem mój bekon na tym! Wystąpił problem z przypadkową utratą uwierzytelnienia w Androidzie Chrome. Nic innego w tym wątku nie działało. Używam VS2019 i ASP MVC 5.
zfrank
2

Najszybsze rozwiązanie kodu jednowierszowego:

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

Po prostu dodaj tę linię przed metodą CreateIdentity:

HttpContext.Current.Session["RunSession"] = "1";
var userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
_authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = rememberLogin }, userIdentity);
Alexander Trofimov
źródło
1
Gdzie umieściłeś ten kod HttpContext.Current.Session["RunSession"] = "1";? w Globa.asax Session_Start ?
Kiquenet
1
W rzeczywistości jest to najprostsze i najszybsze dostępne rozwiązanie i dopóki rozwiązanie tego problemu nie zostanie uwzględnione we frameworku (już zapowiadałem, że tak będzie) - ja na przykład wolałbym jednowierszowy, zamiast klasy + wiązka zależności . To rozwiązanie jest niedoceniane IMHO.
Der Zinger
Dodałem go w moim AuthManager u góry metody IssueAuthToken
Alexander Trofimov
1

Miałem ten sam objaw, że nagłówek Set-Cookie nie był wysyłany, ale żadna z tych odpowiedzi nie pomogła. Wszystko działało na moim komputerze lokalnym, ale po wdrożeniu do produkcji nagłówki set-cookie nigdy nie zostały ustawione.

Okazuje się, że było to połączenie używania niestandardowego CookieAuthenticationMiddlewarez WebApi wraz z obsługą kompresji WebApi

Na szczęście korzystałem z ELMAH w moim projekcie, co pozwoliło mi zalogować się do tego wyjątku:

Serwer System.Web.HttpException Server nie może dołączyć nagłówka po wysłaniu nagłówków HTTP.

Co mnie do tego doprowadziło problemu z GitHubem

Zasadniczo, jeśli masz dziwną konfigurację, taką jak moja, będziesz chciał wyłączyć kompresję dla kontrolerów / metod WebApi, które ustawiają pliki cookie, lub wypróbować OwinServerCompressionHandler.

Hugh Jeffner
źródło