Add initial Owin Security cookie interop package

This commit is contained in:
Hao Kung 2015-10-21 15:19:16 -07:00
parent b09777fe38
commit e72a563902
17 changed files with 710 additions and 4 deletions

View File

@ -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

View File

@ -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)

View File

@ -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
/// </summary>
public CookieSecureOption CookieSecure { get; set; }
/// <summary>
/// If set this will be used by the CookieAuthenticationMiddleware for data protection.
/// </summary>
public IDataProtectionProvider DataProtectionProvider { get; set; }
/// <summary>
/// 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

View File

@ -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<AuthenticationTicket>
{
private const string DefaultStringPlaceholder = "\0";

View File

@ -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<AuthenticationTicket>
{
public AspNetTicketDataFormat(IDataProtector protector)
: base(new AspNetTicketSerializer(), protector, TextEncodings.Base64Url)
{
}
}
}

View File

@ -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<AuthenticationTicket>
{
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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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
{
/// <summary>
/// Converts an <see cref="IDataProtector"/> to an
/// <see cref="Microsoft.Owin.Security.DataProtection.IDataProtector"/>.
/// </summary>
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);
}
}
}

View File

@ -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
{
/// <summary>
/// Helpful constants for working with the authentication cookie compatibility shim.
/// </summary>
public static class DefaultCompatibilityConstants
{
/// <summary>
/// The default authentication type for application authentication cookies.
/// </summary>
public const string ApplicationCookieAuthenticationType = "Microsoft.AspNet.Identity.Application.AuthType";
/// <summary>
/// The default cookie name for application authentication cookies.
/// Used by <see cref="CookieAuthenticationOptions.CookieName"/>.
/// </summary>
public const string CookieName = ".AspNet.Microsoft.AspNet.Identity.Application";
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>21a56e78-31de-4868-9778-7e4dbe2a4e35</ProjectGuid>
<RootNamespace>Microsoft.Owin.Security.Cookies.Shareable</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -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"
}
}
}
}

View File

@ -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;

View File

@ -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-*"

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>73e8e654-a2ac-4848-95f3-eb55512f6c39</ProjectGuid>
<RootNamespace>Microsoft.Owin.Security.Cookies.Interop.Test</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -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<string> 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<Transaction> 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; }
}
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<key id="38ae71c9-485f-46f6-8b5d-a1da2230875f" version="1">
<creationDate>2015-10-21T22:18:44.8335016Z</creationDate>
<activationDate>2015-10-21T22:18:44.8335016Z</activationDate>
<expirationDate>2016-01-19T22:18:44.8335016Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNet.DataProtection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
<!-- Warning: the key below is in an unencrypted form. -->
<value>7LN6JAKaUxuHQmzldfRpxCuHZtkEoG6Zrvc0LaNXgP0Ful2wYocEwlB7JRdkKAEcmY53W5wqVNSVfcgln+hNVA==</value>
</masterKey>
</descriptor>
</descriptor>
</key>

View File

@ -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": { }
}
}