diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs index 763981fcf7..eeced270d6 100644 --- a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs +++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgery.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Security.DataProtection; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; namespace Microsoft.AspNet.Mvc diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryContext.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryContext.cs new file mode 100644 index 0000000000..942e06321e --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryContext.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.Mvc +{ + /// + /// Used as a per request state. + /// + internal class AntiForgeryContext + { + public AntiForgeryToken CookieToken { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs index bd36f366fd..4cd1051b81 100644 --- a/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs +++ b/src/Microsoft.AspNet.Mvc.Core/AntiForgery/AntiForgeryTokenStore.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics.Contracts; using System.Threading.Tasks; using Microsoft.AspNet.Http; +using Microsoft.Framework.DependencyInjection; namespace Microsoft.AspNet.Mvc { @@ -12,7 +14,7 @@ namespace Microsoft.AspNet.Mvc { private readonly AntiForgeryOptions _config; private readonly IAntiForgeryTokenSerializer _serializer; - + internal AntiForgeryTokenStore([NotNull] AntiForgeryOptions config, [NotNull] IAntiForgeryTokenSerializer serializer) { @@ -22,14 +24,20 @@ namespace Microsoft.AspNet.Mvc public AntiForgeryToken GetCookieToken(HttpContext httpContext) { - var cookie = httpContext.Request.Cookies[_config.CookieName]; - if (string.IsNullOrEmpty(cookie)) + var contextAccessor = httpContext.RequestServices.GetService>(); + if (contextAccessor.Value != null) { - // did not exist + return contextAccessor.Value.CookieToken; + } + + var requestCookie = httpContext.Request.Cookies[_config.CookieName]; + if (string.IsNullOrEmpty(requestCookie)) + { + // unable to find the cookie. return null; } - return _serializer.Deserialize(cookie); + return _serializer.Deserialize(requestCookie); } public async Task GetFormTokenAsync(HttpContext httpContext) @@ -47,6 +55,12 @@ namespace Microsoft.AspNet.Mvc public void SaveCookieToken(HttpContext httpContext, AntiForgeryToken token) { + // Add the cookie to the request based context. + // This is useful if the cookie needs to be reloaded in the context of the same request. + var contextAccessor = httpContext.RequestServices.GetService>(); + Contract.Assert(contextAccessor.Value == null, "AntiForgeryContext should be set only once per request."); + contextAccessor.SetValue(new AntiForgeryContext() { CookieToken = token }); + var serializedToken = _serializer.Serialize(token); var options = new CookieOptions() { HttpOnly = true }; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/AntiXsrf/AntiForgeryTokenStoreTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/AntiXsrf/AntiForgeryTokenStoreTest.cs index 0fb923b198..1b0d9ce749 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/AntiXsrf/AntiForgeryTokenStoreTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/AntiXsrf/AntiForgeryTokenStoreTest.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Http; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; using Moq; using Xunit; @@ -27,6 +29,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test mockHttpContext .Setup(o => o.Request.Cookies) .Returns(requestCookies.Object); + var contextAccessor = new ContextAccessor(); + mockHttpContext.SetupGet(o => o.RequestServices) + .Returns(GetServiceProvider(contextAccessor)); var config = new AntiForgeryOptions() { CookieName = _cookieName @@ -43,6 +48,41 @@ namespace Microsoft.AspNet.Mvc.Core.Test Assert.Null(token); } + [Fact] + public void GetCookieToken_CookieIsMissingInRequest_LooksUpCookieInAntiForgeryContext() + { + // Arrange + var requestCookies = new Mock(); + requestCookies + .Setup(o => o.Get(It.IsAny())) + .Returns(string.Empty); + var mockHttpContext = new Mock(); + mockHttpContext + .Setup(o => o.Request.Cookies) + .Returns(requestCookies.Object); + var contextAccessor = new ContextAccessor(); + mockHttpContext.SetupGet(o => o.RequestServices) + .Returns(GetServiceProvider(contextAccessor)); + + // add a cookie explicitly. + var cookie = new AntiForgeryToken(); + contextAccessor.SetValue(new AntiForgeryContext() { CookieToken = cookie }); + var config = new AntiForgeryOptions() + { + CookieName = _cookieName + }; + + var tokenStore = new AntiForgeryTokenStore( + config: config, + serializer: null); + + // Act + var token = tokenStore.GetCookieToken(mockHttpContext.Object); + + // Assert + Assert.Equal(cookie, token); + } + [Fact] public void GetCookieToken_CookieIsEmpty_ReturnsNull() { @@ -237,7 +277,10 @@ namespace Microsoft.AspNet.Mvc.Core.Test var mockHttpContext = new Mock(); mockHttpContext.Setup(o => o.Response.Cookies) .Returns(cookies); - + var contextAccessor = new ContextAccessor(); + mockHttpContext.SetupGet(o => o.RequestServices) + .Returns(GetServiceProvider(contextAccessor)); + var mockSerializer = new Mock(); mockSerializer.Setup(o => o.Serialize(token)) .Returns("serialized-value"); @@ -257,7 +300,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test // Assert Assert.Equal(1, cookies.Count); - + Assert.NotNull(contextAccessor.Value.CookieToken); Assert.NotNull(cookies); Assert.Equal(_cookieName, cookies.Key); Assert.Equal("serialized-value", cookies.Value); @@ -276,9 +319,20 @@ namespace Microsoft.AspNet.Mvc.Core.Test mockHttpContext.Setup(o => o.Request) .Returns(request.Object); + var contextAccessor = new ContextAccessor(); + mockHttpContext.SetupGet(o => o.RequestServices) + .Returns(GetServiceProvider(contextAccessor)); + return mockHttpContext.Object; } + private static IServiceProvider GetServiceProvider(IContextAccessor contextAccessor) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddInstance>(contextAccessor); + return serviceCollection.BuildServiceProvider(); + } + private class MockResponseCookieCollection : IResponseCookies { public string Key { get; set; } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTestHelper.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTestHelper.cs new file mode 100644 index 0000000000..7a78b99a79 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTestHelper.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Net.Http; +using System.Xml; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public static class AntiForgeryTestHelper + { + public static string RetrieveAntiForgeryToken(string htmlContent, string actionUrl) + { + int startSearchIndex = 0; + + while (startSearchIndex < htmlContent.Length) + { + var formStartIndex = htmlContent.IndexOf("", startSearchIndex, StringComparison.OrdinalIgnoreCase); + + if (formStartIndex == -1 || formEndIndex == -1) + { + //Unable to find the form start or end - finish the search + return null; + } + + formEndIndex = formEndIndex + "".Length; + startSearchIndex = formEndIndex + 1; + + var htmlDocument = new XmlDocument(); + htmlDocument.LoadXml(htmlContent.Substring(formStartIndex, formEndIndex - formStartIndex)); + foreach (XmlAttribute attribute in htmlDocument.DocumentElement.Attributes) + { + if (string.Equals(attribute.Name, "action", StringComparison.OrdinalIgnoreCase) && attribute.Value.EndsWith(actionUrl, StringComparison.OrdinalIgnoreCase)) + { + foreach (XmlNode input in htmlDocument.GetElementsByTagName("input")) + { + if (input.Attributes["name"].Value == "__RequestVerificationToken" && input.Attributes["type"].Value == "hidden") + { + return input.Attributes["value"].Value; + } + } + } + } + } + + return null; + } + + public static string RetrieveAntiForgeryCookie(HttpResponseMessage response) + { + var setCookieArray = response.Headers.GetValues("Set-Cookie").ToArray(); + return setCookieArray[0].Split(';') + .Where(headerValue => headerValue.StartsWith("__RequestVerificationToken")) + .First() + .Split('=')[1]; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTests.cs index 52e0e8edb9..c69be905f5 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTests.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.TestHost; @@ -17,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests private readonly Action _app = new AntiForgeryWebSite.Startup().Configure; [Fact] - public async Task MultipleAFTokensWithinTheSamePage_AreAllowed() + public async Task MultipleAFTokensWithinTheSamePage_GeneratesASingleCookieToken() { // Arrange var server = TestServer.Create(_services, _app); @@ -32,9 +34,208 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal("SAMEORIGIN", header); var setCookieHeader = response.Headers.GetValues("Set-Cookie").ToArray(); - Assert.Equal(2, setCookieHeader.Length); + + // Even though there are two forms there should only be one response cookie, + // as for the second form, the cookie from the first token should be reused. + Assert.Equal(1, setCookieHeader.Length); Assert.True(setCookieHeader[0].StartsWith("__RequestVerificationToken")); - Assert.True(setCookieHeader[1].StartsWith("__RequestVerificationToken")); + } + + [Fact] + public async Task MultipleFormPostWithingASingleView_AreAllowed() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + // do a get response. + var getResponse = await client.GetAsync("http://localhost/Account/Login"); + var resposneBody = await getResponse.Content.ReadAsStringAsync(); + + // Get the AF token for the second login. If the cookies are generated twice(i.e are different), + // this AF token will not work with the first cookie. + var formToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(resposneBody, "Account/UseFacebookLogin"); + var cookieToken = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse); + + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login"); + request.Headers.Add("Cookie", "__RequestVerificationToken=" + cookieToken); + var nameValueCollection = new List> + { + new KeyValuePair("__RequestVerificationToken", formToken), + new KeyValuePair("UserName", "abra"), + new KeyValuePair("Password", "cadabra"), + }; + + request.Content = new FormUrlEncodedContent(nameValueCollection); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("OK", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task InvalidCookieToken_Throws() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + var cookieToken = "asdad"; + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login"); + request.Headers.Add("Cookie", "__RequestVerificationToken=" + cookieToken); + var formToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAABx9"+ + "2btLE7MLa5AVabrJ3TOAAAAAASAAACgAAAAEAAAAOc8lIs3RfhLkS2fHqBHeuIYAAAACIspnfiEu6QYzrfOul"+ + "vXbCNm5E7VyKW8FAAAAOD25c81cu0Zi06Myn8Ne1JLOK2K"; + var nameValueCollection = new List> + { + new KeyValuePair("__RequestVerificationToken", formToken), + new KeyValuePair("UserName", "abra"), + new KeyValuePair("Password", "cadabra"), + }; + + request.Content = new FormUrlEncodedContent(nameValueCollection); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); + Assert.Equal("The anti-forgery token could not be decrypted.", ex.Message); + } + + [Fact] + public async Task InvalidFormToken_Throws() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + var cookieToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAAD"+ + "2ZaQPi5Dq1fUTYj06LxMVAAAAAASAAACgAAAAEAAAADVYzWBsC5SHK_AWCieAFsgYAAAA-XHHnq2Yz2GS-e"+ + "R8cHq-A2T8BfPHM21GFAAAALpW0H8-5oPxbe2DOKuj8ZG3bohn"; + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login"); + var formToken = "adsad"; + request.Headers.Add("Cookie", "__RequestVerificationToken=" + cookieToken); + var nameValueCollection = new List> + { + new KeyValuePair("__RequestVerificationToken", formToken), + new KeyValuePair("UserName", "abra"), + new KeyValuePair("Password", "cadabra"), + }; + + request.Content = new FormUrlEncodedContent(nameValueCollection); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); + Assert.Equal("The anti-forgery token could not be decrypted.", ex.Message); + } + + [Fact] + public async Task IncompatibleCookieToken_Throws() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + var cookieToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAAB"+ + "QrVKmVeuzQHJX3jUAzFNNAAAAAASAAACgAAAAEAAAADd2PjKWhB8NmuaPMZDDutgYAAAAXGTjIRTnjLHqwC"+ + "KFGx9ZVQOLVfWIGQxiFAAAAIVqLISuhF2sFrd3UQqLDteT0vRu"; + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login"); + request.Headers.Add("Cookie", "__RequestVerificationToken=" + cookieToken); + var formToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAABx9"+ + "2btLE7MLa5AVabrJ3TOAAAAAASAAACgAAAAEAAAAOc8lIs3RfhLkS2fHqBHeuIYAAAACIspnfiEu6QYzrfOul"+ + "vXbCNm5E7VyKW8FAAAAOD25c81cu0Zi06Myn8Ne1JLOK2K"; + var nameValueCollection = new List> + { + new KeyValuePair("__RequestVerificationToken", formToken), + new KeyValuePair("UserName", "abra"), + new KeyValuePair("Password", "cadabra"), + }; + + request.Content = new FormUrlEncodedContent(nameValueCollection); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); + Assert.Equal("The anti-forgery cookie token and form field token do not match.", ex.Message); + } + + [Fact] + public async Task IncompatibleFormToken_Throws() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + var cookieToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAAD"+ + "2ZaQPi5Dq1fUTYj06LxMVAAAAAASAAACgAAAAEAAAADVYzWBsC5SHK_AWCieAFsgYAAAA-XHHnq2Yz2GS-e"+ + "R8cHq-A2T8BfPHM21GFAAAALpW0H8-5oPxbe2DOKuj8ZG3bohn"; + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login"); + var formToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAAApj"+ + "_D9vARroIdg2t6sfw06AAAAAASAAACgAAAAEAAAAKMz9G6buL-JM_3eknUq4aoYAAAAuPBN2dc0RsRvrde1V6"+ + "FBrQYCEuUdx-nSFAAAAEvqGxIwr8zFKO_osS6r953VqjS-"; + request.Headers.Add("Cookie", "__RequestVerificationToken=" + cookieToken); + var nameValueCollection = new List> + { + new KeyValuePair("__RequestVerificationToken", formToken), + new KeyValuePair("UserName", "abra"), + new KeyValuePair("Password", "cadabra"), + }; + + request.Content = new FormUrlEncodedContent(nameValueCollection); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); + Assert.Equal("The anti-forgery cookie token and form field token do not match.", ex.Message); + } + + [Fact] + public async Task MissingCookieToken_Throws() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login"); + var formToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAABx9"+ + "2btLE7MLa5AVabrJ3TOAAAAAASAAACgAAAAEAAAAOc8lIs3RfhLkS2fHqBHeuIYAAAACIspnfiEu6QYzrfOulvXbCNm5E7VyK"+ + "W8FAAAAOD25c81cu0Zi06Myn8Ne1JLOK2K"; + var nameValueCollection = new List> + { + new KeyValuePair("__RequestVerificationToken", formToken), + new KeyValuePair("UserName", "abra"), + new KeyValuePair("Password", "cadabra"), + }; + + request.Content = new FormUrlEncodedContent(nameValueCollection); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); + Assert.Equal("The required anti-forgery cookie \"__RequestVerificationToken\" is not present.", ex.Message); + } + + [Fact] + public async Task MissingAFToken_Throws() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var cookieToken = "AQAAANCMnd8BFdERjHoAwE_Cl-sBAAAADBPoDUIPtEee8EZ40kjaOQAAAAACAAAAAAADZgAAwAAAABAAAAD"+ + "2ZaQPi5Dq1fUTYj06LxMVAAAAAASAAACgAAAAEAAAADVYzWBsC5SHK_AWCieAFsgYAAAA-XHHnq2Yz2GS-e"+ + "R8cHq-A2T8BfPHM21GFAAAALpW0H8-5oPxbe2DOKuj8ZG3bohn"; + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login"); + request.Headers.Add("Cookie", "__RequestVerificationToken=" + cookieToken); + var nameValueCollection = new List> + { + new KeyValuePair("UserName", "abra"), + new KeyValuePair("Password", "cadabra"), + }; + + request.Content = new FormUrlEncodedContent(nameValueCollection); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); + Assert.Equal("The required anti-forgery form field \"__RequestVerificationToken\" is not present.", + ex.Message); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index 82ca7dda62..39ce9d4c2a 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -7,7 +7,7 @@ "AddServicesWebSite": "", "AntiForgeryWebSite": "", "BasicWebSite": "", - "CompositeViewEngine": "", + "CompositeViewEngine": "", "ConnegWebsite": "", "FiltersWebSite": "", "FormatterWebSite": "", @@ -25,7 +25,7 @@ "MvcSample.Web": "", "RoutingWebSite": "", "RazorWebSite": "", - "ValueProvidersSite": "", + "ValueProvidersSite": "", "Xunit.KRunner": "1.0.0-*" }, "commands": { @@ -37,6 +37,10 @@ "AutofacWebSite": "" } }, - "aspnetcore50": { } + "aspnetcore50": { + "dependencies": { + "System.Xml.XmlDocument": "4.0.0.0" + } + } } } diff --git a/test/WebSites/AntiForgeryWebSite/Controllers/AccountController.cs b/test/WebSites/AntiForgeryWebSite/Controllers/AccountController.cs index 90bcb0c1c1..73e52877be 100644 --- a/test/WebSites/AntiForgeryWebSite/Controllers/AccountController.cs +++ b/test/WebSites/AntiForgeryWebSite/Controllers/AccountController.cs @@ -18,14 +18,19 @@ namespace AntiForgeryWebSite return View(); } + [AllowAnonymous] + public string UseFacebookLogin() + { + return "somestring"; + } + // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] - public ActionResult Login(LoginViewModel model, string returnUrl) + public string Login(LoginViewModel model) { - // Send to register which gets another html antiforgery token. - return RedirectToAction("Index", "Home"); + return "OK"; } } } \ No newline at end of file diff --git a/test/WebSites/AntiForgeryWebSite/Views/Account/Login.cshtml b/test/WebSites/AntiForgeryWebSite/Views/Account/Login.cshtml index 519e0bf57f..1075ad5735 100644 --- a/test/WebSites/AntiForgeryWebSite/Views/Account/Login.cshtml +++ b/test/WebSites/AntiForgeryWebSite/Views/Account/Login.cshtml @@ -8,7 +8,7 @@
- @using (Html.BeginForm("Login", "Account", new { ReturnUrl = "sds" }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) + @using (Html.BeginForm("Login", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken()

Use a local account to log in.