From 30392a1811d45b41ffb12c0c57e4df6cac51e1fb Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 24 May 2017 09:24:08 -0700 Subject: [PATCH] Retarget to netstandard2.0 and net461 --- Security.sln | 48 ++- build/common.props | 4 +- build/dependencies.props | 4 +- build/repo.props | 1 + samples/CookieSample/CookieSample.csproj | 8 +- .../CookieSessionSample.csproj | 8 +- .../JwtBearerSample/JwtBearerSample.csproj | 8 +- .../OpenIdConnect.AzureAdSample.csproj | 6 +- .../OpenIdConnectSample.csproj | 6 +- samples/SocialSample/SocialSample.csproj | 6 +- ...t.AspNetCore.Authentication.Cookies.csproj | 2 +- ....AspNetCore.Authentication.Facebook.csproj | 2 +- ...ft.AspNetCore.Authentication.Google.csproj | 2 +- ...AspNetCore.Authentication.JwtBearer.csproj | 2 +- ...ore.Authentication.MicrosoftAccount.csproj | 2 +- ...oft.AspNetCore.Authentication.OAuth.csproj | 2 +- ...etCore.Authentication.OpenIdConnect.csproj | 2 +- ...t.AspNetCore.Authentication.Twitter.csproj | 2 +- ...Microsoft.AspNetCore.Authentication.csproj | 4 +- .../Microsoft.AspNetCore.Authorization.csproj | 2 +- .../Microsoft.AspNetCore.CookiePolicy.csproj | 2 +- .../AspNetTicketDataFormat.cs | 17 + .../AspNetTicketSerializer.cs | 220 +++++++++++ .../ChunkingCookieManager.cs | 281 +++++++++++++ .../Constants.cs | 13 + .../DataProtectorShim.cs | 31 ++ .../Microsoft.Owin.Security.Interop.csproj | 18 + .../Properties/AssemblyInfo.cs | 8 + .../baseline.netframework.json | 373 ++++++++++++++++++ ...soft.AspNetCore.Authentication.Test.csproj | 6 +- ...osoft.AspNetCore.Authorization.Test.csproj | 3 +- ....ChunkingCookieManager.Sources.Test.csproj | 3 +- ...rosoft.AspNetCore.CookiePolicy.Test.csproj | 5 +- .../CookieInteropTests.cs | 332 ++++++++++++++++ ...icrosoft.Owin.Security.Interop.Test.csproj | 26 ++ .../TicketInteropTests.cs | 91 +++++ 36 files changed, 1517 insertions(+), 33 deletions(-) create mode 100644 src/Microsoft.Owin.Security.Interop/AspNetTicketDataFormat.cs create mode 100644 src/Microsoft.Owin.Security.Interop/AspNetTicketSerializer.cs create mode 100644 src/Microsoft.Owin.Security.Interop/ChunkingCookieManager.cs create mode 100644 src/Microsoft.Owin.Security.Interop/Constants.cs create mode 100644 src/Microsoft.Owin.Security.Interop/DataProtectorShim.cs create mode 100644 src/Microsoft.Owin.Security.Interop/Microsoft.Owin.Security.Interop.csproj create mode 100644 src/Microsoft.Owin.Security.Interop/Properties/AssemblyInfo.cs create mode 100644 src/Microsoft.Owin.Security.Interop/baseline.netframework.json create mode 100644 test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs create mode 100644 test/Microsoft.Owin.Security.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj create mode 100644 test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs diff --git a/Security.sln b/Security.sln index 157849c911..b9e6ac5672 100644 --- a/Security.sln +++ b/Security.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +VisualStudioVersion = 15.0.26510.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D2B6A51-2F9F-44F5-8131-EA5CAC053652}" EndProject @@ -46,10 +46,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authen EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JwtBearerSample", "samples\JwtBearerSample\JwtBearerSample.csproj", "{D399B84F-591B-4E98-92BA-B0F63E7B6957}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Owin.Security.Interop", "src\Microsoft.Owin.Security.Interop\Microsoft.Owin.Security.Interop.csproj", "{A7922DD8-09F1-43E4-938B-CC523EA08898}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Owin.Security.Interop.Test", "test\Microsoft.Owin.Security.Interop.Test\Microsoft.Owin.Security.Interop.Test.csproj", "{A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIdConnect.AzureAdSample", "samples\OpenIdConnect.AzureAdSample\OpenIdConnect.AzureAdSample.csproj", "{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test", "test\Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test\Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj", "{51563775-C659-4907-9BAF-9995BAB87D01}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{86BD08B1-F978-4F58-9982-2A017807F01C}" + ProjectSection(SolutionItems) = preProject + build\common.props = build\common.props + build\dependencies.props = build\dependencies.props + build\Key.snk = build\Key.snk + build\repo.props = build\repo.props + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -360,6 +372,38 @@ Global {D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|x64.Build.0 = Release|Any CPU {D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|x86.ActiveCfg = Release|Any CPU {D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|x86.Build.0 = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|x64.Build.0 = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Debug|x86.Build.0 = Debug|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|Any CPU.Build.0 = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|x64.ActiveCfg = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|x64.Build.0 = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|x86.ActiveCfg = Release|Any CPU + {A7922DD8-09F1-43E4-938B-CC523EA08898}.Release|x86.Build.0 = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|x64.Build.0 = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Debug|x86.Build.0 = Debug|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|Any CPU.Build.0 = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|x64.ActiveCfg = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|x64.Build.0 = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|x86.ActiveCfg = Release|Any CPU + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|x86.Build.0 = Release|Any CPU {3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -416,6 +460,8 @@ Global {1790E052-646F-4529-B90E-6FEA95520D69} = {7BF11F3A-60B6-4796-B504-579C67FFBA34} {2755BFE5-7421-4A31-A644-F817DF5CAA98} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652} {D399B84F-591B-4E98-92BA-B0F63E7B6957} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF} + {A7922DD8-09F1-43E4-938B-CC523EA08898} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652} + {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24} = {7BF11F3A-60B6-4796-B504-579C67FFBA34} {3A7AD414-EBDE-4F92-B307-4E8F19B6117E} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF} {51563775-C659-4907-9BAF-9995BAB87D01} = {7BF11F3A-60B6-4796-B504-579C67FFBA34} EndGlobalSection diff --git a/build/common.props b/build/common.props index 3f55ba5b33..dc4ad9a786 100644 --- a/build/common.props +++ b/build/common.props @@ -16,8 +16,8 @@ - - + + diff --git a/build/dependencies.props b/build/dependencies.props index 74d501f7cd..f7e538a504 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -1,11 +1,13 @@ 2.0.0-* + 4.4.0-* 3.13.8 2.1.3 2.1.0-* 10.0.1 - $(BundledNETStandardPackageVersion) + 2.0.0-* + 3.0.1 15.3.0-* 2.3.0-beta2-* diff --git a/build/repo.props b/build/repo.props index 396aed1f53..d4bab3eebd 100644 --- a/build/repo.props +++ b/build/repo.props @@ -1,4 +1,5 @@ + diff --git a/samples/CookieSample/CookieSample.csproj b/samples/CookieSample/CookieSample.csproj index 27fa4ca17b..d251b844e1 100644 --- a/samples/CookieSample/CookieSample.csproj +++ b/samples/CookieSample/CookieSample.csproj @@ -1,9 +1,9 @@ - + - netcoreapp2.0 + net461;netcoreapp2.0 @@ -19,4 +19,8 @@ + + + + diff --git a/samples/CookieSessionSample/CookieSessionSample.csproj b/samples/CookieSessionSample/CookieSessionSample.csproj index 2a838df7e4..a2d0490f1a 100644 --- a/samples/CookieSessionSample/CookieSessionSample.csproj +++ b/samples/CookieSessionSample/CookieSessionSample.csproj @@ -1,9 +1,9 @@ - + - netcoreapp2.0 + net461;netcoreapp2.0 @@ -19,4 +19,8 @@ + + + + diff --git a/samples/JwtBearerSample/JwtBearerSample.csproj b/samples/JwtBearerSample/JwtBearerSample.csproj index 86b04d587c..c2f73fd961 100644 --- a/samples/JwtBearerSample/JwtBearerSample.csproj +++ b/samples/JwtBearerSample/JwtBearerSample.csproj @@ -1,9 +1,9 @@ - + - netcoreapp2.0 + net461;netcoreapp2.0 aspnet5-JwtBearerSample-20151210102827 @@ -20,4 +20,8 @@ + + + + diff --git a/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj b/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj index 78ba03c2af..7857249087 100644 --- a/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj +++ b/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj @@ -3,7 +3,7 @@ - netcoreapp2.0 + net461;netcoreapp2.0 aspnet5-OpenIdConnectSample-20151210110318 @@ -23,4 +23,8 @@ + + + + diff --git a/samples/OpenIdConnectSample/OpenIdConnectSample.csproj b/samples/OpenIdConnectSample/OpenIdConnectSample.csproj index 875762d126..03384f567e 100644 --- a/samples/OpenIdConnectSample/OpenIdConnectSample.csproj +++ b/samples/OpenIdConnectSample/OpenIdConnectSample.csproj @@ -3,7 +3,7 @@ - netcoreapp2.0 + net461;netcoreapp2.0 aspnet5-OpenIdConnectSample-20151210110318 @@ -33,4 +33,8 @@ + + + + diff --git a/samples/SocialSample/SocialSample.csproj b/samples/SocialSample/SocialSample.csproj index 723d74de32..999dc91a6f 100644 --- a/samples/SocialSample/SocialSample.csproj +++ b/samples/SocialSample/SocialSample.csproj @@ -3,7 +3,7 @@ - netcoreapp2.0 + net461;netcoreapp2.0 aspnet5-SocialSample-20151210111056 @@ -35,4 +35,8 @@ + + + + diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj b/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj index a5728fbabc..41c4ff7905 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj @@ -4,7 +4,7 @@ ASP.NET Core middleware that enables an application to use cookie based authentication. - netcoreapp2.0 + netstandard2.0 $(DefineConstants);SECURITY $(NoWarn);CS1591 true diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj b/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj index 19ec83413c..e39b31e904 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj @@ -4,7 +4,7 @@ ASP.NET Core middleware that enables an application to support Facebook's OAuth 2.0 authentication workflow. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj b/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj index e403fe4a95..c4f1e7ad8f 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj @@ -4,7 +4,7 @@ ASP.NET Core contains middleware to support Google's OpenId and OAuth 2.0 authentication workflows. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj b/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj index c2b99e47db..5b25e00e86 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj @@ -2,7 +2,7 @@ ASP.NET Core middleware that enables an application to receive an OpenID Connect bearer token. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj index 4d91e0da1e..1a954c850a 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj @@ -4,7 +4,7 @@ ASP.NET Core middleware that enables an application to support the Microsoft Account authentication workflow. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj b/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj index 8e33372c1f..508c815fe8 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj @@ -4,7 +4,7 @@ ASP.NET Core middleware that enables an application to support any standard OAuth 2.0 authentication workflow. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj index b3829b59dc..0ce2fe34e0 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj @@ -2,7 +2,7 @@ ASP.NET Core middleware that enables an application to support the OpenID Connect authentication workflow. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj b/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj index 5dd58d680c..b78de6597f 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj @@ -4,7 +4,7 @@ ASP.NET Core middleware that enables an application to support Twitter's OAuth 1.0 authentication workflow. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj b/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj index 54b560702a..cbdb05b58a 100644 --- a/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj +++ b/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj @@ -4,7 +4,7 @@ ASP.NET Core common types used by the various authentication middleware components. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authentication;security @@ -18,7 +18,7 @@ - + diff --git a/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj b/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj index b39a7bf3dd..93673ef007 100644 --- a/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj +++ b/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj @@ -7,7 +7,7 @@ Commonly used types: Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute Microsoft.AspNetCore.Authorization.AuthorizeAttribute - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore;authorization diff --git a/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj b/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj index 31ce5b761a..5dc2cd9281 100644 --- a/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj +++ b/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj @@ -4,7 +4,7 @@ ASP.NET Core cookie policy classes to control the behavior of cookies. - netcoreapp2.0 + netstandard2.0 $(NoWarn);CS1591 true aspnetcore diff --git a/src/Microsoft.Owin.Security.Interop/AspNetTicketDataFormat.cs b/src/Microsoft.Owin.Security.Interop/AspNetTicketDataFormat.cs new file mode 100644 index 0000000000..f1a07c5bf7 --- /dev/null +++ b/src/Microsoft.Owin.Security.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.Interop +{ + public class AspNetTicketDataFormat : SecureDataFormat + { + public AspNetTicketDataFormat(IDataProtector protector) + : base(AspNetTicketSerializer.Default, protector, TextEncodings.Base64Url) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Owin.Security.Interop/AspNetTicketSerializer.cs b/src/Microsoft.Owin.Security.Interop/AspNetTicketSerializer.cs new file mode 100644 index 0000000000..6a1019fbc8 --- /dev/null +++ b/src/Microsoft.Owin.Security.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.Interop +{ + // This MUST be kept in sync with Microsoft.AspNetCore.Authentication.DataHandler.TicketSerializer + public class AspNetTicketSerializer : IDataSerializer + { + private const string DefaultStringPlaceholder = "\0"; + private const int FormatVersion = 5; + + public static AspNetTicketSerializer Default { get; } = new AspNetTicketSerializer(); + + 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.Interop/ChunkingCookieManager.cs b/src/Microsoft.Owin.Security.Interop/ChunkingCookieManager.cs new file mode 100644 index 0000000000..b323258d9b --- /dev/null +++ b/src/Microsoft.Owin.Security.Interop/ChunkingCookieManager.cs @@ -0,0 +1,281 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using Microsoft.Owin.Infrastructure; + +namespace Microsoft.Owin.Security.Interop +{ + // This MUST be kept in sync with Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager + /// + /// This handles cookies that are limited by per cookie length. It breaks down long cookies for responses, and reassembles them + /// from requests. + /// + public class ChunkingCookieManager : ICookieManager + { + private const string ChunkKeySuffix = "C"; + private const string ChunkCountPrefix = "chunks-"; + + public ChunkingCookieManager() + { + // Lowest common denominator. Safari has the lowest known limit (4093), and we leave little extra just in case. + // See http://browsercookielimits.x64.me/. + // Leave at least 20 in case CookiePolicy tries to add 'secure' and/or 'httponly'. + ChunkSize = 4070; + ThrowForPartialCookies = true; + } + + /// + /// The maximum size of cookie to send back to the client. If a cookie exceeds this size it will be broken down into multiple + /// cookies. Set this value to null to disable this behavior. The default is 4090 characters, which is supported by all + /// common browsers. + /// + /// Note that browsers may also have limits on the total size of all cookies per domain, and on the number of cookies per domain. + /// + public int? ChunkSize { get; set; } + + /// + /// Throw if not all chunks of a cookie are available on a request for re-assembly. + /// + public bool ThrowForPartialCookies { get; set; } + + // Parse the "chunks-XX" to determine how many chunks there should be. + private static int ParseChunksCount(string value) + { + if (value != null && value.StartsWith(ChunkCountPrefix, StringComparison.Ordinal)) + { + var chunksCountString = value.Substring(ChunkCountPrefix.Length); + int chunksCount; + if (int.TryParse(chunksCountString, NumberStyles.None, CultureInfo.InvariantCulture, out chunksCount)) + { + return chunksCount; + } + } + return 0; + } + + /// + /// Get the reassembled cookie. Non chunked cookies are returned normally. + /// Cookies with missing chunks just have their "chunks-XX" header returned. + /// + /// + /// + /// The reassembled cookie, if any, or null. + public string GetRequestCookie(IOwinContext context, string key) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + var requestCookies = context.Request.Cookies; + var value = requestCookies[key]; + var chunksCount = ParseChunksCount(value); + if (chunksCount > 0) + { + var chunks = new string[chunksCount]; + for (var chunkId = 1; chunkId <= chunksCount; chunkId++) + { + var chunk = requestCookies[key + ChunkKeySuffix + chunkId.ToString(CultureInfo.InvariantCulture)]; + if (string.IsNullOrEmpty(chunk)) + { + if (ThrowForPartialCookies) + { + var totalSize = 0; + for (int i = 0; i < chunkId - 1; i++) + { + totalSize += chunks[i].Length; + } + throw new FormatException( + string.Format(CultureInfo.CurrentCulture, + "The chunked cookie is incomplete. Only {0} of the expected {1} chunks were found, totaling {2} characters. A client size limit may have been exceeded.", + chunkId - 1, chunksCount, totalSize)); + } + // Missing chunk, abort by returning the original cookie value. It may have been a false positive? + return value; + } + + chunks[chunkId - 1] = chunk; + } + + return string.Join(string.Empty, chunks); + } + return value; + } + + /// + /// Appends a new response cookie to the Set-Cookie header. If the cookie is larger than the given size limit + /// then it will be broken down into multiple cookies as follows: + /// Set-Cookie: CookieName=chunks-3; path=/ + /// Set-Cookie: CookieNameC1=Segment1; path=/ + /// Set-Cookie: CookieNameC2=Segment2; path=/ + /// Set-Cookie: CookieNameC3=Segment3; path=/ + /// + /// + /// + /// + /// + public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + var domainHasValue = !string.IsNullOrEmpty(options.Domain); + var pathHasValue = !string.IsNullOrEmpty(options.Path); + var expiresHasValue = options.Expires.HasValue; + + var templateLength = key.Length + "=".Length + + (domainHasValue ? "; domain=".Length + options.Domain.Length : 0) + + (pathHasValue ? "; path=".Length + options.Path.Length : 0) + + (expiresHasValue ? "; expires=ddd, dd-MMM-yyyy HH:mm:ss GMT".Length : 0) + + (options.Secure ? "; secure".Length : 0) + + (options.HttpOnly ? "; HttpOnly".Length : 0); + + // Normal cookie + var responseCookies = context.Response.Cookies; + if (!ChunkSize.HasValue || ChunkSize.Value > templateLength + value.Length) + { + responseCookies.Append(key, value, options); + } + else if (ChunkSize.Value < templateLength + 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 + throw new InvalidOperationException("The cookie key and options are larger than ChunksSize, leaving no room for data."); + } + else + { + // Break the cookie down into multiple cookies. + // Key = CookieName, value = "Segment1Segment2Segment2" + // Set-Cookie: CookieName=chunks-3; path=/ + // Set-Cookie: CookieNameC1="Segment1"; path=/ + // Set-Cookie: CookieNameC2="Segment2"; path=/ + // Set-Cookie: CookieNameC3="Segment3"; path=/ + var dataSizePerCookie = ChunkSize.Value - templateLength - 3; // Budget 3 chars for the chunkid. + var cookieChunkCount = (int)Math.Ceiling(value.Length * 1.0 / dataSizePerCookie); + + responseCookies.Append(key, ChunkCountPrefix + cookieChunkCount.ToString(CultureInfo.InvariantCulture), options); + + var offset = 0; + for (var chunkId = 1; chunkId <= cookieChunkCount; chunkId++) + { + var remainingLength = value.Length - offset; + var length = Math.Min(dataSizePerCookie, remainingLength); + var segment = value.Substring(offset, length); + offset += length; + + responseCookies.Append(key + ChunkKeySuffix + chunkId.ToString(CultureInfo.InvariantCulture), segment, options); + } + } + } + + /// + /// Deletes the cookie with the given key by setting an expired state. If a matching chunked cookie exists on + /// the request, delete each chunk. + /// + /// + /// + /// + public void DeleteCookie(IOwinContext context, string key, CookieOptions options) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + var keys = new List(); + keys.Add(key + "="); + + var requestCookie = context.Request.Cookies[key]; + var chunks = ParseChunksCount(requestCookie); + if (chunks > 0) + { + for (int i = 1; i <= chunks + 1; i++) + { + var subkey = key + ChunkKeySuffix + i.ToString(CultureInfo.InvariantCulture); + keys.Add(subkey + "="); + } + } + + var domainHasValue = !string.IsNullOrEmpty(options.Domain); + var pathHasValue = !string.IsNullOrEmpty(options.Path); + + Func rejectPredicate; + Func predicate = value => keys.Any(k => value.StartsWith(k, StringComparison.OrdinalIgnoreCase)); + if (domainHasValue) + { + rejectPredicate = value => predicate(value) && value.IndexOf("domain=" + options.Domain, StringComparison.OrdinalIgnoreCase) != -1; + } + else if (pathHasValue) + { + rejectPredicate = value => predicate(value) && value.IndexOf("path=" + options.Path, StringComparison.OrdinalIgnoreCase) != -1; + } + else + { + rejectPredicate = value => predicate(value); + } + + var responseHeaders = context.Response.Headers; + string[] existingValues; + if (responseHeaders.TryGetValue(Constants.Headers.SetCookie, out existingValues) && existingValues != null) + { + responseHeaders.SetValues(Constants.Headers.SetCookie, existingValues.Where(value => !rejectPredicate(value)).ToArray()); + } + + AppendResponseCookie( + context, + key, + string.Empty, + new CookieOptions() + { + Path = options.Path, + Domain = options.Domain, + Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + }); + + for (int i = 1; i <= chunks; i++) + { + AppendResponseCookie( + context, + key + "C" + i.ToString(CultureInfo.InvariantCulture), + string.Empty, + new CookieOptions() + { + Path = options.Path, + Domain = options.Domain, + Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + }); + } + } + } +} diff --git a/src/Microsoft.Owin.Security.Interop/Constants.cs b/src/Microsoft.Owin.Security.Interop/Constants.cs new file mode 100644 index 0000000000..1e75761b70 --- /dev/null +++ b/src/Microsoft.Owin.Security.Interop/Constants.cs @@ -0,0 +1,13 @@ +// 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. + +namespace Microsoft.Owin.Security.Interop +{ + internal static class Constants + { + internal static class Headers + { + internal const string SetCookie = "Set-Cookie"; + } + } +} diff --git a/src/Microsoft.Owin.Security.Interop/DataProtectorShim.cs b/src/Microsoft.Owin.Security.Interop/DataProtectorShim.cs new file mode 100644 index 0000000000..7313588948 --- /dev/null +++ b/src/Microsoft.Owin.Security.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.AspNetCore.DataProtection; + +namespace Microsoft.Owin.Security.Interop +{ + /// + /// Converts an to an + /// . + /// + public 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.Interop/Microsoft.Owin.Security.Interop.csproj b/src/Microsoft.Owin.Security.Interop/Microsoft.Owin.Security.Interop.csproj new file mode 100644 index 0000000000..10a8be30f5 --- /dev/null +++ b/src/Microsoft.Owin.Security.Interop/Microsoft.Owin.Security.Interop.csproj @@ -0,0 +1,18 @@ + + + + + + A compatibility layer for sharing authentication tickets between Microsoft.Owin.Security and Microsoft.AspNetCore.Authentication. + net461 + $(NoWarn);CS1591 + true + aspnetcore;katana;owin;security + + + + + + + + diff --git a/src/Microsoft.Owin.Security.Interop/Properties/AssemblyInfo.cs b/src/Microsoft.Owin.Security.Interop/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..490fa7cb2a --- /dev/null +++ b/src/Microsoft.Owin.Security.Interop/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// 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.Runtime.InteropServices; + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a7922dd8-09f1-43e4-938b-cc523ea08898")] + diff --git a/src/Microsoft.Owin.Security.Interop/baseline.netframework.json b/src/Microsoft.Owin.Security.Interop/baseline.netframework.json new file mode 100644 index 0000000000..1fc242ec55 --- /dev/null +++ b/src/Microsoft.Owin.Security.Interop/baseline.netframework.json @@ -0,0 +1,373 @@ +{ + "AssemblyIdentity": "Microsoft.Owin.Security.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Owin.Security.Interop.AspNetTicketDataFormat", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.Owin.Security.DataHandler.SecureDataFormat", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "protector", + "Type": "Microsoft.Owin.Security.DataProtection.IDataProtector" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Owin.Security.Interop.AspNetTicketSerializer", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Owin.Security.DataHandler.Serializer.IDataSerializer" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Default", + "Parameters": [], + "ReturnType": "Microsoft.Owin.Security.Interop.AspNetTicketSerializer", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Serialize", + "Parameters": [ + { + "Name": "ticket", + "Type": "Microsoft.Owin.Security.AuthenticationTicket" + } + ], + "ReturnType": "System.Byte[]", + "Virtual": true, + "ImplementedInterface": "Microsoft.Owin.Security.DataHandler.Serializer.IDataSerializer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Deserialize", + "Parameters": [ + { + "Name": "data", + "Type": "System.Byte[]" + } + ], + "ReturnType": "Microsoft.Owin.Security.AuthenticationTicket", + "Virtual": true, + "ImplementedInterface": "Microsoft.Owin.Security.DataHandler.Serializer.IDataSerializer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Write", + "Parameters": [ + { + "Name": "writer", + "Type": "System.IO.BinaryWriter" + }, + { + "Name": "ticket", + "Type": "Microsoft.Owin.Security.AuthenticationTicket" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WriteIdentity", + "Parameters": [ + { + "Name": "writer", + "Type": "System.IO.BinaryWriter" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WriteClaim", + "Parameters": [ + { + "Name": "writer", + "Type": "System.IO.BinaryWriter" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Read", + "Parameters": [ + { + "Name": "reader", + "Type": "System.IO.BinaryReader" + } + ], + "ReturnType": "Microsoft.Owin.Security.AuthenticationTicket", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReadIdentity", + "Parameters": [ + { + "Name": "reader", + "Type": "System.IO.BinaryReader" + } + ], + "ReturnType": "System.Security.Claims.ClaimsIdentity", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReadClaim", + "Parameters": [ + { + "Name": "reader", + "Type": "System.IO.BinaryReader" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + } + ], + "ReturnType": "System.Security.Claims.Claim", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Owin.Security.Interop.ChunkingCookieManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Owin.Infrastructure.ICookieManager" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_ChunkSize", + "Parameters": [], + "ReturnType": "System.Nullable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ChunkSize", + "Parameters": [ + { + "Name": "value", + "Type": "System.Nullable" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ThrowForPartialCookies", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ThrowForPartialCookies", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRequestCookie", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.Owin.IOwinContext" + }, + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Owin.Infrastructure.ICookieManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendResponseCookie", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.Owin.IOwinContext" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.Owin.CookieOptions" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Owin.Infrastructure.ICookieManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteCookie", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.Owin.IOwinContext" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.Owin.CookieOptions" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Owin.Infrastructure.ICookieManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Owin.Security.Interop.DataProtectorShim", + "Visibility": "Public", + "Kind": "Class", + "Sealed": true, + "ImplementedInterfaces": [ + "Microsoft.Owin.Security.DataProtection.IDataProtector" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Protect", + "Parameters": [ + { + "Name": "userData", + "Type": "System.Byte[]" + } + ], + "ReturnType": "System.Byte[]", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Owin.Security.DataProtection.IDataProtector", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Unprotect", + "Parameters": [ + { + "Name": "protectedData", + "Type": "System.Byte[]" + } + ], + "ReturnType": "System.Byte[]", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Owin.Security.DataProtection.IDataProtector", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "protector", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtector" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ], + "SourceFilters": [] +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj b/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj index 98d727fde5..c44069f437 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj +++ b/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj @@ -3,9 +3,8 @@ - netcoreapp2.0 - true - true + netcoreapp2.0;net461 + netcoreapp2.0 @@ -24,6 +23,7 @@ + diff --git a/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj b/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj index a9ba71a797..48b134740a 100644 --- a/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj +++ b/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj @@ -3,7 +3,8 @@ - netcoreapp2.0 + netcoreapp2.0;net461 + netcoreapp2.0 diff --git a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj index 7908e98fcb..b578f5dcfa 100644 --- a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj +++ b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj @@ -3,7 +3,8 @@ - netcoreapp2.0 + netcoreapp2.0;net461 + netcoreapp2.0 diff --git a/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj b/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj index b7b2c6afe0..f513de4b35 100644 --- a/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj +++ b/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj @@ -2,9 +2,8 @@ - netcoreapp2.0 - true - true + netcoreapp2.0;net461 + netcoreapp2.0 diff --git a/test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs b/test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs new file mode 100644 index 0000000000..ae5e6f0183 --- /dev/null +++ b/test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs @@ -0,0 +1,332 @@ +// 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.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Microsoft.Owin.Security.Cookies; +using Microsoft.Owin.Testing; +using Owin; +using Xunit; + +namespace Microsoft.Owin.Security.Interop +{ + public class CookiesInteropTests + { + [Fact] + public async Task AspNetCoreWithInteropCookieContainsIdentity() + { + var identity = new ClaimsIdentity("Cookies"); + identity.AddClaim(new Claim(ClaimTypes.Name, "Alice")); + + var dataProtection = DataProtectionProvider.Create(new DirectoryInfo("..\\..\\artifacts")); + var dataProtector = dataProtection.CreateProtector( + "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", // full name of the ASP.NET Core type + Cookies.CookieAuthenticationDefaults.AuthenticationType, "v2"); + + var interopServer = TestServer.Create(app => + { + app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests"; + + app.UseCookieAuthentication(new Cookies.CookieAuthenticationOptions + { + TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector)), + CookieName = AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.CookiePrefix + + AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme, + }); + + app.Run(context => + { + context.Authentication.SignIn(identity); + return Task.FromResult(0); + }); + }); + + var transaction = await SendAsync(interopServer, "http://example.com"); + + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseAuthentication(); + app.Run(async context => + { + var result = await context.AuthenticateAsync("Cookies"); + await context.Response.WriteAsync(result.Ticket.Principal.Identity.Name); + }); + }) + .ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); + var newServer = new AspNetCore.TestHost.TestServer(builder); + + var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/login"); + foreach (var cookie in SetCookieHeaderValue.ParseList(transaction.SetCookie)) + { + request.Headers.Add("Cookie", cookie.Name + "=" + cookie.Value); + } + var response = await newServer.CreateClient().SendAsync(request); + + Assert.Equal("Alice", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task AspNetCoreWithLargeInteropCookieContainsIdentity() + { + var identity = new ClaimsIdentity("Cookies"); + identity.AddClaim(new Claim(ClaimTypes.Name, new string('a', 1024 * 5))); + + var dataProtection = DataProtectionProvider.Create(new DirectoryInfo("..\\..\\artifacts")); + var dataProtector = dataProtection.CreateProtector( + "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", // full name of the ASP.NET Core type + Cookies.CookieAuthenticationDefaults.AuthenticationType, "v2"); + + var interopServer = TestServer.Create(app => + { + app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests"; + + app.UseCookieAuthentication(new Cookies.CookieAuthenticationOptions + { + TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector)), + CookieName = AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.CookiePrefix + + AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme, + CookieManager = new ChunkingCookieManager(), + }); + + app.Run(context => + { + context.Authentication.SignIn(identity); + return Task.FromResult(0); + }); + }); + + var transaction = await SendAsync(interopServer, "http://example.com"); + + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseAuthentication(); + app.Run(async context => + { + var result = await context.AuthenticateAsync("Cookies"); + await context.Response.WriteAsync(result.Ticket.Principal.Identity.Name); + }); + }) + .ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); + var newServer = new AspNetCore.TestHost.TestServer(builder); + + var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/login"); + foreach (var cookie in SetCookieHeaderValue.ParseList(transaction.SetCookie)) + { + request.Headers.Add("Cookie", cookie.Name + "=" + cookie.Value); + } + var response = await newServer.CreateClient().SendAsync(request); + + Assert.Equal(1024 * 5, (await response.Content.ReadAsStringAsync()).Length); + } + + [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 = DataProtectionProvider.Create(new DirectoryInfo("..\\..\\artifacts")); + var dataProtector = dataProtection.CreateProtector( + "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", // full name of the ASP.NET Core type + Cookies.CookieAuthenticationDefaults.AuthenticationType, "v2"); + + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseAuthentication(); + app.Run(context => context.SignInAsync("Cookies", user)); + }) + .ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); + var newServer = new AspNetCore.TestHost.TestServer(builder); + + var cookies = await SendAndGetCookies(newServer, "http://example.com/login"); + + var server = TestServer.Create(app => + { + app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests"; + + app.UseCookieAuthentication(new Cookies.CookieAuthenticationOptions + { + TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector)), + CookieName = AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.CookiePrefix + + AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme, + }); + + 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", cookies); + + Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name)); + } + + [Fact] + public async Task InteropWithLargeNewCookieContainsIdentity() + { + var user = new ClaimsPrincipal(); + var identity = new ClaimsIdentity("scheme"); + identity.AddClaim(new Claim(ClaimTypes.Name, new string('a', 1024 * 5))); + user.AddIdentity(identity); + + var dataProtection = DataProtectionProvider.Create(new DirectoryInfo("..\\..\\artifacts")); + var dataProtector = dataProtection.CreateProtector( + "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", // full name of the ASP.NET Core type + Cookies.CookieAuthenticationDefaults.AuthenticationType, "v2"); + + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseAuthentication(); + app.Run(context => context.SignInAsync("Cookies", user)); + }) + .ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); + var newServer = new AspNetCore.TestHost.TestServer(builder); + + var cookies = await SendAndGetCookies(newServer, "http://example.com/login"); + + var server = TestServer.Create(app => + { + app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests"; + + app.UseCookieAuthentication(new Cookies.CookieAuthenticationOptions + { + TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector)), + CookieName = AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.CookiePrefix + + AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme, + CookieManager = new ChunkingCookieManager(), + }); + + 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", cookies); + + Assert.Equal(1024 * 5, FindClaimValue(transaction2, ClaimTypes.Name).Length); + } + + private static async Task> SendAndGetCookies(AspNetCore.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")) + { + IList cookieHeaders = new List(); + foreach (var cookie in SetCookieHeaderValue.ParseList(response.Headers.GetValues("Set-Cookie").ToList())) + { + cookieHeaders.Add(cookie.Name + "=" + cookie.Value); + } + return cookieHeaders; + } + 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, 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, IList cookieHeaders = null, bool ajaxRequest = false) + { + var request = new HttpRequestMessage(HttpMethod.Get, uri); + if (cookieHeaders != null) + { + request.Headers.Add("Cookie", cookieHeaders); + } + 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").ToList(); + } + if (transaction.SetCookie != null && transaction.SetCookie.Any()) + { + transaction.CookieNameValue = transaction.SetCookie.First().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 IList 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.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj b/test/Microsoft.Owin.Security.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj new file mode 100644 index 0000000000..d6e9d16e5f --- /dev/null +++ b/test/Microsoft.Owin.Security.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj @@ -0,0 +1,26 @@ + + + + + + net461 + + + + + + + + + + + + + + + + + + + + diff --git a/test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs b/test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs new file mode 100644 index 0000000000..b14ea0d74e --- /dev/null +++ b/test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs @@ -0,0 +1,91 @@ +// 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.Linq; +using System.Security.Claims; +using Microsoft.AspNetCore.Authentication; +using Xunit; + +namespace Microsoft.Owin.Security.Interop.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 AspNetCore.Authentication.AuthenticationProperties(); + properties.IsPersistent = true; + properties.RedirectUri = "/redirect"; + properties.Items["key"] = "value"; + properties.ExpiresUtc = expires; + properties.IssuedUtc = issued; + + var newTicket = new AspNetCore.Authentication.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); + } + } +} + +