diff --git a/Security.sln b/Security.sln
index 486611009f..6367cfeadf 100644
--- a/Security.sln
+++ b/Security.sln
@@ -50,6 +50,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.CookiePoli
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Authentication.JwtBearer", "src\Microsoft.AspNet.Authentication.JwtBearer\Microsoft.AspNet.Authentication.JwtBearer.xproj", "{2755BFE5-7421-4A31-A644-F817DF5CAA98}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Owin.Security.Cookies.Interop", "src\Microsoft.Owin.Security.Cookies.Interop\Microsoft.Owin.Security.Cookies.Interop.xproj", "{21A56E78-31DE-4868-9778-7E4DBE2A4E35}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Owin.Security.Cookies.Interop.Test", "test\Microsoft.Owin.Security.Cookies.Interop.Test\Microsoft.Owin.Security.Cookies.Interop.Test.xproj", "{73E8E654-A2AC-4848-95F3-EB55512F6C39}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -270,6 +274,30 @@ Global
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|x86.ActiveCfg = Release|Any CPU
{2755BFE5-7421-4A31-A644-F817DF5CAA98}.Release|x86.Build.0 = Release|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Debug|x86.Build.0 = Debug|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Release|Any CPU.Build.0 = Release|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Release|x86.ActiveCfg = Release|Any CPU
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35}.Release|x86.Build.0 = Release|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Debug|x86.Build.0 = Debug|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|Any CPU.Build.0 = Release|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|x86.ActiveCfg = Release|Any CPU
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -293,5 +321,7 @@ Global
{86183DC3-02A8-4A68-8B60-71ECEC066E79} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{1790E052-646F-4529-B90E-6FEA95520D69} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
{2755BFE5-7421-4A31-A644-F817DF5CAA98} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
+ {21A56E78-31DE-4868-9778-7E4DBE2A4E35} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
+ {73E8E654-A2AC-4848-95F3-EB55512F6C39} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
EndGlobalSection
EndGlobal
diff --git a/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationMiddleware.cs
index bce9648b87..aca234da08 100644
--- a/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationMiddleware.cs
+++ b/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationMiddleware.cs
@@ -54,7 +54,8 @@ namespace Microsoft.AspNet.Authentication.Cookies
}
if (Options.TicketDataFormat == null)
{
- var dataProtector = dataProtectionProvider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, Options.AuthenticationScheme, "v2");
+ var provider = Options.DataProtectionProvider ?? dataProtectionProvider;
+ var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, Options.AuthenticationScheme, "v2");
Options.TicketDataFormat = new TicketDataFormat(dataProtector);
}
if (Options.CookieManager == null)
diff --git a/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationOptions.cs
index 8bb1d39598..1d0484ab64 100644
--- a/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationOptions.cs
+++ b/src/Microsoft.AspNet.Authentication.Cookies/CookieAuthenticationOptions.cs
@@ -4,6 +4,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
+using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.OptionsModel;
@@ -74,6 +75,11 @@ namespace Microsoft.AspNet.Authentication.Cookies
///
public CookieSecureOption CookieSecure { get; set; }
+ ///
+ /// If set this will be used by the CookieAuthenticationMiddleware for data protection.
+ ///
+ public IDataProtectionProvider DataProtectionProvider { get; set; }
+
///
/// Controls how much time the cookie will remain valid from the point it is created. The expiration
/// information is in the protected cookie ticket. Because of that an expired cookie will be ignored
diff --git a/src/Microsoft.AspNet.Authentication/DataHandler/TicketSerializer.cs b/src/Microsoft.AspNet.Authentication/DataHandler/TicketSerializer.cs
index b27027d11b..8fae5f9235 100644
--- a/src/Microsoft.AspNet.Authentication/DataHandler/TicketSerializer.cs
+++ b/src/Microsoft.AspNet.Authentication/DataHandler/TicketSerializer.cs
@@ -8,6 +8,7 @@ using System.Security.Claims;
namespace Microsoft.AspNet.Authentication
{
+ // This MUST be kept in sync with Microsoft.Owin.Security.Cookies.AspNetTicketSerializer
public class TicketSerializer : IDataSerializer
{
private const string DefaultStringPlaceholder = "\0";
diff --git a/src/Microsoft.Owin.Security.Cookies.Interop/AspNetTicketDataFormat.cs b/src/Microsoft.Owin.Security.Cookies.Interop/AspNetTicketDataFormat.cs
new file mode 100644
index 0000000000..48ded33091
--- /dev/null
+++ b/src/Microsoft.Owin.Security.Cookies.Interop/AspNetTicketDataFormat.cs
@@ -0,0 +1,17 @@
+// 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 Microsoft.Owin.Security.DataHandler;
+using Microsoft.Owin.Security.DataHandler.Encoder;
+using Microsoft.Owin.Security.DataProtection;
+
+namespace Microsoft.Owin.Security.Cookies.Interop
+{
+ public class AspNetTicketDataFormat : SecureDataFormat
+ {
+ public AspNetTicketDataFormat(IDataProtector protector)
+ : base(new AspNetTicketSerializer(), protector, TextEncodings.Base64Url)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Owin.Security.Cookies.Interop/AspNetTicketSerializer.cs b/src/Microsoft.Owin.Security.Cookies.Interop/AspNetTicketSerializer.cs
new file mode 100644
index 0000000000..8727f7c46b
--- /dev/null
+++ b/src/Microsoft.Owin.Security.Cookies.Interop/AspNetTicketSerializer.cs
@@ -0,0 +1,220 @@
+// 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.IO;
+using System.Linq;
+using System.Security.Claims;
+using Microsoft.Owin.Security.DataHandler.Serializer;
+
+namespace Microsoft.Owin.Security.Cookies.Interop
+{
+ // This MUST be kept in sync with Microsoft.AspNet.Authentication.DataHandler.TicketSerializer
+ public class AspNetTicketSerializer : IDataSerializer
+ {
+ private const string DefaultStringPlaceholder = "\0";
+ private const int FormatVersion = 5;
+
+ public static TicketSerializer Default { get; } = new TicketSerializer();
+
+ public virtual byte[] Serialize(AuthenticationTicket ticket)
+ {
+ using (var memory = new MemoryStream())
+ {
+ using (var writer = new BinaryWriter(memory))
+ {
+ Write(writer, ticket);
+ }
+ return memory.ToArray();
+ }
+ }
+
+ public virtual AuthenticationTicket Deserialize(byte[] data)
+ {
+ using (var memory = new MemoryStream(data))
+ {
+ using (var reader = new BinaryReader(memory))
+ {
+ return Read(reader);
+ }
+ }
+ }
+
+ public virtual void Write(BinaryWriter writer, AuthenticationTicket ticket)
+ {
+ writer.Write(FormatVersion);
+ writer.Write(ticket.Identity.AuthenticationType);
+
+ var identity = ticket.Identity;
+ if (identity == null)
+ {
+ throw new ArgumentNullException("ticket.Identity");
+ }
+
+ // There is always a single identity
+ writer.Write(1);
+ WriteIdentity(writer, identity);
+ PropertiesSerializer.Write(writer, ticket.Properties);
+ }
+
+ protected virtual void WriteIdentity(BinaryWriter writer, ClaimsIdentity identity)
+ {
+ var authenticationType = identity.AuthenticationType ?? string.Empty;
+
+ writer.Write(authenticationType);
+ WriteWithDefault(writer, identity.NameClaimType, ClaimsIdentity.DefaultNameClaimType);
+ WriteWithDefault(writer, identity.RoleClaimType, ClaimsIdentity.DefaultRoleClaimType);
+
+ // Write the number of claims contained in the identity.
+ writer.Write(identity.Claims.Count());
+
+ foreach (var claim in identity.Claims)
+ {
+ WriteClaim(writer, claim);
+ }
+
+ var bootstrap = identity.BootstrapContext as string;
+ if (!string.IsNullOrEmpty(bootstrap))
+ {
+ writer.Write(true);
+ writer.Write(bootstrap);
+ }
+ else
+ {
+ writer.Write(false);
+ }
+
+ if (identity.Actor != null)
+ {
+ writer.Write(true);
+ WriteIdentity(writer, identity.Actor);
+ }
+ else
+ {
+ writer.Write(false);
+ }
+ }
+
+ protected virtual void WriteClaim(BinaryWriter writer, Claim claim)
+ {
+ WriteWithDefault(writer, claim.Type, claim.Subject?.NameClaimType ?? ClaimsIdentity.DefaultNameClaimType);
+ writer.Write(claim.Value);
+ WriteWithDefault(writer, claim.ValueType, ClaimValueTypes.String);
+ WriteWithDefault(writer, claim.Issuer, ClaimsIdentity.DefaultIssuer);
+ WriteWithDefault(writer, claim.OriginalIssuer, claim.Issuer);
+
+ // Write the number of properties contained in the claim.
+ writer.Write(claim.Properties.Count);
+
+ foreach (var property in claim.Properties)
+ {
+ writer.Write(property.Key ?? string.Empty);
+ writer.Write(property.Value ?? string.Empty);
+ }
+ }
+
+ public virtual AuthenticationTicket Read(BinaryReader reader)
+ {
+ if (reader.ReadInt32() != FormatVersion)
+ {
+ return null;
+ }
+
+ var scheme = reader.ReadString();
+
+ // Any identities after the first will be ignored.
+ var count = reader.ReadInt32();
+ if (count < 0)
+ {
+ return null;
+ }
+
+ var identity = ReadIdentity(reader);
+ var properties = PropertiesSerializer.Read(reader);
+
+ return new AuthenticationTicket(identity, properties);
+ }
+
+ protected virtual ClaimsIdentity ReadIdentity(BinaryReader reader)
+ {
+ var authenticationType = reader.ReadString();
+ var nameClaimType = ReadWithDefault(reader, ClaimsIdentity.DefaultNameClaimType);
+ var roleClaimType = ReadWithDefault(reader, ClaimsIdentity.DefaultRoleClaimType);
+
+ // Read the number of claims contained
+ // in the serialized identity.
+ var count = reader.ReadInt32();
+
+ var identity = new ClaimsIdentity(authenticationType, nameClaimType, roleClaimType);
+
+ for (int index = 0; index != count; ++index)
+ {
+ var claim = ReadClaim(reader, identity);
+
+ identity.AddClaim(claim);
+ }
+
+ // Determine whether the identity
+ // has a bootstrap context attached.
+ if (reader.ReadBoolean())
+ {
+ identity.BootstrapContext = reader.ReadString();
+ }
+
+ // Determine whether the identity
+ // has an actor identity attached.
+ if (reader.ReadBoolean())
+ {
+ identity.Actor = ReadIdentity(reader);
+ }
+
+ return identity;
+ }
+
+ protected virtual Claim ReadClaim(BinaryReader reader, ClaimsIdentity identity)
+ {
+ var type = ReadWithDefault(reader, identity.NameClaimType);
+ var value = reader.ReadString();
+ var valueType = ReadWithDefault(reader, ClaimValueTypes.String);
+ var issuer = ReadWithDefault(reader, ClaimsIdentity.DefaultIssuer);
+ var originalIssuer = ReadWithDefault(reader, issuer);
+
+ var claim = new Claim(type, value, valueType, issuer, originalIssuer, identity);
+
+ // Read the number of properties stored in the claim.
+ var count = reader.ReadInt32();
+
+ for (var index = 0; index != count; ++index)
+ {
+ var key = reader.ReadString();
+ var propertyValue = reader.ReadString();
+
+ claim.Properties.Add(key, propertyValue);
+ }
+
+ return claim;
+ }
+
+ private static void WriteWithDefault(BinaryWriter writer, string value, string defaultValue)
+ {
+ if (string.Equals(value, defaultValue, StringComparison.Ordinal))
+ {
+ writer.Write(DefaultStringPlaceholder);
+ }
+ else
+ {
+ writer.Write(value);
+ }
+ }
+
+ private static string ReadWithDefault(BinaryReader reader, string defaultValue)
+ {
+ var value = reader.ReadString();
+ if (string.Equals(value, DefaultStringPlaceholder, StringComparison.Ordinal))
+ {
+ return defaultValue;
+ }
+ return value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Owin.Security.Cookies.Interop/CookieAuthenticationExtensions.cs b/src/Microsoft.Owin.Security.Cookies.Interop/CookieAuthenticationExtensions.cs
new file mode 100644
index 0000000000..3bbe74aad6
--- /dev/null
+++ b/src/Microsoft.Owin.Security.Cookies.Interop/CookieAuthenticationExtensions.cs
@@ -0,0 +1,26 @@
+// 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 Microsoft.AspNet.DataProtection;
+using Microsoft.Owin.Security.Cookies;
+using Microsoft.Owin.Security.Cookies.Interop;
+
+namespace Owin
+{
+ public static class CookieAuthenticationExtensions
+ {
+ public static IAppBuilder UseCookieAuthentication(
+ this IAppBuilder app,
+ CookieAuthenticationOptions options,
+ DataProtectionProvider dataProtectionProvider,
+ PipelineStage stage = PipelineStage.Authenticate)
+ {
+ var dataProtector = dataProtectionProvider.CreateProtector(
+ "Microsoft.AspNet.Authentication.Cookies.CookieAuthenticationMiddleware", // full name of the ASP.NET 5 type
+ options.AuthenticationType, "v2");
+ options.TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector));
+
+ return app.UseCookieAuthentication(options, stage);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Owin.Security.Cookies.Interop/DataProtectorShim.cs b/src/Microsoft.Owin.Security.Cookies.Interop/DataProtectorShim.cs
new file mode 100644
index 0000000000..0dab249aea
--- /dev/null
+++ b/src/Microsoft.Owin.Security.Cookies.Interop/DataProtectorShim.cs
@@ -0,0 +1,31 @@
+// 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 Microsoft.AspNet.DataProtection;
+
+namespace Microsoft.Owin.Security.Cookies.Interop
+{
+ ///
+ /// Converts an to an
+ /// .
+ ///
+ internal sealed class DataProtectorShim : Microsoft.Owin.Security.DataProtection.IDataProtector
+ {
+ private readonly IDataProtector _protector;
+
+ public DataProtectorShim(IDataProtector protector)
+ {
+ _protector = protector;
+ }
+
+ public byte[] Protect(byte[] userData)
+ {
+ return _protector.Protect(userData);
+ }
+
+ public byte[] Unprotect(byte[] protectedData)
+ {
+ return _protector.Unprotect(protectedData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Owin.Security.Cookies.Interop/DefaultCompatibilityConstants.cs b/src/Microsoft.Owin.Security.Cookies.Interop/DefaultCompatibilityConstants.cs
new file mode 100644
index 0000000000..b403033e32
--- /dev/null
+++ b/src/Microsoft.Owin.Security.Cookies.Interop/DefaultCompatibilityConstants.cs
@@ -0,0 +1,22 @@
+// 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.
+
+namespace Microsoft.AspNet.Identity
+{
+ ///
+ /// Helpful constants for working with the authentication cookie compatibility shim.
+ ///
+ public static class DefaultCompatibilityConstants
+ {
+ ///
+ /// The default authentication type for application authentication cookies.
+ ///
+ public const string ApplicationCookieAuthenticationType = "Microsoft.AspNet.Identity.Application.AuthType";
+
+ ///
+ /// The default cookie name for application authentication cookies.
+ /// Used by .
+ ///
+ public const string CookieName = ".AspNet.Microsoft.AspNet.Identity.Application";
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Owin.Security.Cookies.Interop/Microsoft.Owin.Security.Cookies.Interop.xproj b/src/Microsoft.Owin.Security.Cookies.Interop/Microsoft.Owin.Security.Cookies.Interop.xproj
new file mode 100644
index 0000000000..fd666b061d
--- /dev/null
+++ b/src/Microsoft.Owin.Security.Cookies.Interop/Microsoft.Owin.Security.Cookies.Interop.xproj
@@ -0,0 +1,19 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 21a56e78-31de-4868-9778-7e4dbe2a4e35
+ Microsoft.Owin.Security.Cookies.Shareable
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.Owin.Security.Cookies.Interop/project.json b/src/Microsoft.Owin.Security.Cookies.Interop/project.json
new file mode 100644
index 0000000000..58830a72cc
--- /dev/null
+++ b/src/Microsoft.Owin.Security.Cookies.Interop/project.json
@@ -0,0 +1,14 @@
+{
+ "version": "1.0.0-*",
+ "description": "A compatibility layer for sharing authentication tickets between Microsoft.Owin.Security.Cookies and Microsoft.AspNet.Authentication.Cookies.",
+ "dependencies": {
+ },
+ "frameworks": {
+ "net451": {
+ "dependencies": {
+ "Microsoft.AspNet.DataProtection.Extensions": "1.0.0-*",
+ "Microsoft.Owin.Security.Cookies": "3.0.1"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs b/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs
index ff1a67d532..3f40b1cebb 100644
--- a/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs
+++ b/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -10,7 +9,6 @@ using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
-using System.Xml;
using System.Xml.Linq;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.DataProtection;
diff --git a/test/Microsoft.AspNet.Authentication.Test/project.json b/test/Microsoft.AspNet.Authentication.Test/project.json
index 9b0c6a2375..af0759d42b 100644
--- a/test/Microsoft.AspNet.Authentication.Test/project.json
+++ b/test/Microsoft.AspNet.Authentication.Test/project.json
@@ -10,7 +10,6 @@
"Microsoft.AspNet.Authentication.MicrosoftAccount": "1.0.0-*",
"Microsoft.AspNet.Authentication.OpenIdConnect": "1.0.0-*",
"Microsoft.AspNet.Authentication.Twitter": "1.0.0-*",
- "Microsoft.AspNet.DataProtection": "1.0.0-*",
"Microsoft.AspNet.TestHost": "1.0.0-*",
"Microsoft.AspNet.Testing": "1.0.0-*",
"xunit.runner.aspnet": "2.0.0-aspnet-*"
diff --git a/test/Microsoft.Owin.Security.Cookies.Interop.Test/Microsoft.Owin.Security.Cookies.Interop.Test.xproj b/test/Microsoft.Owin.Security.Cookies.Interop.Test/Microsoft.Owin.Security.Cookies.Interop.Test.xproj
new file mode 100644
index 0000000000..26bc63e140
--- /dev/null
+++ b/test/Microsoft.Owin.Security.Cookies.Interop.Test/Microsoft.Owin.Security.Cookies.Interop.Test.xproj
@@ -0,0 +1,19 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 73e8e654-a2ac-4848-95f3-eb55512f6c39
+ Microsoft.Owin.Security.Cookies.Interop.Test
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.Owin.Security.Cookies.Interop.Test/TicketInteropTests.cs b/test/Microsoft.Owin.Security.Cookies.Interop.Test/TicketInteropTests.cs
new file mode 100644
index 0000000000..63ab2d36cd
--- /dev/null
+++ b/test/Microsoft.Owin.Security.Cookies.Interop.Test/TicketInteropTests.cs
@@ -0,0 +1,269 @@
+// Copyright (c) .NET Foundation. 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.IO;
+using System.Linq;
+using System.Net.Http;
+using Microsoft.AspNet.Http;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.AspNet.Authentication;
+using Microsoft.AspNet.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Owin;
+using Microsoft.Owin.Security.Cookies;
+using Microsoft.Owin.Security.Cookies.Interop;
+using Microsoft.Owin.Testing;
+using Owin;
+using Xunit;
+
+namespace Microsoft.AspNet.CookiePolicy.Test
+{
+ public class TicketInteropTests
+ {
+ [Fact]
+ public void NewSerializerCanReadInteropTicket()
+ {
+ var identity = new ClaimsIdentity("scheme");
+ identity.AddClaim(new Claim("Test", "Value"));
+
+ var expires = DateTime.Today;
+ var issued = new DateTime(1979, 11, 11);
+ var properties = new Owin.Security.AuthenticationProperties();
+ properties.IsPersistent = true;
+ properties.RedirectUri = "/redirect";
+ properties.Dictionary["key"] = "value";
+ properties.ExpiresUtc = expires;
+ properties.IssuedUtc = issued;
+
+ var interopTicket = new Owin.Security.AuthenticationTicket(identity, properties);
+ var interopSerializer = new AspNetTicketSerializer();
+
+ var bytes = interopSerializer.Serialize(interopTicket);
+
+ var newSerializer = new TicketSerializer();
+ var newTicket = newSerializer.Deserialize(bytes);
+
+ Assert.NotNull(newTicket);
+ Assert.Equal(1, newTicket.Principal.Identities.Count());
+ var newIdentity = newTicket.Principal.Identity as ClaimsIdentity;
+ Assert.NotNull(newIdentity);
+ Assert.Equal("scheme", newIdentity.AuthenticationType);
+ Assert.True(newIdentity.HasClaim(c => c.Type == "Test" && c.Value == "Value"));
+ Assert.NotNull(newTicket.Properties);
+ Assert.True(newTicket.Properties.IsPersistent);
+ Assert.Equal("/redirect", newTicket.Properties.RedirectUri);
+ Assert.Equal("value", newTicket.Properties.Items["key"]);
+ Assert.Equal(expires, newTicket.Properties.ExpiresUtc);
+ Assert.Equal(issued, newTicket.Properties.IssuedUtc);
+ }
+
+ [Fact]
+ public void InteropSerializerCanReadNewTicket()
+ {
+ var user = new ClaimsPrincipal();
+ var identity = new ClaimsIdentity("scheme");
+ identity.AddClaim(new Claim("Test", "Value"));
+ user.AddIdentity(identity);
+
+ var expires = DateTime.Today;
+ var issued = new DateTime(1979, 11, 11);
+ var properties = new Http.Authentication.AuthenticationProperties();
+ properties.IsPersistent = true;
+ properties.RedirectUri = "/redirect";
+ properties.Items["key"] = "value";
+ properties.ExpiresUtc = expires;
+ properties.IssuedUtc = issued;
+
+ var newTicket = new AuthenticationTicket(user, properties, "scheme");
+ var newSerializer = new TicketSerializer();
+
+ var bytes = newSerializer.Serialize(newTicket);
+
+ var interopSerializer = new AspNetTicketSerializer();
+ var interopTicket = interopSerializer.Deserialize(bytes);
+
+ Assert.NotNull(interopTicket);
+ var newIdentity = interopTicket.Identity;
+ Assert.NotNull(newIdentity);
+ Assert.Equal("scheme", newIdentity.AuthenticationType);
+ Assert.True(newIdentity.HasClaim(c => c.Type == "Test" && c.Value == "Value"));
+ Assert.NotNull(interopTicket.Properties);
+ Assert.True(interopTicket.Properties.IsPersistent);
+ Assert.Equal("/redirect", interopTicket.Properties.RedirectUri);
+ Assert.Equal("value", interopTicket.Properties.Dictionary["key"]);
+ Assert.Equal(expires, interopTicket.Properties.ExpiresUtc);
+ Assert.Equal(issued, interopTicket.Properties.IssuedUtc);
+ }
+
+ [Fact]
+ public async Task AspNet5WithInteropCookieContainsIdentity()
+ {
+ var identity = new ClaimsIdentity("Cookies");
+ identity.AddClaim(new Claim(ClaimTypes.Name, "Alice"));
+
+ var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("."));
+
+ var interopServer = TestServer.Create(app =>
+ {
+ app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests";
+ app.UseCookieAuthentication(new CookieAuthenticationOptions(), dataProtection);
+ app.Run(context =>
+ {
+ context.Authentication.SignIn(identity);
+ return Task.FromResult(0);
+ });
+ });
+
+ var transaction = await SendAsync(interopServer, "http://example.com");
+
+ var newServer = TestHost.TestServer.Create(app =>
+ {
+ app.UseCookieAuthentication(options => options.DataProtectionProvider = dataProtection);
+ app.Run(async context =>
+ {
+ var result = await context.Authentication.AuthenticateAsync("Cookies");
+ await context.Response.WriteAsync(result.Identity.Name);
+ });
+ }, services => services.AddAuthentication());
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/login");
+ request.Headers.Add("Cookie", transaction.SetCookie.Split(new[] { ';' }, 2).First());
+ var response = await newServer.CreateClient().SendAsync(request);
+
+ Assert.Equal("Alice", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task InteropWithNewCookieContainsIdentity()
+ {
+ var user = new ClaimsPrincipal();
+ var identity = new ClaimsIdentity("scheme");
+ identity.AddClaim(new Claim(ClaimTypes.Name, "Alice"));
+ user.AddIdentity(identity);
+
+ var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("."));
+
+ var newServer = TestHost.TestServer.Create(app =>
+ {
+ app.UseCookieAuthentication(options => options.DataProtectionProvider = dataProtection);
+ app.Run(context => context.Authentication.SignInAsync("Cookies", user));
+ }, services => services.AddAuthentication());
+
+ var cookie = await SendAndGetCookie(newServer, "http://example.com/login");
+
+ var server = TestServer.Create(app =>
+ {
+ app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests";
+ app.UseCookieAuthentication(new CookieAuthenticationOptions(), dataProtection);
+ app.Run(async context =>
+ {
+ var result = await context.Authentication.AuthenticateAsync("Cookies");
+ Describe(context.Response, result);
+ });
+ });
+
+ var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", cookie);
+
+ Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name));
+ }
+
+ private static async Task SendAndGetCookie(TestHost.TestServer server, string uri)
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, uri);
+ var response = await server.CreateClient().SendAsync(request);
+ if (response.Headers.Contains("Set-Cookie"))
+ {
+ return response.Headers.GetValues("Set-Cookie").ToList().First();
+ }
+ return null;
+ }
+
+ private static string FindClaimValue(Transaction transaction, string claimType)
+ {
+ XElement claim = transaction.ResponseElement.Elements("claim").SingleOrDefault(elt => elt.Attribute("type").Value == claimType);
+ if (claim == null)
+ {
+ return null;
+ }
+ return claim.Attribute("value").Value;
+ }
+
+ private static void Describe(IOwinResponse res, Owin.Security.AuthenticateResult result)
+ {
+ res.StatusCode = 200;
+ res.ContentType = "text/xml";
+ var xml = new XElement("xml");
+ if (result != null && result.Identity != null)
+ {
+ xml.Add(result.Identity.Claims.Select(claim => new XElement("claim", new XAttribute("type", claim.Type), new XAttribute("value", claim.Value))));
+ }
+ if (result != null && result.Properties != null)
+ {
+ xml.Add(result.Properties.Dictionary.Select(extra => new XElement("extra", new XAttribute("type", extra.Key), new XAttribute("value", extra.Value))));
+ }
+ using (var memory = new MemoryStream())
+ {
+ using (var writer = new XmlTextWriter(memory, Encoding.UTF8))
+ {
+ xml.WriteTo(writer);
+ }
+ res.Body.Write(memory.ToArray(), 0, memory.ToArray().Length);
+ }
+ }
+
+ private static async Task SendAsync(TestServer server, string uri, string cookieHeader = null, bool ajaxRequest = false)
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, uri);
+ if (!string.IsNullOrEmpty(cookieHeader))
+ {
+ request.Headers.Add("Cookie", cookieHeader);
+ }
+ if (ajaxRequest)
+ {
+ request.Headers.Add("X-Requested-With", "XMLHttpRequest");
+ }
+ var transaction = new Transaction
+ {
+ Request = request,
+ Response = await server.HttpClient.SendAsync(request),
+ };
+ if (transaction.Response.Headers.Contains("Set-Cookie"))
+ {
+ transaction.SetCookie = transaction.Response.Headers.GetValues("Set-Cookie").SingleOrDefault();
+ }
+ if (!string.IsNullOrEmpty(transaction.SetCookie))
+ {
+ transaction.CookieNameValue = transaction.SetCookie.Split(new[] { ';' }, 2).First();
+ }
+ transaction.ResponseText = await transaction.Response.Content.ReadAsStringAsync();
+
+ if (transaction.Response.Content != null &&
+ transaction.Response.Content.Headers.ContentType != null &&
+ transaction.Response.Content.Headers.ContentType.MediaType == "text/xml")
+ {
+ transaction.ResponseElement = XElement.Parse(transaction.ResponseText);
+ }
+ return transaction;
+ }
+
+ private class Transaction
+ {
+ public HttpRequestMessage Request { get; set; }
+ public HttpResponseMessage Response { get; set; }
+
+ public string SetCookie { get; set; }
+ public string CookieNameValue { get; set; }
+
+ public string ResponseText { get; set; }
+ public XElement ResponseElement { get; set; }
+ }
+
+ }
+}
+
+
diff --git a/test/Microsoft.Owin.Security.Cookies.Interop.Test/key-38ae71c9-485f-46f6-8b5d-a1da2230875f.xml b/test/Microsoft.Owin.Security.Cookies.Interop.Test/key-38ae71c9-485f-46f6-8b5d-a1da2230875f.xml
new file mode 100644
index 0000000000..67d822bca5
--- /dev/null
+++ b/test/Microsoft.Owin.Security.Cookies.Interop.Test/key-38ae71c9-485f-46f6-8b5d-a1da2230875f.xml
@@ -0,0 +1,16 @@
+
+
+ 2015-10-21T22:18:44.8335016Z
+ 2015-10-21T22:18:44.8335016Z
+ 2016-01-19T22:18:44.8335016Z
+
+
+
+
+
+
+ 7LN6JAKaUxuHQmzldfRpxCuHZtkEoG6Zrvc0LaNXgP0Ful2wYocEwlB7JRdkKAEcmY53W5wqVNSVfcgln+hNVA==
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.Owin.Security.Cookies.Interop.Test/project.json b/test/Microsoft.Owin.Security.Cookies.Interop.Test/project.json
new file mode 100644
index 0000000000..c206ad20eb
--- /dev/null
+++ b/test/Microsoft.Owin.Security.Cookies.Interop.Test/project.json
@@ -0,0 +1,18 @@
+{
+ "compilationOptions": {
+ "warningsAsErrors": true
+ },
+ "dependencies": {
+ "Microsoft.AspNet.Authentication.Cookies": "1.0.0-*",
+ "Microsoft.AspNet.TestHost": "1.0.0-*",
+ "Microsoft.Owin.Security.Cookies.Interop": "1.0.0-*",
+ "Microsoft.Owin.Testing": "3.0.1",
+ "xunit.runner.aspnet": "2.0.0-aspnet-*"
+ },
+ "commands": {
+ "test": "xunit.runner.aspnet"
+ },
+ "frameworks": {
+ "dnx451": { }
+ }
+}