diff --git a/src/Microsoft.AspNet.Session/CookieSecureOption.cs b/src/Microsoft.AspNet.Session/CookieSecureOption.cs
new file mode 100644
index 0000000..0395024
--- /dev/null
+++ b/src/Microsoft.AspNet.Session/CookieSecureOption.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Open Technologies, Inc. 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.Threading.Tasks;
+
+namespace Microsoft.AspNet.Session
+{
+ ///
+ /// Determines how the identity cookie's security property is set.
+ ///
+ public enum CookieSecureOption
+ {
+ ///
+ /// If the URI that provides the cookie is HTTPS, then the cookie will only be returned to the server on
+ /// subsequent HTTPS requests. Otherwise if the URI that provides the cookie is HTTP, then the cookie will
+ /// be returned to the server on all HTTP and HTTPS requests. This is the default value because it ensures
+ /// HTTPS for all authenticated requests on deployed servers, and also supports HTTP for localhost development
+ /// and for servers that do not have HTTPS support.
+ ///
+ SameAsRequest,
+
+ ///
+ /// CookieOptions.Secure is never marked true. Use this value when your login page is HTTPS, but other pages
+ /// on the site which are HTTP also require authentication information. This setting is not recommended because
+ /// the authentication information provided with an HTTP request may be observed and used by other computers
+ /// on your local network or wireless connection.
+ ///
+ Never,
+
+ ///
+ /// CookieOptions.Secure is always marked true. Use this value when your login page and all subsequent pages
+ /// requiring the authenticated identity are HTTPS. Local development will also need to be done with HTTPS urls.
+ ///
+ Always,
+ }
+}
diff --git a/src/Microsoft.AspNet.Session/SessionDefaults.cs b/src/Microsoft.AspNet.Session/SessionDefaults.cs
index 7fb15b3..84425ad 100644
--- a/src/Microsoft.AspNet.Session/SessionDefaults.cs
+++ b/src/Microsoft.AspNet.Session/SessionDefaults.cs
@@ -9,5 +9,7 @@ public static class SessionDefaults
{
public static string CookieName = ".AspNet.Session";
public static string CookiePath = "/";
+ public static CookieSecureOption CookieSecure = CookieSecureOption.SameAsRequest;
+ public static bool CookieHTTPOnly = true;
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Session/SessionMiddleware.cs b/src/Microsoft.AspNet.Session/SessionMiddleware.cs
index b5db2ab..e8d918c 100644
--- a/src/Microsoft.AspNet.Session/SessionMiddleware.cs
+++ b/src/Microsoft.AspNet.Session/SessionMiddleware.cs
@@ -130,6 +130,15 @@ private void SetCookie()
Path = _options.CookiePath ?? SessionDefaults.CookiePath,
};
+ if (_options.CookieSecure == CookieSecureOption.SameAsRequest)
+ {
+ cookieOptions.Secure = _context.Request.IsHttps;
+ }
+ else
+ {
+ cookieOptions.Secure = _options.CookieSecure == CookieSecureOption.Always;
+ }
+
_context.Response.Cookies.Append(_options.CookieName, _sessionKey, cookieOptions);
_context.Response.Headers.Set(
diff --git a/src/Microsoft.AspNet.Session/SessionOptions.cs b/src/Microsoft.AspNet.Session/SessionOptions.cs
index 345df18..a6965c4 100644
--- a/src/Microsoft.AspNet.Session/SessionOptions.cs
+++ b/src/Microsoft.AspNet.Session/SessionOptions.cs
@@ -27,7 +27,7 @@ public class SessionOptions
/// default is true, which means the cookie will only be passed to HTTP requests and is not made available
/// to script on the page.
///
- public bool CookieHttpOnly { get; set; } = true;
+ public bool CookieHttpOnly { get; set; } = SessionDefaults.CookieHTTPOnly;
///
/// The IdleTimeout indicates how long the session can be idle before its contents are abandoned. Each session access
@@ -35,6 +35,13 @@ public class SessionOptions
///
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(20);
+ ///
+ /// Determines if the cookie should only be transmitted on HTTPS request. The default is to limit the cookie
+ /// to HTTPS requests if the page which is doing the SignIn is also HTTPS. If you have an HTTPS sign in page
+ /// and portions of your site are HTTP you may need to change this value.
+ ///
+ public CookieSecureOption CookieSecure { get; set; } = SessionDefaults.CookieSecure;
+
///
/// Gets or sets the session storage manager. This overrides any session store passed into the middleware constructor.
///
diff --git a/test/Microsoft.AspNet.Session.Tests/SessionTests.cs b/test/Microsoft.AspNet.Session.Tests/SessionTests.cs
index be29618..8d6ec20 100644
--- a/test/Microsoft.AspNet.Session.Tests/SessionTests.cs
+++ b/test/Microsoft.AspNet.Session.Tests/SessionTests.cs
@@ -268,5 +268,140 @@ public async Task ExpiredSession_LogsWarning()
Assert.Equal(LogLevel.Warning, sink.Writes[1].LogLevel);
}
}
+
+ [Fact]
+ public async Task SettingSecureCookieOptionToNeverWillEnsureSecureFlagNotSet()
+ {
+ using (var server = TestServer.Create(app =>
+ {
+ app.UseInMemorySession(null, cookieoption => cookieoption.CookieSecure = CookieSecureOption.Never);
+ app.Run(context =>
+ {
+ Assert.Null(context.Session.GetString("Key"));
+ context.Session.SetString("Key", "Value");
+ Assert.Equal("Value", context.Session.GetString("Key"));
+ return Task.FromResult(0);
+ });
+ },
+ services => services.AddOptions()))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ IEnumerable values;
+ Assert.True(response.Headers.TryGetValues("Set-Cookie", out values));
+ Assert.Equal(1, values.Count());
+ Assert.True(!string.IsNullOrWhiteSpace(values.First()));
+ Assert.DoesNotContain("secure", values.First());
+ }
+ }
+
+ [Fact]
+ public async Task SettingSecureCookieOptionToAlwaysWillEnsureSecureFlagSet()
+ {
+ using (var server = TestServer.Create(app =>
+ {
+ app.UseInMemorySession(null, cookieoption => cookieoption.CookieSecure = CookieSecureOption.Always);
+ app.Run(context =>
+ {
+ Assert.Null(context.Session.GetString("Key"));
+ context.Session.SetString("Key", "Value");
+ Assert.Equal("Value", context.Session.GetString("Key"));
+ return Task.FromResult(0);
+ });
+ },
+ services => services.AddOptions()))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ IEnumerable values;
+ Assert.True(response.Headers.TryGetValues("Set-Cookie", out values));
+ Assert.Equal(1, values.Count());
+ Assert.True(!string.IsNullOrWhiteSpace(values.First()));
+ Assert.Contains("secure", values.First());
+ }
+ }
+
+ [Fact]
+ public async Task SettingSecureCookieOptionToSameAsRequestWillEnsureSecureFlagNotSetWhileNotUsingSSL()
+ {
+ using (var server = TestServer.Create(app =>
+ {
+ app.UseInMemorySession(null, cookieoption => cookieoption.CookieSecure = CookieSecureOption.SameAsRequest);
+ app.Run(context =>
+ {
+ Assert.Null(context.Session.GetString("Key"));
+ context.Session.SetString("Key", "Value");
+ Assert.Equal("Value", context.Session.GetString("Key"));
+ return Task.FromResult(0);
+ });
+ },
+ services => services.AddOptions()))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ IEnumerable values;
+ Assert.True(response.Headers.TryGetValues("Set-Cookie", out values));
+ Assert.Equal(1, values.Count());
+ Assert.True(!string.IsNullOrWhiteSpace(values.First()));
+ Assert.DoesNotContain("secure", values.First());
+ }
+ }
+
+ [Fact]
+ public async Task SettingHTTPOnlyCookieOptionToTrueWillEnsureHTTPOnlyFlagSet()
+ {
+ using (var server = TestServer.Create(app =>
+ {
+ app.UseInMemorySession(null, cookieoption => cookieoption.CookieHttpOnly = true);
+ app.Run(context =>
+ {
+ Assert.Null(context.Session.GetString("Key"));
+ context.Session.SetString("Key", "Value");
+ Assert.Equal("Value", context.Session.GetString("Key"));
+ return Task.FromResult(0);
+ });
+ },
+ services => services.AddOptions()))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ IEnumerable values;
+ Assert.True(response.Headers.TryGetValues("Set-Cookie", out values));
+ Assert.Equal(1, values.Count());
+ Assert.True(!string.IsNullOrWhiteSpace(values.First()));
+ Assert.Contains("httponly", values.First());
+ }
+ }
+
+ [Fact]
+ public async Task SettingHTTPOnlyCookieOptionToFalseWillEnsureHTTPOnlyFlagNotSet()
+ {
+ using (var server = TestServer.Create(app =>
+ {
+ app.UseInMemorySession(null, cookieoption => cookieoption.CookieHttpOnly = false);
+ app.Run(context =>
+ {
+ Assert.Null(context.Session.GetString("Key"));
+ context.Session.SetString("Key", "Value");
+ Assert.Equal("Value", context.Session.GetString("Key"));
+ return Task.FromResult(0);
+ });
+ },
+ services => services.AddOptions()))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ IEnumerable values;
+ Assert.True(response.Headers.TryGetValues("Set-Cookie", out values));
+ Assert.Equal(1, values.Count());
+ Assert.True(!string.IsNullOrWhiteSpace(values.First()));
+ Assert.DoesNotContain("httponly", values.First());
+ }
+ }
}
}
\ No newline at end of file