diff --git a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs index 9e3658fa93..ada7752396 100644 --- a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs +++ b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs @@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal var options = new CookieOptions(); options.HttpOnly = true; - options.Path = _options.CookiePath ?? httpContext.Request.PathBase; + options.Path = _options.CookiePath ?? GetPathBase(httpContext); options.Domain = _options.CookieDomain; // Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default // value of newCookie.Secure is populated out of band. @@ -82,5 +82,15 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal httpContext.Response.Cookies.Append(_options.CookieName, token, options); } + + private string GetPathBase(HttpContext httpContext) + { + var pathBase = httpContext.Request.PathBase.ToString(); + if (string.IsNullOrEmpty(pathBase)) + { + pathBase = "/"; + } + return pathBase; + } } } diff --git a/test/Microsoft.AspNetCore.Antiforgery.FunctionalTests/AntiforgerySampleTest.cs b/test/Microsoft.AspNetCore.Antiforgery.FunctionalTests/AntiforgerySampleTest.cs index d49d8df723..52c46bd6b6 100644 --- a/test/Microsoft.AspNetCore.Antiforgery.FunctionalTests/AntiforgerySampleTest.cs +++ b/test/Microsoft.AspNetCore.Antiforgery.FunctionalTests/AntiforgerySampleTest.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.Net.Http.Headers; using Xunit; namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests @@ -26,11 +27,19 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests var response = await Client.GetAsync("http://localhost/Index.html"); // Assert - var cookie = RetrieveAntiforgeryCookie(response); - Assert.NotNull(cookie.Value); + var setCookieHeaderValue = RetrieveAntiforgeryCookie(response); + Assert.NotNull(setCookieHeaderValue); + Assert.False(string.IsNullOrEmpty(setCookieHeaderValue.Value)); + Assert.Null(setCookieHeaderValue.Domain); + Assert.Equal("/", setCookieHeaderValue.Path); + Assert.False(setCookieHeaderValue.Secure); - var token = RetrieveAntiforgeryToken(response); - Assert.NotNull(token.Value); + setCookieHeaderValue = RetrieveAntiforgeryToken(response); + Assert.NotNull(setCookieHeaderValue); + Assert.False(string.IsNullOrEmpty(setCookieHeaderValue.Value)); + Assert.Null(setCookieHeaderValue.Domain); + Assert.Equal("/", setCookieHeaderValue.Path); + Assert.False(setCookieHeaderValue.Secure); } [Fact] @@ -49,7 +58,7 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests }); // Assert - Assert.Contains($"The required antiforgery cookie \"{cookie.Key}\" is not present.", exception.Message); + Assert.Contains($"The required antiforgery cookie \"{cookie.Name}\" is not present.", exception.Message); } [Fact] @@ -63,8 +72,8 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/items"); + httpRequestMessage.Headers.Add("Cookie", $"{cookie.Name}={cookie.Value}"); httpRequestMessage.Headers.Add("X-XSRF-TOKEN", token.Value); - httpRequestMessage.Headers.Add("Cookie", $"{cookie.Key}={cookie.Value}"); // Act var response = await Client.SendAsync(httpRequestMessage); @@ -73,24 +82,20 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); } - private static KeyValuePair RetrieveAntiforgeryToken(HttpResponseMessage response) + private static SetCookieHeaderValue RetrieveAntiforgeryToken(HttpResponseMessage response) { - return GetCookie(response, 1); + return response.Headers.GetValues(HeaderNames.SetCookie) + .Select(setCookieValue => SetCookieHeaderValue.Parse(setCookieValue)) + .Where(setCookieHeaderValue => setCookieHeaderValue.Name == "XSRF-TOKEN") + .FirstOrDefault(); } - private static KeyValuePair RetrieveAntiforgeryCookie(HttpResponseMessage response) + private static SetCookieHeaderValue 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); + return response.Headers.GetValues(HeaderNames.SetCookie) + .Select(setCookieValue => SetCookieHeaderValue.Parse(setCookieValue)) + .Where(setCookieHeaderValue => setCookieHeaderValue.Name.StartsWith(".AspNetCore.Antiforgery.")) + .FirstOrDefault(); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs index fb6927f535..f328ff908b 100644 --- a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs +++ b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs @@ -274,10 +274,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal } [Theory] - [InlineData("/")] - [InlineData("/vdir1")] - [InlineData("/vdir1/vdir2")] - public void SaveCookieToken_SetsCookieWithApproriatePathBase(string requestPathBase) + [InlineData(null, "/")] + [InlineData("", "/")] + [InlineData("/", "/")] + [InlineData("/vdir1", "/vdir1")] + [InlineData("/vdir1/vdir2", "/vdir1/vdir2")] + public void SaveCookieToken_SetsCookieWithApproriatePathBase(string requestPathBase, string expectedCookiePath) { // Arrange var token = "serialized-value"; @@ -305,7 +307,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal Assert.Equal(_cookieName, cookies.Key); Assert.Equal("serialized-value", cookies.Value); Assert.True(cookies.Options.HttpOnly); - Assert.Equal(requestPathBase, cookies.Options.Path); + Assert.Equal(expectedCookiePath, cookies.Options.Path); } [Fact]