#118 - Use common cookie header formatters.
This commit is contained in:
parent
a3b2d2c3eb
commit
99f3aa197f
|
|
@ -10,6 +10,7 @@ namespace CookieSample
|
|||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace CookieSessionSample
|
|||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ namespace OpenIdConnectSample
|
|||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.Configure<ExternalAuthenticationOptions>(options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace CookieSample
|
|||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.Configure<ExternalAuthenticationOptions>(options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNet.DataProtection;
|
|||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Cookies
|
||||
{
|
||||
|
|
@ -20,6 +21,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
[NotNull] RequestDelegate next,
|
||||
[NotNull] IDataProtectionProvider dataProtectionProvider,
|
||||
[NotNull] ILoggerFactory loggerFactory,
|
||||
[NotNull] IUrlEncoder urlEncoder,
|
||||
[NotNull] IOptions<CookieAuthenticationOptions> options,
|
||||
ConfigureOptions<CookieAuthenticationOptions> configureOptions)
|
||||
: base(next, options, configureOptions)
|
||||
|
|
@ -40,7 +42,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
}
|
||||
if (Options.CookieManager == null)
|
||||
{
|
||||
Options.CookieManager = new ChunkingCookieManager();
|
||||
Options.CookieManager = new ChunkingCookieManager(urlEncoder);
|
||||
}
|
||||
|
||||
_logger = loggerFactory.CreateLogger(typeof(CookieAuthenticationMiddleware).FullName);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
|
||||
public static IServiceCollection ConfigureCookieAuthentication([NotNull] this IServiceCollection services, [NotNull] Action<CookieAuthenticationOptions> configure, string optionsName)
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
return services.Configure(configure, optionsName);
|
||||
}
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
|
||||
public static IServiceCollection ConfigureCookieAuthentication([NotNull] this IServiceCollection services, [NotNull] IConfiguration config, string optionsName)
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
return services.Configure<CookieAuthenticationOptions>(config, optionsName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ using System.Globalization;
|
|||
using System.Linq;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
||||
{
|
||||
|
|
@ -16,12 +18,13 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
/// </summary>
|
||||
public class ChunkingCookieManager : ICookieManager
|
||||
{
|
||||
public ChunkingCookieManager()
|
||||
public ChunkingCookieManager(IUrlEncoder urlEncoder)
|
||||
{
|
||||
// Lowest common denominator. Safari has the lowest known limit (4093), and we leave little extra just in case.
|
||||
// See http://browsercookielimits.x64.me/.
|
||||
ChunkSize = 4090;
|
||||
ThrowForPartialCookies = true;
|
||||
Encoder = urlEncoder ?? UrlEncoder.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -38,6 +41,8 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
/// </summary>
|
||||
public bool ThrowForPartialCookies { get; set; }
|
||||
|
||||
private IUrlEncoder Encoder { get; set; }
|
||||
|
||||
// Parse the "chunks:XX" to determine how many chunks there should be.
|
||||
private static int ParseChunksCount(string value)
|
||||
{
|
||||
|
|
@ -119,22 +124,18 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
/// <param name="options"></param>
|
||||
public void AppendResponseCookie([NotNull] HttpContext context, [NotNull] string key, string value, [NotNull] CookieOptions options)
|
||||
{
|
||||
var domainHasValue = !string.IsNullOrEmpty(options.Domain);
|
||||
var pathHasValue = !string.IsNullOrEmpty(options.Path);
|
||||
var expiresHasValue = options.Expires.HasValue;
|
||||
var escapedKey = Encoder.UrlEncode(key);
|
||||
|
||||
var escapedKey = Uri.EscapeDataString(key);
|
||||
var prefix = escapedKey + "=";
|
||||
var template = new SetCookieHeaderValue(escapedKey)
|
||||
{
|
||||
Domain = options.Domain,
|
||||
Expires = options.Expires,
|
||||
HttpOnly = options.HttpOnly,
|
||||
Path = options.Path,
|
||||
Secure = options.Secure,
|
||||
};
|
||||
|
||||
var suffix = string.Concat(
|
||||
!domainHasValue ? null : "; domain=",
|
||||
!domainHasValue ? null : options.Domain,
|
||||
!pathHasValue ? null : "; path=",
|
||||
!pathHasValue ? null : options.Path,
|
||||
!expiresHasValue ? null : "; expires=",
|
||||
!expiresHasValue ? null : options.Expires.Value.ToString("ddd, dd-MMM-yyyy HH:mm:ss ", CultureInfo.InvariantCulture) + "GMT",
|
||||
!options.Secure ? null : "; secure",
|
||||
!options.HttpOnly ? null : "; HttpOnly");
|
||||
var templateLength = template.ToString().Length;
|
||||
|
||||
value = value ?? string.Empty;
|
||||
var quoted = false;
|
||||
|
|
@ -143,19 +144,16 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
quoted = true;
|
||||
value = RemoveQuotes(value);
|
||||
}
|
||||
var escapedValue = Uri.EscapeDataString(value);
|
||||
var escapedValue = Encoder.UrlEncode(value);
|
||||
|
||||
// Normal cookie
|
||||
var responseHeaders = context.Response.Headers;
|
||||
if (!ChunkSize.HasValue || ChunkSize.Value > prefix.Length + escapedValue.Length + suffix.Length + (quoted ? 2 : 0))
|
||||
if (!ChunkSize.HasValue || ChunkSize.Value > templateLength + escapedValue.Length + (quoted ? 2 : 0))
|
||||
{
|
||||
var setCookieValue = string.Concat(
|
||||
prefix,
|
||||
quoted ? Quote(escapedValue) : escapedValue,
|
||||
suffix);
|
||||
responseHeaders.AppendValues(Constants.Headers.SetCookie, setCookieValue);
|
||||
template.Value = quoted ? Quote(escapedValue) : escapedValue;
|
||||
responseHeaders.AppendValues(Constants.Headers.SetCookie, template.ToString());
|
||||
}
|
||||
else if (ChunkSize.Value < prefix.Length + suffix.Length + (quoted ? 2 : 0) + 10)
|
||||
else if (ChunkSize.Value < templateLength + (quoted ? 2 : 0) + 10)
|
||||
{
|
||||
// 10 is the minimum data we want to put in an individual cookie, including the cookie chunk identifier "CXX".
|
||||
// No room for data, we can't chunk the options and name
|
||||
|
|
@ -169,10 +167,11 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
// Set-Cookie: CookieNameC1="Segment1"; path=/
|
||||
// Set-Cookie: CookieNameC2="Segment2"; path=/
|
||||
// Set-Cookie: CookieNameC3="Segment3"; path=/
|
||||
var dataSizePerCookie = ChunkSize.Value - prefix.Length - suffix.Length - (quoted ? 2 : 0) - 3; // Budget 3 chars for the chunkid.
|
||||
var dataSizePerCookie = ChunkSize.Value - templateLength - (quoted ? 2 : 0) - 3; // Budget 3 chars for the chunkid.
|
||||
var cookieChunkCount = (int)Math.Ceiling(escapedValue.Length * 1.0 / dataSizePerCookie);
|
||||
|
||||
responseHeaders.AppendValues(Constants.Headers.SetCookie, prefix + "chunks:" + cookieChunkCount.ToString(CultureInfo.InvariantCulture) + suffix);
|
||||
template.Value = "chunks:" + cookieChunkCount.ToString(CultureInfo.InvariantCulture);
|
||||
responseHeaders.AppendValues(Constants.Headers.SetCookie, template.ToString());
|
||||
|
||||
var chunks = new string[cookieChunkCount];
|
||||
var offset = 0;
|
||||
|
|
@ -183,15 +182,9 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
var segment = escapedValue.Substring(offset, length);
|
||||
offset += length;
|
||||
|
||||
chunks[chunkId - 1] = string.Concat(
|
||||
escapedKey,
|
||||
"C",
|
||||
chunkId.ToString(CultureInfo.InvariantCulture),
|
||||
"=",
|
||||
quoted ? "\"" : string.Empty,
|
||||
segment,
|
||||
quoted ? "\"" : string.Empty,
|
||||
suffix);
|
||||
template.Name = escapedKey + "C" + chunkId.ToString(CultureInfo.InvariantCulture);
|
||||
template.Value = quoted ? Quote(segment) : segment;
|
||||
chunks[chunkId - 1] = template.ToString();
|
||||
}
|
||||
responseHeaders.AppendValues(Constants.Headers.SetCookie, chunks);
|
||||
}
|
||||
|
|
@ -206,7 +199,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
/// <param name="options"></param>
|
||||
public void DeleteCookie([NotNull] HttpContext context, [NotNull] string key, [NotNull] CookieOptions options)
|
||||
{
|
||||
var escapedKey = Uri.EscapeDataString(key);
|
||||
var escapedKey = Encoder.UrlEncode(key);
|
||||
var keys = new List<string>();
|
||||
keys.Add(escapedKey + "=");
|
||||
|
||||
|
|
@ -260,7 +253,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
for (int i = 1; i <= chunks; i++)
|
||||
{
|
||||
AppendResponseCookie(
|
||||
context,
|
||||
context,
|
||||
key + "C" + i.ToString(CultureInfo.InvariantCulture),
|
||||
string.Empty,
|
||||
new CookieOptions()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"dependencies": {
|
||||
"Microsoft.AspNet.Authentication": "1.0.0-*",
|
||||
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" },
|
||||
"Microsoft.Framework.WebEncoders": "1.0.0-*",
|
||||
"Newtonsoft.Json": "6.0.6"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
|
|
@ -573,6 +573,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
if (claimsTransform != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
HttpContext context = new DefaultHttpContext();
|
||||
|
||||
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
new ChunkingCookieManager() { ChunkSize = null }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
|
||||
new ChunkingCookieManager(null) { ChunkSize = null }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
|
||||
IList<string> values = context.Response.Headers.GetValues("Set-Cookie");
|
||||
Assert.Equal(1, values.Count);
|
||||
Assert.Equal("TestCookie=" + testString + "; path=/", values[0]);
|
||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
HttpContext context = new DefaultHttpContext();
|
||||
|
||||
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
new ChunkingCookieManager() { ChunkSize = 30 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
|
||||
new ChunkingCookieManager(null) { ChunkSize = 30 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
|
||||
IList<string> values = context.Response.Headers.GetValues("Set-Cookie");
|
||||
Assert.Equal(9, values.Count);
|
||||
Assert.Equal(new[]
|
||||
|
|
@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
HttpContext context = new DefaultHttpContext();
|
||||
|
||||
string testString = "\"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\"";
|
||||
new ChunkingCookieManager() { ChunkSize = 32 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
|
||||
new ChunkingCookieManager(null) { ChunkSize = 32 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
|
||||
IList<string> values = context.Response.Headers.GetValues("Set-Cookie");
|
||||
Assert.Equal(9, values.Count);
|
||||
Assert.Equal(new[]
|
||||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
"TestCookieC6=JKLMNOPQR",
|
||||
"TestCookieC7=STUVWXYZ");
|
||||
|
||||
string result = new ChunkingCookieManager().GetRequestCookie(context, "TestCookie");
|
||||
string result = new ChunkingCookieManager(null).GetRequestCookie(context, "TestCookie");
|
||||
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
Assert.Equal(testString, result);
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
"TestCookieC6=\"JKLMNOPQR\"",
|
||||
"TestCookieC7=\"STUVWXYZ\"");
|
||||
|
||||
string result = new ChunkingCookieManager().GetRequestCookie(context, "TestCookie");
|
||||
string result = new ChunkingCookieManager(null).GetRequestCookie(context, "TestCookie");
|
||||
string testString = "\"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\"";
|
||||
Assert.Equal(testString, result);
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
"TestCookieC6=JKLMNOPQR",
|
||||
"TestCookieC7=STUVWXYZ");
|
||||
|
||||
Assert.Throws<FormatException>(() => new ChunkingCookieManager().GetRequestCookie(context, "TestCookie"));
|
||||
Assert.Throws<FormatException>(() => new ChunkingCookieManager(null).GetRequestCookie(context, "TestCookie"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -137,7 +137,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
"TestCookieC6=JKLMNOPQR",
|
||||
"TestCookieC7=STUVWXYZ");
|
||||
|
||||
string result = new ChunkingCookieManager() { ThrowForPartialCookies = false }.GetRequestCookie(context, "TestCookie");
|
||||
string result = new ChunkingCookieManager(null) { ThrowForPartialCookies = false }.GetRequestCookie(context, "TestCookie");
|
||||
string testString = "chunks:7";
|
||||
Assert.Equal(testString, result);
|
||||
}
|
||||
|
|
@ -148,19 +148,19 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
|
|||
HttpContext context = new DefaultHttpContext();
|
||||
context.Request.Headers.AppendValues("Cookie", "TestCookie=chunks:7");
|
||||
|
||||
new ChunkingCookieManager().DeleteCookie(context, "TestCookie", new CookieOptions() { Domain = "foo.com" });
|
||||
new ChunkingCookieManager(null).DeleteCookie(context, "TestCookie", new CookieOptions() { Domain = "foo.com" });
|
||||
var cookies = context.Response.Headers.GetValues("Set-Cookie");
|
||||
Assert.Equal(8, cookies.Count);
|
||||
Assert.Equal(new[]
|
||||
{
|
||||
"TestCookie=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookieC1=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookieC2=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookieC3=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookieC4=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookieC5=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookieC6=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookieC7=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT",
|
||||
"TestCookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
"TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
"TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
"TestCookieC3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
"TestCookieC4=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
"TestCookieC5=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
"TestCookieC6=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
"TestCookieC7=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
|
||||
}, cookies);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.ConfigureFacebookAuthentication(options =>
|
||||
{
|
||||
|
|
@ -74,6 +75,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.ConfigureFacebookAuthentication(options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -530,6 +530,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.Configure<ExternalAuthenticationOptions>(options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.Configure<ExternalAuthenticationOptions>(options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -436,6 +436,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
}
|
||||
);
|
||||
|
|
@ -454,6 +455,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.Configure<ExternalAuthenticationOptions>(options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
services.Configure<ExternalAuthenticationOptions>(options =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue