Permalink
Browse files

Antiforgery goes at the end of filters

  • Loading branch information...
1 parent 82b2e9c commit 01b237dda9f7320313cbbc881d881d0a26ec432f @ryanbrandenburg ryanbrandenburg committed Oct 28, 2016
View
@@ -123,6 +123,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MvcSandbox", "samples\MvcSa
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SimpleWebSite", "test\WebSites\SimpleWebSite\SimpleWebSite.xproj", "{396B40D7-AC70-49A7-B33C-ED42129FEBE3}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SecurityWebSite", "test\WebSites\SecurityWebSite\SecurityWebSite.xproj", "{D28CAC79-7004-4B69-993B-EDEB4653BFA8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -727,6 +729,18 @@ Global
{396B40D7-AC70-49A7-B33C-ED42129FEBE3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{396B40D7-AC70-49A7-B33C-ED42129FEBE3}.Release|x86.ActiveCfg = Release|Any CPU
{396B40D7-AC70-49A7-B33C-ED42129FEBE3}.Release|x86.Build.0 = Release|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Debug|x86.Build.0 = Debug|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Release|x86.ActiveCfg = Release|Any CPU
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -787,5 +801,6 @@ Global
{9879B5D5-2325-4A81-B4DF-F279FE8FEEB4} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{14ED4476-9F24-4776-8417-EA6927F6C9C9} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
{396B40D7-AC70-49A7-B33C-ED42129FEBE3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
+ {D28CAC79-7004-4B69-993B-EDEB4653BFA8} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal
@@ -21,8 +21,23 @@ namespace Microsoft.AspNetCore.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AutoValidateAntiforgeryTokenAttribute : Attribute, IFilterFactory, IOrderedFilter
{
- /// <inheritdoc />
- public int Order { get; set; }
+ /// <summary>
+ /// Gets the order value for determining the order of execution of filters. Filters execute in
+ /// ascending numeric value of the <see cref="Order"/> property.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Filters are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
+ /// </para>
+ /// <para>
+ /// The default Order for this attribute is 1000 because it must run after any filter which does authentication
+ /// or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
+ /// </para>
+ /// <para>
+ /// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
+ /// </para>
+ /// </remarks>
+ public int Order { get; set; } = 1000;
/// <inheritdoc />
public bool IsReusable => true;
@@ -20,8 +20,23 @@ namespace Microsoft.AspNetCore.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenAttribute : Attribute, IFilterFactory, IOrderedFilter
{
- /// <inheritdoc />
- public int Order { get; set; }
+ /// <summary>
+ /// Gets the order value for determining the order of execution of filters. Filters execute in
+ /// ascending numeric value of the <see cref="Order"/> property.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Filters are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
+ /// </para>
+ /// <para>
+ /// The default Order for this attribute is 1000 because it must run after any filter which does authentication
+ /// or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
+ /// </para>
+ /// <para>
+ /// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
+ /// </para>
+ /// </remarks>
+ public int Order { get; set; } = 1000;
/// <inheritdoc />
public bool IsReusable => true;
@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using SecurityWebSite;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.FunctionalTests
+{
+ public class AntiforgeryAuthTests : IClassFixture<MvcTestFixture<Startup>>
+ {
+ public AntiforgeryAuthTests(MvcTestFixture<Startup> fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [Fact]
+ public async Task AutomaticAuthenticationBeforeAntiforgery()
+ {
+ // Arrange & Act
+ var response = await Client.PostAsync("http://localhost/Home/AutoAntiforgery", null);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+ Assert.Equal("/Home/Login", response.Headers.Location.AbsolutePath, StringComparer.OrdinalIgnoreCase);
+ }
+
+ [Fact]
+ public async Task AuthBeforeAntiforgery()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Home/Antiforgery");
+
+ // Assert
+ // Redirected to login page, Antiforgery didn't fail yet
+ Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
+ Assert.Equal("/Home/Login", response.Headers.Location.AbsolutePath, StringComparer.OrdinalIgnoreCase);
+ }
+ }
+}
@@ -10,6 +10,7 @@
"warningsAsErrors": true
},
"dependencies": {
+ "SecurityWebSite": "1.0.0",
"ApiExplorerWebSite": "1.0.0",
"ApplicationModelWebSite": "1.0.0",
"BasicWebSite": "1.0.0",
@@ -0,0 +1,41 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace SecurityWebSite.Controllers
+{
+ public class HomeController : Controller
+ {
+ public IActionResult Index()
+ {
+ return View();
+ }
+
+ [AutoValidateAntiforgeryToken]
+ [Authorize]
+ [HttpPost]
+ public IActionResult AutoAntiforgery()
+ {
+ return Content("Automaticaly doesn't matter");
+ }
+
+ [Authorize]
+ [ValidateAntiForgeryToken]
+ public IActionResult Antiforgery()
+ {
+ return Content("Doesn't matter");
+ }
+
+ public IActionResult Login()
+ {
+ return Content("Login!");
+ }
+
+ public IActionResult Logout()
+ {
+ return Content("Logout!");
+ }
+ }
+}
@@ -0,0 +1,22 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO;
+using Microsoft.AspNetCore.Hosting;
+
+namespace SecurityWebSite
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .UseStartup<Startup>()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>d28cac79-7004-4b69-993b-edeb4653bfa8</ProjectGuid>
+ <RootNamespace>AjaxAntiForgeryValidation</RootNamespace>
+ <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
+ <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
+ <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <DnxInvisibleContent Include="bower.json" />
+ <DnxInvisibleContent Include=".bowerrc" />
+ </ItemGroup>
+ <Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project>
@@ -0,0 +1,63 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace SecurityWebSite
+{
+ public class Startup
+ {
+ public Startup(IHostingEnvironment env)
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(env.ContentRootPath)
+ .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+ .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
+ .AddEnvironmentVariables();
+ Configuration = builder.Build();
+ }
+
+ public IConfigurationRoot Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Add framework services.
+ services.AddMvc();
+ services.AddAntiforgery();
+ services.AddAuthentication();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
+ {
+ loggerFactory.AddConsole(Configuration.GetSection("Logging"));
+ loggerFactory.AddDebug();
+
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseStaticFiles();
+ app.UseCookieAuthentication(new CookieAuthenticationOptions
+ {
+ LoginPath = "/Home/Login",
+ LogoutPath = "/Home/Logout",
+ AutomaticAuthenticate = true,
+ AutomaticChallenge = true
+ });
+
+ app.UseMvc(routes =>
+ {
+ routes.MapRoute(
+ name: "default",
+ template: "{controller=Home}/{action=Index}/{id?}");
+ });
+ }
+ }
+}
@@ -0,0 +1,21 @@
+@{
+ ViewData["Title"] = "Home Page";
+}
+<h1>Hello!</h1>
+<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.4.min.js"></script>
+<script>
+ $(document).ready(function () {
+ $.ajax({
+ type: "get",
+ dataType: "html",
+ url: '@Url.Action("Antiforgery", "Home")',
+ data: {},
+ success: function (result) {
+ alert("We were redirected to login!");
+ },
+ error: function (err, scnd) {
+ alert(err.statusText);
+ }
+ });
+ });
+</script>
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>@ViewData["Title"] - SecurityWebSite</title>
+</head>
+<body>
+ <div class="navbar navbar-inverse navbar-fixed-top">
+ <div class="container">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">SecurityWebSite</a>
+ </div>
+ <div class="navbar-collapse collapse">
+ <ul class="nav navbar-nav">
+ <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
+ <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
+ <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="container body-content">
+ @RenderBody()
+ <hr />
+ <footer>
+ <p>&copy; 2016 - SecurityWebSite</p>
+ </footer>
+ </div>
+</body>
+</html>
@@ -0,0 +1,2 @@
+@using SecurityWebSite
+ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -0,0 +1,3 @@
+@{
+ Layout = "_Layout";
+}
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ }
+}
Oops, something went wrong.

1 comment on commit 01b237d

@pranavkm
Member

The website creates a cycle between the Mvc and Identity repos.

Please sign in to comment.