diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/CookieTempDataProviderOptions.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/CookieTempDataProviderOptions.cs index f6140e4212..0b2badbd32 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/CookieTempDataProviderOptions.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/CookieTempDataProviderOptions.cs @@ -17,7 +17,13 @@ namespace Microsoft.AspNetCore.Mvc Name = CookieTempDataProvider.CookieName, HttpOnly = true, SameSite = SameSiteMode.Strict, - SecurePolicy = CookieSecurePolicy.SameAsRequest, + + // Some browsers do not allow non-secure endpoints to set cookies with a 'secure' flag or overwrite cookies + // whose 'secure' flag is set (http://httpwg.org/http-extensions/draft-ietf-httpbis-cookie-alone.html). + // Since mixing secure and non-secure endpoints is a common scenario in applications, we are relaxing the + // restriction on secure policy on some cookies by setting to 'None'. Cookies related to authentication or + // authorization use a stronger policy than 'None'. + SecurePolicy = CookieSecurePolicy.None, }; /// diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TempDataInCookiesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TempDataInCookiesTest.cs index 7344842d64..b27db0d5dd 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TempDataInCookiesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/TempDataInCookiesTest.cs @@ -137,7 +137,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests [Theory] [InlineData(true)] [InlineData(false)] - public async Task CookieTempDataProviderCookie_SetsSecureAttributeOnCookie_OnlyIfRequestIsSecure(bool secureRequest) + public async Task CookieTempDataProviderCookie_DoesNotSetsSecureAttributeOnCookie(bool secureRequest) { // Arrange var protocol = secureRequest ? "https" : "http"; @@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.NotNull(setCookieHeader); Assert.Equal("/", setCookieHeader.Path); Assert.Null(setCookieHeader.Domain.Value); - Assert.Equal(secureRequest, setCookieHeader.Secure); + Assert.False(setCookieHeader.Secure); Assert.Null(setCookieHeader.Expires); } } diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewFeatures/CookieTempDataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewFeatures/CookieTempDataProviderTest.cs index f0f2203e6d..1fe6b0920b 100644 --- a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewFeatures/CookieTempDataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/ViewFeatures/CookieTempDataProviderTest.cs @@ -162,9 +162,16 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures } [Theory] - [InlineData(true)] - [InlineData(false)] - public void SaveTempData_SetsSecureAttributeOnCookie_OnlyIfRequestIsSecure(bool isSecure) + [InlineData(true, CookieSecurePolicy.None, false)] + [InlineData(false, CookieSecurePolicy.None, false)] + [InlineData(true, CookieSecurePolicy.Always, true)] + [InlineData(false, CookieSecurePolicy.Always, true)] + [InlineData(true, CookieSecurePolicy.SameAsRequest, true)] + [InlineData(false, CookieSecurePolicy.SameAsRequest, false)] + public void SaveTempData_HonorsCookieSecurePolicy_OnOptions( + bool isRequestSecure, + CookieSecurePolicy cookieSecurePolicy, + bool expectedSecureFlag) { // Arrange var values = new Dictionary(); @@ -173,7 +180,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures var expectedDataToProtect = tempDataProviderStore.Serialize(values); var expectedDataInCookie = WebEncoders.Base64UrlEncode(expectedDataToProtect); var dataProtector = new PassThroughDataProtector(); - var tempDataProvider = GetProvider(dataProtector); + var options = new CookieTempDataProviderOptions(); + options.Cookie.SecurePolicy = cookieSecurePolicy; + var tempDataProvider = GetProvider(dataProtector, options); var responseCookies = new MockResponseCookieCollection(); var httpContext = new Mock(); httpContext @@ -181,7 +190,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures .Returns("/"); httpContext .SetupGet(hc => hc.Request.IsHttps) - .Returns(isSecure); + .Returns(isRequestSecure); httpContext .Setup(hc => hc.Response.Cookies) .Returns(responseCookies); @@ -196,7 +205,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures Assert.Equal(expectedDataInCookie, cookieInfo.Value); Assert.Equal(expectedDataToProtect, dataProtector.PlainTextToProtect); Assert.Equal("/", cookieInfo.Options.Path); - Assert.Equal(isSecure, cookieInfo.Options.Secure); + Assert.Equal(expectedSecureFlag, cookieInfo.Options.Secure); Assert.True(cookieInfo.Options.HttpOnly); Assert.Null(cookieInfo.Options.Expires); Assert.Null(cookieInfo.Options.Domain);