diff --git a/.bowerrc b/.bowerrc
new file mode 100644
index 0000000000..eae11312d8
--- /dev/null
+++ b/.bowerrc
@@ -0,0 +1,3 @@
+{
+ "directory": "wwwroot/lib"
+}
\ No newline at end of file
diff --git a/Antiforgery.sln b/Antiforgery.sln
index 3074ae4f3d..48fb0ca448 100644
--- a/Antiforgery.sln
+++ b/Antiforgery.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.22808.1
+VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{71D070C4-B325-48F7-9F25-DD4E91C2BBCA}"
EndProject
@@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{D8C4
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AntiforgerySample", "samples\AntiforgerySample\AntiforgerySample.xproj", "{AF9E0784-5EDB-494F-B46C-1A8DA785C49C}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Antiforgery.FunctionalTests", "test\Microsoft.AspNet.Antiforgery.FunctionalTests\Microsoft.AspNet.Antiforgery.FunctionalTests.xproj", "{8B288810-5A96-4AF5-9836-8BA2D2953203}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
{AF9E0784-5EDB-494F-B46C-1A8DA785C49C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF9E0784-5EDB-494F-B46C-1A8DA785C49C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF9E0784-5EDB-494F-B46C-1A8DA785C49C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8B288810-5A96-4AF5-9836-8BA2D2953203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B288810-5A96-4AF5-9836-8BA2D2953203}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B288810-5A96-4AF5-9836-8BA2D2953203}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B288810-5A96-4AF5-9836-8BA2D2953203}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -41,5 +47,6 @@ Global
{46FB03FB-7A44-4106-BDDE-D6F5417544AB} = {71D070C4-B325-48F7-9F25-DD4E91C2BBCA}
{415E83F8-6002-47E4-AA8E-CD5169C06F28} = {6EDD8B57-4DE8-4246-A6A3-47ECD92740B4}
{AF9E0784-5EDB-494F-B46C-1A8DA785C49C} = {D8C46ADF-E40A-4B48-ADE9-E1FA80466FE3}
+ {8B288810-5A96-4AF5-9836-8BA2D2953203} = {6EDD8B57-4DE8-4246-A6A3-47ECD92740B4}
EndGlobalSection
EndGlobal
diff --git a/samples/AntiforgerySample/wwwroot/Index.html b/samples/AntiforgerySample/wwwroot/Index.html
index b9a1fc0182..7789fe514b 100644
--- a/samples/AntiforgerySample/wwwroot/Index.html
+++ b/samples/AntiforgerySample/wwwroot/Index.html
@@ -4,7 +4,7 @@
Todo List Antiforgery Sample
-
+
@@ -38,7 +38,7 @@
-
+
diff --git a/test/Microsoft.AspNet.Antiforgery.FunctionalTests/AntiForgerySampleTestFixture.cs b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/AntiForgerySampleTestFixture.cs
new file mode 100644
index 0000000000..dd43b79399
--- /dev/null
+++ b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/AntiForgerySampleTestFixture.cs
@@ -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.Collections.Generic;
+using System.Net.Http;
+using Microsoft.AspNet.Hosting;
+using Microsoft.AspNet.TestHost;
+using Microsoft.Extensions.Configuration;
+
+namespace Microsoft.AspNet.Antiforgery.FunctionalTests
+{
+ public class AntiForgerySampleTestFixture : IDisposable
+ {
+ private readonly TestServer _server;
+
+ public AntiForgerySampleTestFixture()
+ {
+ var configurationBuilder = new ConfigurationBuilder();
+
+ configurationBuilder.AddInMemoryCollection(new[]
+ {
+ new KeyValuePair("webroot", "wwwroot")
+ });
+
+ var builder = new WebApplicationBuilder()
+ .UseConfiguration(configurationBuilder.Build())
+ .UseStartup(typeof(AntiforgerySample.Startup))
+ .UseApplicationBasePath("../../samples/AntiforgerySample");
+
+ _server = new TestServer(builder);
+
+ Client = _server.CreateClient();
+ Client.BaseAddress = new Uri("http://localhost");
+ }
+
+ public HttpClient Client { get; }
+
+ public void Dispose()
+ {
+ Client.Dispose();
+ _server.Dispose();
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Antiforgery.FunctionalTests/AntiforgerySampleTest.cs b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/AntiforgerySampleTest.cs
new file mode 100644
index 0000000000..4135a56bd3
--- /dev/null
+++ b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/AntiforgerySampleTest.cs
@@ -0,0 +1,98 @@
+// 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.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNet.Antiforgery.FunctionalTests
+{
+ public class AntiforgerySampleTests : IClassFixture
+ {
+ public AntiforgerySampleTests(AntiForgerySampleTestFixture fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [Fact]
+ public async Task ItemsPage_SetsXSRFTokens()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Index.html");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var cookie = RetrieveAntiforgeryCookie(response);
+ Assert.NotNull(cookie.Value);
+
+ var token = RetrieveAntiforgeryToken(response);
+ Assert.NotNull(token.Value);
+ }
+
+ [Fact]
+ public async Task PostItem_NeedsHeader()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/items");
+
+ // Act
+ var exception = await Assert.ThrowsAsync(async () =>
+ {
+ var response = await Client.SendAsync(httpRequestMessage);
+ });
+
+ // Assert
+ Assert.Contains("required antiforgery cookie", exception.Message);
+ }
+
+ [Fact]
+ public async Task PostItem_XSRFWorks()
+ {
+ // Arrange
+ var content = new StringContent("{'name': 'Todoitem'}");
+ var httpResponse = await Client.GetAsync("http://localhost/Index.html");
+
+ var cookie = RetrieveAntiforgeryCookie(httpResponse);
+ var token = RetrieveAntiforgeryToken(httpResponse);
+
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/items");
+
+ httpRequestMessage.Headers.Add("X-XSRF-TOKEN", token.Value);
+ httpRequestMessage.Headers.Add("Cookie", $"{cookie.Key}={cookie.Value}");
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode);
+ Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
+ }
+
+ private static KeyValuePair RetrieveAntiforgeryToken(HttpResponseMessage response)
+ {
+ return GetCookie(response, 1);
+ }
+
+ private static KeyValuePair RetrieveAntiforgeryCookie(HttpResponseMessage response)
+ {
+ return GetCookie(response, 0);
+ }
+
+ private static KeyValuePair GetCookie(HttpResponseMessage response, int index)
+ {
+ var setCookieArray = response.Headers.GetValues("Set-Cookie").ToArray();
+ var cookie = setCookieArray[index].Split(';').First().Split('=');
+ var cookieKey = cookie[0];
+ var cookieData = cookie[1];
+
+ return new KeyValuePair(cookieKey, cookieData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Antiforgery.FunctionalTests/Microsoft.AspNet.Antiforgery.FunctionalTests.xproj b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/Microsoft.AspNet.Antiforgery.FunctionalTests.xproj
new file mode 100644
index 0000000000..775501b0ed
--- /dev/null
+++ b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/Microsoft.AspNet.Antiforgery.FunctionalTests.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0.24720
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 8b288810-5a96-4af5-9836-8ba2d2953203
+ FunctionalTests
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+ 2.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Antiforgery.FunctionalTests/project.json b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/project.json
new file mode 100644
index 0000000000..0df539fa57
--- /dev/null
+++ b/test/Microsoft.AspNet.Antiforgery.FunctionalTests/project.json
@@ -0,0 +1,25 @@
+{
+ "dependencies": {
+ "AntiforgerySample": "1.0.0-*",
+ "Microsoft.AspNet.TestHost": "1.0.0-*",
+ "Microsoft.AspNet.Testing": "1.0.0-*",
+ "xunit.runner.aspnet": "2.0.0-aspnet-*"
+ },
+ "commands": {
+ "run": "xunit.runner.aspnet",
+ "test": "xunit.runner.aspnet"
+ },
+ "frameworks": {
+ "dnx451": {
+ "dependencies": {
+ "Moq": "4.2.1312.1622",
+ "System.Net.Http": "4.0.1-rc2-23621"
+ }
+ },
+ "dnxcore50": {
+ "dependencies": {
+ "moq.netcore": "4.4.0-beta8"
+ }
+ }
+ }
+}