Estou com um problema estranho ao usar a autenticação de cookies Owin.
Quando inicio a autenticação do servidor IIS, funciona perfeitamente no IE / Firefox e Chrome.
Comecei a fazer alguns testes com autenticação e a fazer login em plataformas diferentes e vi um erro estranho. Esporadicamente, o framework Owin / IIS simplesmente não envia cookies para os navegadores. Vou digitar um nome de usuário e senha corretos, o código é executado, mas nenhum cookie é entregue ao navegador. Se eu reiniciar o servidor, ele começará a funcionar e, em algum momento, tentarei fazer login e novamente os cookies deixarão de ser entregues. Passar o código não faz nada e não gera erros.
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);
}
}
}
});
E dentro do meu procedimento de login, tenho o seguinte código:
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);
Atualização 1: Parece que uma das causas do problema é quando eu adiciono itens à sessão. Os problemas começam. Adicionar algo simples como Session.Content["ABC"]= 123
parece criar o problema.
O que eu consigo entender é o seguinte: 1) (Chrome) Ao fazer login, recebo o ASP.NET_SessionId + meu cookie de autenticação. 2) Vou para uma página que define uma sessão. Conteúdo ... 3) Abra um novo navegador (Firefox) e tente fazer o login e ele não recebe um ASP.NET_SessionId nem recebe um cookie de autenticação 4) Enquanto o primeiro navegador tem o ASP.NET_SessionId, ele continua funcionando. No momento em que removo este cookie, ele tem o mesmo problema que todos os outros navegadores em que estou trabalhando no endereço IP (10.xxx) e no localhost.
Atualização 2: force a criação da ASPNET_SessionId
primeira na minha página login_load antes da autenticação com o OWIN.
1) antes de me autenticar com o OWIN, faço um Session.Content
valor aleatório na minha página de login para iniciar o ASP.NET_SessionId 2) depois autentico e faço mais sessões 3) Outros navegadores parecem funcionar agora
Isso é bizarro. Só posso concluir que isso tem algo a ver com ASP e OWIN pensando que eles estão em domínios diferentes ou algo assim.
Atualização 3 - Comportamento estranho entre os dois.
Comportamento estranho adicional identificado - O tempo limite da sessão Owin e ASP é diferente. O que estou vendo é que minhas sessões Owin permanecem vivas por mais tempo do que minhas sessões ASP através de algum mecanismo. Portanto, ao fazer login: 1.) eu tenho uma sessão de autenticação baseada em cookied 2.) eu defini algumas variáveis de sessão
Minhas variáveis de sessão (2) "morrem" antes da variável de sessão do cookie owin força o login novamente, o que causa comportamento inesperado em todo o aplicativo. (A pessoa está logada, mas não está realmente logada)
Atualização 3B
Após algumas pesquisas, vi alguns comentários em uma página dizendo que o tempo limite da autenticação "forms" e o tempo limite da sessão precisam corresponder. Penso que normalmente os dois estão sincronizados, mas por qualquer motivo os dois não estão sincronizados.
Resumo das soluções alternativas
1) Crie sempre uma sessão antes da autenticação. Basicamente, crie uma sessão ao iniciar o aplicativoSession["Workaround"] = 0;
2) [Experimental] se você persistir com cookies, verifique se o tempo limite / duração do OWIN é maior que o tempo da sua sessão no seu web.config (em teste)
fonte
Respostas:
Eu encontrei o mesmo problema e rastreei a causa da implementação de hospedagem do ASP.NET do OWIN. Eu diria que é um bug.
Alguma experiência
Minhas descobertas são baseadas nessas versões de montagem:
O OWIN usa sua própria abstração para trabalhar com os cookies de resposta ( Microsoft.Owin.ResponseCookieCollection ). Essa implementação envolve diretamente a coleção de cabeçalhos de resposta e atualiza o cabeçalho Set-Cookie . O host OWIN ASP.NET ( Microsoft.Owin.Host.SystemWeb ) apenas envolve System.Web.HttpResponse e sua coleção de cabeçalhos. Portanto, quando um novo cookie é criado por meio do OWIN, o cabeçalho Set-Cookie da resposta é alterado diretamente.
Mas o ASP.NET também usa sua própria abstração para trabalhar com os cookies de resposta. Isso é exposto a nós como propriedade System.Web.HttpResponse.Cookies e implementado pela classe selada System.Web.HttpCookieCollection . Essa implementação não envolve diretamente o cabeçalho Set-Cookie da resposta, mas usa algumas otimizações e um punhado de notificações internas para manifestar seu estado alterado para o objeto de resposta.
Depois, há um ponto no final da vida útil da solicitação em que o estado alterado de HttpCookieCollection é testado ( System.Web.HttpResponse.GenerateResponseHeadersForCookies () ) e os cookies são serializados no cabeçalho Set-Cookie . Se essa coleção estiver em algum estado específico, o cabeçalho Set-Cookie inteiro será limpo e recriado primeiro a partir de cookies armazenados na coleção.
A implementação da sessão do ASP.NET usa a propriedade System.Web.HttpResponse.Cookies para armazenar seu cookie ASP.NET_SessionId. Também há alguma otimização básica no módulo de estado da sessão do ASP.NET ( System.Web.SessionState.SessionStateModule ) implementado através da propriedade estática chamada s_sessionEverSet, que é bastante autoexplicativa. Se você armazenar algo no estado da sessão em seu aplicativo, este módulo fará um pouco mais de trabalho para cada solicitação.
Voltar ao nosso problema de login
Com todas essas peças, seus cenários podem ser explicados.
Caso 1 - A sessão nunca foi definida
System.Web.SessionState.SessionStateModule , a propriedade s_sessionEverSet é falsa. Nenhuma identificação de sessão é gerada pelo módulo de estado da sessão e o estado da coleção System.Web.HttpResponse.Cookies não é detectado como alterado . Nesse caso, os cookies OWIN são enviados corretamente para o navegador e o login funciona.
Caso 2 - A sessão foi usada em algum lugar do aplicativo, mas não antes que o usuário tente autenticar
System.Web.SessionState.SessionStateModule , a propriedade s_sessionEverSet é verdadeira. Os IDs de sessão são gerados por SessionStateModule , o ASP.NET_SessionId é adicionado à coleção System.Web.HttpResponse.Cookies, mas é removido posteriormente na vida útil da solicitação, pois a sessão do usuário está realmente vazia. Nesse caso, o estado da coleção System.Web.HttpResponse.Cookies é detectado como alterado e o cabeçalho Set-Cookie é limpo primeiro antes que os cookies sejam serializados com o valor do cabeçalho.
Nesse caso, os cookies de resposta OWIN são "perdidos" e o usuário não é autenticado e é redirecionado de volta à página de login.
Caso 3 - A sessão é usada antes que o usuário tente se autenticar
System.Web.SessionState.SessionStateModule , a propriedade s_sessionEverSet é verdadeira. Os IDs de sessão são gerados pelo SessionStateModule , o ASP.NET_SessionId é adicionado ao System.Web.HttpResponse.Cookies . Devido à otimização interna no System.Web.HttpCookieCollection e System.Web.HttpResponse.GenerateResponseHeadersForCookies () , o cabeçalho Set-Cookie NÃO é limpo primeiro, mas é atualizado apenas.
Nesse caso, os cookies de autenticação OWIN e o ASP.NET_SessionId são enviados em resposta e o login funciona.
Problema mais geral com cookies
Como você pode ver, o problema é mais geral e não se limita à sessão do ASP.NET. Se você estiver hospedando o OWIN através do Microsoft.Owin.Host.SystemWeb e você / alguma coisa estiver usando diretamente a coleção System.Web.HttpResponse.Cookies, você estará em risco.
Por exemplo, isso funciona e os dois cookies são enviados corretamente ao navegador ...
Mas isso não acontece e o OwinCookie está "perdido" ...
Ambos testados no VS2013, IISExpress e modelo de projeto MVC padrão.
fonte
Começando com a ótima análise de @TomasDolezal, observei as fontes Owin e System.Web.
O problema é que o System.Web possui sua própria fonte principal de informações sobre cookies e esse não é o cabeçalho Set-Cookie. Owin conhece apenas o cabeçalho Set-Cookie. Uma solução alternativa é garantir que todos os cookies definidos por Owin também estejam definidos na
HttpContext.Current.Response.Cookies
coleção.Eu criei um pequeno middleware ( fonte , pepita ) que faz exatamente isso, que deve ser colocado imediatamente acima do registro do middleware do cookie.
fonte
app.UseKentorCookieMiddlewareSaver();
em Startup.Auth.cs. Ele também deve lidar com a limpeza de cookies de logout.app.UseKentorOwinCookieSaver()
e talvez incluído na sua resposta original, como na página GitHub do pacote .Em resumo, o gerenciador de cookies .NET conquistará o gerenciador de cookies OWIN e substituirá os cookies definidos na camada OWIN . A correção é usar a classe SystemWebCookieManager, fornecida como uma solução no Projeto Katana aqui . Você precisa usar essa classe ou semelhante a ela, o que forçará o OWIN a usar o gerenciador de cookies .NET para que não haja inconsistências :
Na inicialização do aplicativo, atribua-o ao criar suas dependências OWIN:
Uma resposta semelhante foi fornecida aqui, mas ela não inclui toda a base de código necessária para resolver o problema. Por isso, vejo a necessidade de adicioná-la aqui, pois o link externo para o Projeto Katana pode ficar inativo e isso deve ser totalmente registrado. como uma solução aqui também.
fonte
ASP.NET Webforms, OWIN, ADFS
Startup.ConfigureAuth
temosapp.UseCookieAuthentication
eapp.UseWsFederationAuthentication
finalmenteapp.UseStageMarker
A equipe de Katana respondeu ao problema levantado por Tomas Dolezar e postou a documentação sobre as soluções alternativas :
Consulte a implementação do SystemWebCookieManager na documentação (link acima)
Mais informações aqui
Editar
Abaixo os passos que tomamos para resolver o problema. 1. e 2. resolveram o problema também separadamente, mas decidimos aplicar os dois por precaução:
1. Use SystemWebCookieManager
2. Defina a variável da sessão:
(observação: o método Initialize acima é o local lógico da correção porque base.Initialize disponibiliza a Session. No entanto, a correção também pode ser aplicada posteriormente, porque no OpenId há primeiro uma solicitação anônima, depois redireciona para o provedor OpenId e depois volta Os problemas ocorreriam após o redirecionamento de volta para o aplicativo, enquanto a correção já define a variável de sessão durante a primeira solicitação anônima, corrigindo o problema antes que ocorra qualquer redirecionamento de volta)
Editar 2
Copie e cole do projeto Katana 14-05-2016:
Adicione isso:
...e isto:
fonte
ControllerContext.HttpContext.Session.RemoveAll();
em suaExternalLogin()
ação, antes de ligarChallengeResult()
. Não sei se é a melhor solução, mas é a mais simples.?.
operador nulo-condicional só funciona em C # 6. #As respostas já foram fornecidas, mas no owin 3.1.0, há uma classe SystemWebChunkingCookieManager que pode ser usada.
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
fonte
Se você está configurando cookies no middleware OWIN, o uso
OnSendingHeaders
parece contornar o problema.Por exemplo, o uso do código abaixo
owinResponseCookie2
será definido, mesmo queowinResponseCookie1
não seja:fonte
Eu enfrentei o problema semelhante com o Visual Studio 2017 e o .net MVC 5.2.4 , atualizando o Nuget Microsoft.Owin.Security.Google para a versão mais recente, que atualmente é 4.0.1, funcionou para mim! Espero que isso ajude alguém!
fonte
A solução mais rápida de código de uma linha:
Basta adicionar esta linha antes do método CreateIdentity:
fonte
HttpContext.Current.Session["RunSession"] = "1";
? em Globa.asaxSession_Start
?Eu tinha o mesmo sintoma do cabeçalho Set-Cookie não sendo enviado, mas nenhuma dessas respostas me ajudou. Tudo funcionou na minha máquina local, mas, quando implantados na produção, os cabeçalhos de conjunto de cookies nunca seriam configurados.
Acontece que foi uma combinação do uso de um personalizado
CookieAuthenticationMiddleware
com o WebApi junto com o suporte à compactação do WebApiFelizmente eu estava usando o ELMAH no meu projeto, o que permitiu que essa exceção fosse registrada:
O que me levou a esta edição do GitHub
Basicamente, se você tiver uma configuração estranha como a minha, desabilitará a compactação para os métodos / controladores WebApi que configuram cookies ou tente o
OwinServerCompressionHandler
.fonte