diff --git a/Mvc.sln b/Mvc.sln index d6c3adbc18..fd2508e6b0 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -66,6 +66,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution global.json = global.json EndProjectSection Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ConnegWebsite", "test\WebSites\ConnegWebSite\ConnegWebsite.kproj", "{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AntiForgeryWebSite", "test\WebSites\AntiForgeryWebSite\AntiForgeryWebSite.kproj", "{A353B17E-A940-4CE8-8BF9-179E24A9041F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -337,6 +338,16 @@ Global {C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}.Release|Mixed Platforms.Build.0 = Release|Any CPU {C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}.Release|x86.ActiveCfg = Release|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|x86.ActiveCfg = Debug|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Any CPU.Build.0 = Release|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -369,5 +380,6 @@ Global {98335B23-E4B9-4CAD-9749-0DED32A659A1} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E} {E69FD235-2042-43A4-9970-59CB29955B4E} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} {C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} + {A353B17E-A940-4CE8-8BF9-179E24A9041F} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} EndGlobalSection EndGlobal diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTests.cs new file mode 100644 index 0000000000..1f7d6e8bc3 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/AntiForgeryTests.cs @@ -0,0 +1,42 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public class AntiForgeryTests + { + private readonly IServiceProvider _services; + private readonly Action _app = new AntiForgeryWebSite.Startup().Configure; + + public AntiForgeryTests() + { + _services = TestHelper.CreateServices("AntiForgeryWebSite"); + } + + [Fact] + public async Task MultipleAFTokensWithinTheSamePage_AreAllowed() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.Handler; + + // Act + var response = await client.GetAsync("http://localhost/Account/Login"); + + //Assert + Assert.Equal(200, response.StatusCode); + Assert.Equal("SAMEORIGIN", response.Headers["X-Frame-Options"]); + + var setCookieHeader = response.Headers.GetCommaSeparatedValues("Set-Cookie"); + Assert.Equal(2, setCookieHeader.Count); + Assert.Equal(true, setCookieHeader[0].StartsWith("__RequestVerificationToken")); + Assert.Equal(true, setCookieHeader[1].StartsWith("__RequestVerificationToken")); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj b/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj index 0002e0cf6e..c2a93aa2c1 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/Microsoft.AspNet.Mvc.FunctionalTests.kproj @@ -32,6 +32,7 @@ + diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index 90a5f6a8b2..79163665b7 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -9,6 +9,7 @@ "ConnegWebsite": "", "FormatterWebSite": "", "InlineConstraintsWebSite": "", + "AntiForgeryWebSite": "", "Microsoft.AspNet.TestHost": "1.0.0-*", "Microsoft.AspNet.PipelineCore": "1.0.0-*", "Microsoft.AspNet.Mvc.TestConfiguration": "", diff --git a/test/WebSites/AntiForgeryWebSite/AntiForgeryWebSite.kproj b/test/WebSites/AntiForgeryWebSite/AntiForgeryWebSite.kproj new file mode 100644 index 0000000000..2d61da8f44 --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/AntiForgeryWebSite.kproj @@ -0,0 +1,44 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + Debug + AnyCPU + + + + a353b17e-a940-4ce8-8bf9-179e24a9041f + Web + + + ConsoleDebugger + + + WebDebugger + + + + + 2.0 + + + 11178 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/WebSites/AntiForgeryWebSite/Controllers/AccountController.cs b/test/WebSites/AntiForgeryWebSite/Controllers/AccountController.cs new file mode 100644 index 0000000000..360ae5a666 --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Controllers/AccountController.cs @@ -0,0 +1,31 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace AntiForgeryWebSite +{ + // This controller is reachable via traditional routing. + public class AccountController : Controller + { + // GET: /Account/Login + [AllowAnonymous] + public ActionResult Login(string returnUrl = null) + { + ViewBag.ReturnUrl = returnUrl; + + return View(); + } + + // POST: /Account/Login + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task Login(LoginViewModel model, string returnUrl) + { + // Send to register which gets another html antiforgery token. + return RedirectToAction("Index", "Home"); + } + } +} \ No newline at end of file diff --git a/test/WebSites/AntiForgeryWebSite/Controllers/HomeController.cs b/test/WebSites/AntiForgeryWebSite/Controllers/HomeController.cs new file mode 100644 index 0000000000..7931daa46d --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Controllers/HomeController.cs @@ -0,0 +1,16 @@ +// 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 Microsoft.AspNet.Mvc; + +namespace AntiForgeryWebSite +{ + // This controller is reachable via traditional routing. + public class HomeController : Controller + { + public IActionResult Index() + { + return View("MyView"); + } + } +} \ No newline at end of file diff --git a/test/WebSites/AntiForgeryWebSite/Models/AccountViewModels.cs b/test/WebSites/AntiForgeryWebSite/Models/AccountViewModels.cs new file mode 100644 index 0000000000..939a7ae78c --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Models/AccountViewModels.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace AntiForgeryWebSite +{ + public class LoginViewModel + { + [Required] + [Display(Name = "User name")] + public string UserName { get; set; } + + [Required] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } +} diff --git a/test/WebSites/AntiForgeryWebSite/Startup.cs b/test/WebSites/AntiForgeryWebSite/Startup.cs new file mode 100644 index 0000000000..a25cb4932c --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Startup.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Routing; +using Microsoft.Framework.DependencyInjection; + +namespace AntiForgeryWebSite +{ + public class Startup + { + public void Configure(IBuilder app) + { + var configuration = app.GetTestConfiguration(); + app.UseServices(services => + { + services.AddMvc(configuration); + }); + + app.UseMvc(routes => + { + routes.MapRoute("ActionAsMethod", "{controller}/{action}", + defaults: new { controller = "Home", action = "Index" }); + }); + } + } +} diff --git a/test/WebSites/AntiForgeryWebSite/Views/Account/Login.cshtml b/test/WebSites/AntiForgeryWebSite/Views/Account/Login.cshtml new file mode 100644 index 0000000000..519e0bf57f --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Views/Account/Login.cshtml @@ -0,0 +1,63 @@ +@model AntiForgeryWebSite.LoginViewModel + +@{ + ViewBag.Title = "Log in"; +} + +

@ViewBag.Title.

+
+
+
+ @using (Html.BeginForm("Login", "Account", new { ReturnUrl = "sds" }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) + { + @Html.AntiForgeryToken() +

Use a local account to log in.

+
+ @Html.ValidationSummary(true) +
+ @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" }) +
+ @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) + @Html.ValidationMessageFor(m => m.UserName) +
+
+
+ @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) +
+ @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) + @Html.ValidationMessageFor(m => m.Password) +
+
+
+
+ +
+
+ } +
+ +@using (Html.BeginForm("UseFacebookLogin", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) +{ + @Html.AntiForgeryToken() +

Use Facebook login.

+
+ @Html.ValidationSummary() +
+ @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" }) +
+ @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) +
+
+
+ @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) +
+ @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) +
+
+
+
+ +
+
+} +
diff --git a/test/WebSites/AntiForgeryWebSite/Views/Home/MyView.cshtml b/test/WebSites/AntiForgeryWebSite/Views/Home/MyView.cshtml new file mode 100644 index 0000000000..a2c25ed46b --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Views/Home/MyView.cshtml @@ -0,0 +1,3 @@ +@{ Layout = "/Views/Shared/_Layout.cshtml"; + ViewBag.Title = "Home Page"; +} diff --git a/test/WebSites/AntiForgeryWebSite/Views/Shared/_Layout.cshtml b/test/WebSites/AntiForgeryWebSite/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..ede382285b --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Views/Shared/_Layout.cshtml @@ -0,0 +1,25 @@ + + + + + + @ViewBag.Title –AntiForgery Functional Tests + + + +
+ @RenderBody() +
+ + \ No newline at end of file diff --git a/test/WebSites/AntiForgeryWebSite/Views/Shared/_LoginPartial.cshtml b/test/WebSites/AntiForgeryWebSite/Views/Shared/_LoginPartial.cshtml new file mode 100644 index 0000000000..ce6e407537 --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/Views/Shared/_LoginPartial.cshtml @@ -0,0 +1,3 @@ + diff --git a/test/WebSites/AntiForgeryWebSite/project.json b/test/WebSites/AntiForgeryWebSite/project.json new file mode 100644 index 0000000000..d3b6d14ea4 --- /dev/null +++ b/test/WebSites/AntiForgeryWebSite/project.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "Microsoft.AspNet.Mvc": "", + "Microsoft.AspNet.Mvc.TestConfiguration": "", + "Microsoft.AspNet.Server.IIS": "1.0.0-*", + "Microsoft.AspNet.Mvc.Razor": "", + "Microsoft.AspNet.Security.Cookies": "1.0.0-*" + }, + "frameworks": { + "net45": { }, + "k10": { } + } +}