diff --git a/src/Microsoft.AspNetCore.Antiforgery/AntiforgeryOptions.cs b/src/Microsoft.AspNetCore.Antiforgery/AntiforgeryOptions.cs index 2c05e1dc1c..98f4968c1e 100644 --- a/src/Microsoft.AspNetCore.Antiforgery/AntiforgeryOptions.cs +++ b/src/Microsoft.AspNetCore.Antiforgery/AntiforgeryOptions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Antiforgery { @@ -45,6 +46,18 @@ namespace Microsoft.AspNetCore.Antiforgery } } + /// + /// The path set on the cookie. If it's null, the "path" attribute on the cookie is set to current + /// request's value. + /// + public PathString? CookiePath { get; set; } + + /// + /// The domain set on the cookie. By default its null which results in the "domain" attribute not being + /// set. + /// + public string CookieDomain { get; set; } + /// /// Specifies the name of the antiforgery token field that is used by the antiforgery system. /// diff --git a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs index 4b30e8ced9..9e3658fa93 100644 --- a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs +++ b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs @@ -71,7 +71,8 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal var options = new CookieOptions(); options.HttpOnly = true; - options.Path = httpContext.Request.PathBase; + options.Path = _options.CookiePath ?? httpContext.Request.PathBase; + options.Domain = _options.CookieDomain; // Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default // value of newCookie.Secure is populated out of band. if (_options.RequireSsl) diff --git a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs index c48c0c9ead..fb6927f535 100644 --- a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs +++ b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs @@ -308,6 +308,76 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal Assert.Equal(requestPathBase, cookies.Options.Path); } + [Fact] + public void SaveCookieToken_NonNullAntiforgeryOptionsCookiePath_UsesOptionsCookiePath() + { + // Arrange + var expectedCookiePath = "/"; + var requestPathBase = "/vdir1"; + var token = "serialized-value"; + var cookies = new MockResponseCookieCollection(); + var httpContext = new Mock(); + httpContext + .Setup(hc => hc.Response.Cookies) + .Returns(cookies); + httpContext + .SetupGet(hc => hc.Request.PathBase) + .Returns(requestPathBase); + httpContext + .SetupGet(hc => hc.Request.Path) + .Returns("/index.html"); + var options = new AntiforgeryOptions(); + options.CookieName = _cookieName; + options.CookiePath = expectedCookiePath; + var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); + + // Act + tokenStore.SaveCookieToken(httpContext.Object, token); + + // Assert + Assert.Equal(1, cookies.Count); + Assert.NotNull(cookies); + Assert.Equal(_cookieName, cookies.Key); + Assert.Equal("serialized-value", cookies.Value); + Assert.True(cookies.Options.HttpOnly); + Assert.Equal(expectedCookiePath, cookies.Options.Path); + } + + [Fact] + public void SaveCookieToken_NonNullAntiforgeryOptionsCookieDomain_UsesOptionsCookieDomain() + { + // Arrange + var expectedCookieDomain = "microsoft.com"; + var token = "serialized-value"; + var cookies = new MockResponseCookieCollection(); + var httpContext = new Mock(); + httpContext + .Setup(hc => hc.Response.Cookies) + .Returns(cookies); + httpContext + .SetupGet(hc => hc.Request.PathBase) + .Returns("/vdir1"); + httpContext + .SetupGet(hc => hc.Request.Path) + .Returns("/index.html"); + var options = new AntiforgeryOptions(); + options.CookieName = _cookieName; + options.CookieDomain = expectedCookieDomain; + var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); + + // Act + tokenStore.SaveCookieToken(httpContext.Object, token); + + // Assert + Assert.Equal(1, cookies.Count); + Assert.NotNull(cookies); + Assert.Equal(_cookieName, cookies.Key); + Assert.Equal("serialized-value", cookies.Value); + Assert.True(cookies.Options.HttpOnly); + Assert.Equal("/vdir1", cookies.Options.Path); + Assert.Equal(expectedCookieDomain, cookies.Options.Domain); + } + private HttpContext GetHttpContext(string cookieName, string cookieValue) { var cookies = new RequestCookieCollection(new Dictionary