Второй вход вызывает бесконечный цикл перенаправления после первого успешного входа в систему. MVC.NET 5 OWIN ADAL OpenIDConnect
первый пост, будьте нежны!:)
Я разрабатываю веб-приложение MVC.NET 5 для Office 365 и использую структуру OpenIDConnect. Я установил OWIN (3) и ADAL (2) и мое приложение Azure AD. Нет логина для входа пользователя, у домашнего контроллера есть атрибут [Авторизовать], заставляющий немедленный переадресацию входа в Azure AD. Я не использую роли в любом из моих атрибутов Authorize.
Проблема. Я могу войти в мои приложения успешно - ОДИН! После первого входа я закрываю браузер (или открываю новый браузер на другой машине), и я снова ударяю приложение. Он перенаправляет меня на экран входа в Azure AD, в который я вхожу, и затем он постоянно перенаправляет между приложением и Azure, пока я не получу печально известный 400 заголовков. Заглядывая в магазин печенья, я нахожу его полным нонче. Я проверяю кеш (рецепт Vittorio EFADALCache, хотя я использовал TokenCache.DefaultShared, когда эта проблема была обнаружена) и содержит сотни строк данных кэша (только одна строка, сгенерированная с успешным знаком).
Я вижу, что через выходное окно происходят перенаправления, когда каждый обратный маршрут генерирует новый токен доступа и обновления:
Microsoft.IdentityModel.Clients.ActiveDirectory Verbose: 1 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - AcquireTokenByAuthorizationCodeHandler: Resource value in the token response was used for storing tokens in the cache
iisexpress.exe Information: 0 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - AcquireTokenByAuthorizationCodeHandler: Resource value in the token response was used for storing tokens in the cache
Microsoft.IdentityModel.Clients.ActiveDirectory Information: 2 : 31/07/2015 12:31:52: - TokenCache: Deserialized 1 items to token cache.
iisexpress.exe Information: 0 : 31/07/2015 12:31:52: - TokenCache: Deserialized 1 items to token cache.
Microsoft.IdentityModel.Clients.ActiveDirectory Verbose: 1 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: Storing token in the cache...
iisexpress.exe Information: 0 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: Storing token in the cache...
Microsoft.IdentityModel.Clients.ActiveDirectory Verbose: 1 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: An item was stored in the cache
iisexpress.exe Information: 0 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - TokenCache: An item was stored in the cache
Microsoft.IdentityModel.Clients.ActiveDirectory Information: 2 : 31/07/2015 12:31:52: 15ad306e-e26d-4827-98dc-dea75853788a - AcquireTokenHandlerBase: === Token Acquisition finished successfully. An access token was retuned:
Access Token Hash: PN5HoBHPlhhHIf1lxZhEWb4B4Hli69UKgcle0w7ssvo=
Refresh Token Hash: 3xmypXCO6MIMS9qUV+37uPD4kPip9WDH6Ex29GdWL88=
Expiration Time: 31/07/2015 13:31:51 +00:00
User Hash: GAWUtY8c4EKcJnsHrO6NOzwcQDMW64z5BNOvVIl1vAI=
Уведомление AuthorizationCodeReceived в моем OpenIdConnectAuthenticationOptions ударяется, когда проблема возникает, поэтому я знаю, что Azure считает, что логин был успешным (или перенаправление обратно к приложению не произойдет):
private static void PrepO365Auth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
//Configure OpenIDConnect, register callbacks for OpenIDConnect Notifications
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigHelper.ClientId,
Authority = authority,
PostLogoutRedirectUri = "https://localhost:44300/Account/SignedOut",
RedirectUri = "https://localhost:44300/",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
ClientCredential credential = new ClientCredential(ConfigHelper.ClientId, ConfigHelper.AppKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(authority, new EFADALTokenCache(signedInUserID)); // TokenCache.DefaultShared Probably need a persistent token cache to handle app restarts etc
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
context.Code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, ConfigHelper.GraphResourceId);
return Task.FromResult(0);
},
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error/ShowError?signIn=true&errorMessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
}
}
Я заменил (после обнаружения проблемы) Авторизованный атрибут с моим собственным атрибутом Auth, наследующим от AuthorizeAttribute, чтобы я мог попытаться войти в код авторизации и посмотреть, что происходит. Я построил PDB файл из версии исходного кода версии MVC 5 версии 5, но все, что происходит, это то, что оно возвращается в мой собственный код:( Как говорится, я переопределил все, что мог, и нашел, что filterContext.HttpContext.User.Identity.IsAuthenticated является ложным, что имеет смысл, так как это приведет к перенаправлению обратно к значку Azure.
Итак, я знаю, что:
- Azure принимает мой логин и возвращает соответствующие токены
- При втором входе в систему, перед OnAuthorization, filterContext.HttpContext.User.Identity.IsAuthenticated возвращает false
- Конфигурация приложения Azure хорошо, или вообще не будет аутентифицироваться
Я думаю, что:
- В настройке MVC Identity есть что-то неправильное. Azure работает правильно или вообще не аутентифицируется.
- Это не проблема с cookie, так как проблема возникает, если вы выполняете второй вход на другой машине.
Мне жаль, что это немного длиннее, но есть так много из этих бесконечных проблем с перенаправлением, мне нужно было объяснить, почему моя ситуация была иной!
То, что я ищу (если не ответ!), - это толкание в правильном направлении относительно того, как я могу отлаживать дальше.
Цените любую помощь, которую вы можете дать!
Andy
Ответы
Ответ 1
Найди ответ для всех, кого это интересует. Это известная ошибка в Катане, где менеджер файлов cookie Katana и менеджер cookie ASP.NET сталкиваются и перезаписывают друг другу файлы cookie. Полная информация и обходные пути здесь:
http://katanaproject.codeplex.com/wikipage?title=System.Web%20response%20cookie%20integration%20issues&referringTitle=Documentation
Отображаемый ниже SystemWebCookieManager теперь можно найти в пакете Nuget Microsoft.Owin.Host.SystemWeb.
Добавление кода, когда умирает CodePlex:
//stick this in public void ConfigureAuth(IAppBuilder app)
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// ...
CookieManager = new SystemWebCookieManager()
});
//And create this class elsewhere:
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),
});
}
}
Я тоже сделал это: https://gist.github.com/irwinwilliams/823f43ef8a5e8019a95874049dbb8b00
Ответ 2
У меня не было точно описанной проблемы, но у меня был цикл переадресации во время входа на основе OpenId, а также на моей машине DEV.
В моем случае это была простая ошибка с файлами cookie. Я обращался к защищенному URL через HTTP. Убедитесь, что вы используете защищенный URL-адрес своей полагающейся стороны через HTTPS.
После аутентификации cookie аутентификации будет отправляться только через HTTPS, это означает, что при доступе к защищенному URL через HTTP браузер не отправит ваш файл cookie с запросом, и, следовательно, сервер увидит вас как не прошедший проверку. На этом этапе сервер перенаправит вас на сервер auth (где вы уже вошли в систему). Сервер Auth перенаправляет вас обратно на исходный URL-адрес, тем самым обеспечивая цикл перенаправления.
Это никогда не должно происходить в ваших развертываниях, потому что вы всегда должны использовать all-SSL в своем приложении, если у вас есть такие функции, как проверка подлинности. Это снижает риск захвата сеанса.
Ответ 3
У меня была точно такая же проблема. Невозможно изменить URL-адрес от HTTP до HTTPS из-за других зависимостей. Окончательно разрешено путем добавления session_start и session_end в global.asax.cs
protected void Session_Start(object sender, EventArgs e)
{
// event is raised each time a new session is created
}
protected void Session_End(object sender, EventArgs e)
{
// event is raised when a session is abandoned or expires
}
Ответ 4
Я столкнулся с этой проблемой и применил ВСЕ ИСПЫТАНИЯ В ИНТЕРНЕТЕ. Ни один из них не работал, затем я вошел и посмотрел на свой файл cookie. Это было огромно. Owin-посредник обрезал его, а затем атрибут [Авторизовать] не смог проверить личность → отправить пользователю oidc → идентификатор хорошо - перенаправить на клиент → truncate cookie → не может подтвердить в [Авторизовать] → отправить пользователя на oidc → и т.д.
Исправление было в Microsoft.Owin.Host.SystemWeb 3.1.0.0 и использовании SystemWebChunkingCookieManager.
Он разделит файлы cookie и проанализирует их вместе.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
});
Ответ 5
Следующий код разрешил мою проблему, добавив события сеанса в файл Golbal.asax.cs.
protected void Session_Start(object sender, EventArgs e)
{
// event is raised each time a new session is created
}
protected void Session_End(object sender, EventArgs e)
{
// event is raised when a session is abandoned or expires
}
И добавив ниже код в общедоступный метод ConfigureAuth (IAppBuilder) для файла Startup.Auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
});