#118 - Use common cookie header formatters.

This commit is contained in:
Chris Ross 2015-04-13 15:56:14 -07:00
parent a3b2d2c3eb
commit 99f3aa197f
16 changed files with 64 additions and 53 deletions

View File

@ -10,6 +10,7 @@ namespace CookieSample
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
} }

View File

@ -11,6 +11,7 @@ namespace CookieSessionSample
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
} }

View File

@ -12,6 +12,7 @@ namespace OpenIdConnectSample
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.Configure<ExternalAuthenticationOptions>(options => services.Configure<ExternalAuthenticationOptions>(options =>
{ {

View File

@ -18,6 +18,7 @@ namespace CookieSample
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.Configure<ExternalAuthenticationOptions>(options => services.Configure<ExternalAuthenticationOptions>(options =>
{ {

View File

@ -9,6 +9,7 @@ using Microsoft.AspNet.DataProtection;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
using Microsoft.Framework.Logging; using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.WebEncoders;
namespace Microsoft.AspNet.Authentication.Cookies namespace Microsoft.AspNet.Authentication.Cookies
{ {
@ -20,6 +21,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
[NotNull] RequestDelegate next, [NotNull] RequestDelegate next,
[NotNull] IDataProtectionProvider dataProtectionProvider, [NotNull] IDataProtectionProvider dataProtectionProvider,
[NotNull] ILoggerFactory loggerFactory, [NotNull] ILoggerFactory loggerFactory,
[NotNull] IUrlEncoder urlEncoder,
[NotNull] IOptions<CookieAuthenticationOptions> options, [NotNull] IOptions<CookieAuthenticationOptions> options,
ConfigureOptions<CookieAuthenticationOptions> configureOptions) ConfigureOptions<CookieAuthenticationOptions> configureOptions)
: base(next, options, configureOptions) : base(next, options, configureOptions)
@ -40,7 +42,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
} }
if (Options.CookieManager == null) if (Options.CookieManager == null)
{ {
Options.CookieManager = new ChunkingCookieManager(); Options.CookieManager = new ChunkingCookieManager(urlEncoder);
} }
_logger = loggerFactory.CreateLogger(typeof(CookieAuthenticationMiddleware).FullName); _logger = loggerFactory.CreateLogger(typeof(CookieAuthenticationMiddleware).FullName);

View File

@ -20,6 +20,7 @@ namespace Microsoft.Framework.DependencyInjection
public static IServiceCollection ConfigureCookieAuthentication([NotNull] this IServiceCollection services, [NotNull] Action<CookieAuthenticationOptions> configure, string optionsName) public static IServiceCollection ConfigureCookieAuthentication([NotNull] this IServiceCollection services, [NotNull] Action<CookieAuthenticationOptions> configure, string optionsName)
{ {
services.AddWebEncoders();
return services.Configure(configure, optionsName); 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) public static IServiceCollection ConfigureCookieAuthentication([NotNull] this IServiceCollection services, [NotNull] IConfiguration config, string optionsName)
{ {
services.AddWebEncoders();
return services.Configure<CookieAuthenticationOptions>(config, optionsName); return services.Configure<CookieAuthenticationOptions>(config, optionsName);
} }
} }

View File

@ -7,6 +7,8 @@ using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
{ {
@ -16,12 +18,13 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
/// </summary> /// </summary>
public class ChunkingCookieManager : ICookieManager 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. // Lowest common denominator. Safari has the lowest known limit (4093), and we leave little extra just in case.
// See http://browsercookielimits.x64.me/. // See http://browsercookielimits.x64.me/.
ChunkSize = 4090; ChunkSize = 4090;
ThrowForPartialCookies = true; ThrowForPartialCookies = true;
Encoder = urlEncoder ?? UrlEncoder.Default;
} }
/// <summary> /// <summary>
@ -38,6 +41,8 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
/// </summary> /// </summary>
public bool ThrowForPartialCookies { get; set; } public bool ThrowForPartialCookies { get; set; }
private IUrlEncoder Encoder { get; set; }
// Parse the "chunks:XX" to determine how many chunks there should be. // Parse the "chunks:XX" to determine how many chunks there should be.
private static int ParseChunksCount(string value) private static int ParseChunksCount(string value)
{ {
@ -119,22 +124,18 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
/// <param name="options"></param> /// <param name="options"></param>
public void AppendResponseCookie([NotNull] HttpContext context, [NotNull] string key, string value, [NotNull] CookieOptions options) public void AppendResponseCookie([NotNull] HttpContext context, [NotNull] string key, string value, [NotNull] CookieOptions options)
{ {
var domainHasValue = !string.IsNullOrEmpty(options.Domain); var escapedKey = Encoder.UrlEncode(key);
var pathHasValue = !string.IsNullOrEmpty(options.Path);
var expiresHasValue = options.Expires.HasValue;
var escapedKey = Uri.EscapeDataString(key); var template = new SetCookieHeaderValue(escapedKey)
var prefix = escapedKey + "="; {
Domain = options.Domain,
Expires = options.Expires,
HttpOnly = options.HttpOnly,
Path = options.Path,
Secure = options.Secure,
};
var suffix = string.Concat( var templateLength = template.ToString().Length;
!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");
value = value ?? string.Empty; value = value ?? string.Empty;
var quoted = false; var quoted = false;
@ -143,19 +144,16 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
quoted = true; quoted = true;
value = RemoveQuotes(value); value = RemoveQuotes(value);
} }
var escapedValue = Uri.EscapeDataString(value); var escapedValue = Encoder.UrlEncode(value);
// Normal cookie // Normal cookie
var responseHeaders = context.Response.Headers; 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( template.Value = quoted ? Quote(escapedValue) : escapedValue;
prefix, responseHeaders.AppendValues(Constants.Headers.SetCookie, template.ToString());
quoted ? Quote(escapedValue) : escapedValue,
suffix);
responseHeaders.AppendValues(Constants.Headers.SetCookie, setCookieValue);
} }
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". // 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 // 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: CookieNameC1="Segment1"; path=/
// Set-Cookie: CookieNameC2="Segment2"; path=/ // Set-Cookie: CookieNameC2="Segment2"; path=/
// Set-Cookie: CookieNameC3="Segment3"; 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); 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 chunks = new string[cookieChunkCount];
var offset = 0; var offset = 0;
@ -183,15 +182,9 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
var segment = escapedValue.Substring(offset, length); var segment = escapedValue.Substring(offset, length);
offset += length; offset += length;
chunks[chunkId - 1] = string.Concat( template.Name = escapedKey + "C" + chunkId.ToString(CultureInfo.InvariantCulture);
escapedKey, template.Value = quoted ? Quote(segment) : segment;
"C", chunks[chunkId - 1] = template.ToString();
chunkId.ToString(CultureInfo.InvariantCulture),
"=",
quoted ? "\"" : string.Empty,
segment,
quoted ? "\"" : string.Empty,
suffix);
} }
responseHeaders.AppendValues(Constants.Headers.SetCookie, chunks); responseHeaders.AppendValues(Constants.Headers.SetCookie, chunks);
} }
@ -206,7 +199,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
/// <param name="options"></param> /// <param name="options"></param>
public void DeleteCookie([NotNull] HttpContext context, [NotNull] string key, [NotNull] CookieOptions options) 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>(); var keys = new List<string>();
keys.Add(escapedKey + "="); keys.Add(escapedKey + "=");
@ -260,7 +253,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
for (int i = 1; i <= chunks; i++) for (int i = 1; i <= chunks; i++)
{ {
AppendResponseCookie( AppendResponseCookie(
context, context,
key + "C" + i.ToString(CultureInfo.InvariantCulture), key + "C" + i.ToString(CultureInfo.InvariantCulture),
string.Empty, string.Empty,
new CookieOptions() new CookieOptions()

View File

@ -4,6 +4,7 @@
"dependencies": { "dependencies": {
"Microsoft.AspNet.Authentication": "1.0.0-*", "Microsoft.AspNet.Authentication": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.WebEncoders": "1.0.0-*",
"Newtonsoft.Json": "6.0.6" "Newtonsoft.Json": "6.0.6"
}, },
"frameworks": { "frameworks": {

View File

@ -573,6 +573,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
if (claimsTransform != null) if (claimsTransform != null)
{ {

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
HttpContext context = new DefaultHttpContext(); HttpContext context = new DefaultHttpContext();
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 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"); IList<string> values = context.Response.Headers.GetValues("Set-Cookie");
Assert.Equal(1, values.Count); Assert.Equal(1, values.Count);
Assert.Equal("TestCookie=" + testString + "; path=/", values[0]); Assert.Equal("TestCookie=" + testString + "; path=/", values[0]);
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
HttpContext context = new DefaultHttpContext(); HttpContext context = new DefaultHttpContext();
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 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"); IList<string> values = context.Response.Headers.GetValues("Set-Cookie");
Assert.Equal(9, values.Count); Assert.Equal(9, values.Count);
Assert.Equal(new[] Assert.Equal(new[]
@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
HttpContext context = new DefaultHttpContext(); HttpContext context = new DefaultHttpContext();
string testString = "\"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\""; 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"); IList<string> values = context.Response.Headers.GetValues("Set-Cookie");
Assert.Equal(9, values.Count); Assert.Equal(9, values.Count);
Assert.Equal(new[] Assert.Equal(new[]
@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
"TestCookieC6=JKLMNOPQR", "TestCookieC6=JKLMNOPQR",
"TestCookieC7=STUVWXYZ"); "TestCookieC7=STUVWXYZ");
string result = new ChunkingCookieManager().GetRequestCookie(context, "TestCookie"); string result = new ChunkingCookieManager(null).GetRequestCookie(context, "TestCookie");
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Assert.Equal(testString, result); Assert.Equal(testString, result);
} }
@ -101,7 +101,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
"TestCookieC6=\"JKLMNOPQR\"", "TestCookieC6=\"JKLMNOPQR\"",
"TestCookieC7=\"STUVWXYZ\""); "TestCookieC7=\"STUVWXYZ\"");
string result = new ChunkingCookieManager().GetRequestCookie(context, "TestCookie"); string result = new ChunkingCookieManager(null).GetRequestCookie(context, "TestCookie");
string testString = "\"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\""; string testString = "\"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\"";
Assert.Equal(testString, result); Assert.Equal(testString, result);
} }
@ -120,7 +120,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
"TestCookieC6=JKLMNOPQR", "TestCookieC6=JKLMNOPQR",
"TestCookieC7=STUVWXYZ"); "TestCookieC7=STUVWXYZ");
Assert.Throws<FormatException>(() => new ChunkingCookieManager().GetRequestCookie(context, "TestCookie")); Assert.Throws<FormatException>(() => new ChunkingCookieManager(null).GetRequestCookie(context, "TestCookie"));
} }
[Fact] [Fact]
@ -137,7 +137,7 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
"TestCookieC6=JKLMNOPQR", "TestCookieC6=JKLMNOPQR",
"TestCookieC7=STUVWXYZ"); "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"; string testString = "chunks:7";
Assert.Equal(testString, result); Assert.Equal(testString, result);
} }
@ -148,19 +148,19 @@ namespace Microsoft.AspNet.Authentication.Cookies.Infrastructure
HttpContext context = new DefaultHttpContext(); HttpContext context = new DefaultHttpContext();
context.Request.Headers.AppendValues("Cookie", "TestCookie=chunks:7"); 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"); var cookies = context.Response.Headers.GetValues("Set-Cookie");
Assert.Equal(8, cookies.Count); Assert.Equal(8, cookies.Count);
Assert.Equal(new[] Assert.Equal(new[]
{ {
"TestCookie=; 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=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
"TestCookieC2=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
"TestCookieC3=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "TestCookieC3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
"TestCookieC4=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "TestCookieC4=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
"TestCookieC5=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "TestCookieC5=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
"TestCookieC6=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "TestCookieC6=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
"TestCookieC7=; domain=foo.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", "TestCookieC7=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
}, cookies); }, cookies);
} }
} }

View File

@ -29,6 +29,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.ConfigureFacebookAuthentication(options => services.ConfigureFacebookAuthentication(options =>
{ {
@ -74,6 +75,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.ConfigureFacebookAuthentication(options => services.ConfigureFacebookAuthentication(options =>
{ {

View File

@ -530,6 +530,7 @@ namespace Microsoft.AspNet.Authentication.Google
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.Configure<ExternalAuthenticationOptions>(options => services.Configure<ExternalAuthenticationOptions>(options =>
{ {

View File

@ -177,6 +177,7 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.Configure<ExternalAuthenticationOptions>(options => services.Configure<ExternalAuthenticationOptions>(options =>
{ {

View File

@ -436,6 +436,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
} }
); );
@ -454,6 +455,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
} }
); );

View File

@ -234,6 +234,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.Configure<ExternalAuthenticationOptions>(options => services.Configure<ExternalAuthenticationOptions>(options =>
{ {

View File

@ -123,6 +123,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
}, },
services => services =>
{ {
services.AddWebEncoders();
services.AddDataProtection(); services.AddDataProtection();
services.Configure<ExternalAuthenticationOptions>(options => services.Configure<ExternalAuthenticationOptions>(options =>
{ {