diff --git a/IdentityCore.sln b/IdentityCore.sln
new file mode 100644
index 0000000000..e88571a3a6
--- /dev/null
+++ b/IdentityCore.sln
@@ -0,0 +1,224 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26507.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{52D59F18-62D2-4D17-8CF2-BE192445AF8E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity", "src\Microsoft.AspNetCore.Identity\Microsoft.AspNetCore.Identity.csproj", "{1729302E-A58E-4652-B639-5B6B68DA2748}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Test", "test\Microsoft.AspNetCore.Identity.Test\Microsoft.AspNetCore.Identity.Test.csproj", "{2CF3927B-19E4-4866-9BAA-2C131580E7C3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.InMemory.Test", "test\Microsoft.AspNetCore.Identity.InMemory.Test\Microsoft.AspNetCore.Identity.InMemory.Test.csproj", "{65161409-C4C4-4D63-A73B-231FCFF4D503}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentitySample.Mvc", "samples\IdentitySample.Mvc\IdentitySample.Mvc.csproj", "{E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test", "test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test.csproj", "{37236EA3-915D-46D5-997C-DF513C500E4B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test", "test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test.csproj", "{EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore", "src\Microsoft.AspNetCore.Identity.EntityFrameworkCore\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj", "{4490894C-3572-4E63-86F1-EE5105CE8A06}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.Identity.AspNetCoreCompat", "src\Microsoft.AspNet.Identity.AspNetCoreCompat\Microsoft.AspNet.Identity.AspNetCoreCompat.csproj", "{6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Specification.Tests", "src\Microsoft.AspNetCore.Identity.Specification.Tests\Microsoft.AspNetCore.Identity.Specification.Tests.csproj", "{5608E828-DD54-4E2A-B73C-FC22268BE797}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Identity.Core", "src\Microsoft.Extensions.Identity.Core\Microsoft.Extensions.Identity.Core.csproj", "{D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Identity.Stores", "src\Microsoft.Extensions.Identity.Stores\Microsoft.Extensions.Identity.Stores.csproj", "{FADA11FC-DC06-4832-A569-7B2374A6CD42}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x64.Build.0 = Debug|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x64.ActiveCfg = Release|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x64.Build.0 = Release|Any CPU
+ {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x86.ActiveCfg = Release|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x64.Build.0 = Debug|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x64.ActiveCfg = Release|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x64.Build.0 = Release|Any CPU
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x86.ActiveCfg = Release|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x64.Build.0 = Debug|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x64.ActiveCfg = Release|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x64.Build.0 = Release|Any CPU
+ {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x86.ActiveCfg = Release|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x64.Build.0 = Debug|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x64.ActiveCfg = Release|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x64.Build.0 = Release|Any CPU
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x86.ActiveCfg = Release|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x64.Build.0 = Debug|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x64.ActiveCfg = Release|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x64.Build.0 = Release|Any CPU
+ {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x86.ActiveCfg = Release|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x64.Build.0 = Debug|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x64.ActiveCfg = Release|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x64.Build.0 = Release|Any CPU
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x86.ActiveCfg = Release|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x64.Build.0 = Debug|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x64.ActiveCfg = Release|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x64.Build.0 = Release|Any CPU
+ {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x86.ActiveCfg = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x64.Build.0 = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x86.Build.0 = Debug|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x64.ActiveCfg = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x64.Build.0 = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.ActiveCfg = Release|Any CPU
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.Build.0 = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x64.Build.0 = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.Build.0 = Debug|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x64.ActiveCfg = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x64.Build.0 = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.ActiveCfg = Release|Any CPU
+ {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.Build.0 = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x64.Build.0 = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x86.Build.0 = Debug|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x64.ActiveCfg = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x64.Build.0 = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x86.ActiveCfg = Release|Any CPU
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x86.Build.0 = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x64.Build.0 = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x86.Build.0 = Debug|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x64.ActiveCfg = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x64.Build.0 = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x86.ActiveCfg = Release|Any CPU
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {1729302E-A58E-4652-B639-5B6B68DA2748} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
+ {2CF3927B-19E4-4866-9BAA-2C131580E7C3} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
+ {65161409-C4C4-4D63-A73B-231FCFF4D503} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
+ {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}
+ {37236EA3-915D-46D5-997C-DF513C500E4B} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
+ {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
+ {4490894C-3572-4E63-86F1-EE5105CE8A06} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
+ {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
+ {5608E828-DD54-4E2A-B73C-FC22268BE797} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
+ {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
+ {FADA11FC-DC06-4832-A569-7B2374A6CD42} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
+ EndGlobalSection
+EndGlobal
diff --git a/build/repo.props b/build/repo.props
new file mode 100644
index 0000000000..205ecdd92f
--- /dev/null
+++ b/build/repo.props
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs
index 2fafc38694..8044d33d09 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs
@@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Base class for the Entity Framework database context used for identity.
///
/// The type of the user objects.
- public class IdentityDbContext : IdentityDbContext where TUser : IdentityUser
+ public class IdentityDbContext : IdentityDbContext where TUser : IdentityUser
{
///
/// Initializes a new instance of .
@@ -41,6 +41,27 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
protected IdentityDbContext() { }
}
+ ///
+ /// Base class for the Entity Framework database context used for identity.
+ ///
+ /// The type of user objects.
+ /// The type of the primary key for users and roles.
+ public class IdentityDbContext : IdentityDbContext, IdentityUserLogin, IdentityUserToken>
+ where TUser : IdentityUser
+ where TKey : IEquatable
+ {
+ ///
+ /// Initializes a new instance of the db context.
+ ///
+ /// The options to be used by a .
+ public IdentityDbContext(DbContextOptions options) : base(options) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected IdentityDbContext() { }
+ }
+
///
/// Base class for the Entity Framework database context used for identity.
///
@@ -68,21 +89,15 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Base class for the Entity Framework database context used for identity.
///
/// The type of user objects.
- /// The type of role objects.
/// The type of the primary key for users and roles.
/// The type of the user claim object.
- /// The type of the user role object.
/// The type of the user login object.
- /// The type of the role claim object.
/// The type of the user token object.
- public abstract class IdentityDbContext : DbContext
- where TUser : IdentityUser
- where TRole : IdentityRole
+ public abstract class IdentityDbContext : DbContext
+ where TUser : IdentityUser
where TKey : IEquatable
where TUserClaim : IdentityUserClaim
- where TUserRole : IdentityUserRole
where TUserLogin : IdentityUserLogin
- where TRoleClaim : IdentityRoleClaim
where TUserToken : IdentityUserToken
{
///
@@ -111,26 +126,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
public DbSet UserLogins { get; set; }
- ///
- /// Gets or sets the of User roles.
- ///
- public DbSet UserRoles { get; set; }
-
///
/// Gets or sets the of User tokens.
///
public DbSet UserTokens { get; set; }
- ///
- /// Gets or sets the of roles.
- ///
- public DbSet Roles { get; set; }
-
- ///
- /// Gets or sets the of role claims.
- ///
- public DbSet RoleClaims { get; set; }
-
///
/// Configures the schema needed for the identity framework.
///
@@ -155,10 +155,90 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
// Replace with b.HasMany().
b.HasMany().WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
b.HasMany().WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
- b.HasMany().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
b.HasMany().WithOne().HasForeignKey(ut => ut.UserId).IsRequired();
});
+ builder.Entity(b =>
+ {
+ b.HasKey(uc => uc.Id);
+ b.ToTable("AspNetUserClaims");
+ });
+
+ builder.Entity(b =>
+ {
+ b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
+ b.ToTable("AspNetUserLogins");
+ });
+
+ builder.Entity(b =>
+ {
+ b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name });
+ b.ToTable("AspNetUserTokens");
+ });
+ }
+ }
+
+ ///
+ /// Base class for the Entity Framework database context used for identity.
+ ///
+ /// The type of user objects.
+ /// The type of role objects.
+ /// The type of the primary key for users and roles.
+ /// The type of the user claim object.
+ /// The type of the user role object.
+ /// The type of the user login object.
+ /// The type of the role claim object.
+ /// The type of the user token object.
+ public abstract class IdentityDbContext : IdentityDbContext
+ where TUser : IdentityUser
+ where TRole : IdentityRole
+ where TKey : IEquatable
+ where TUserClaim : IdentityUserClaim
+ where TUserRole : IdentityUserRole
+ where TUserLogin : IdentityUserLogin
+ where TRoleClaim : IdentityRoleClaim
+ where TUserToken : IdentityUserToken
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options to be used by a .
+ public IdentityDbContext(DbContextOptions options) : base(options) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected IdentityDbContext() { }
+
+ ///
+ /// Gets or sets the of User roles.
+ ///
+ public DbSet UserRoles { get; set; }
+
+ ///
+ /// Gets or sets the of roles.
+ ///
+ public DbSet Roles { get; set; }
+
+ ///
+ /// Gets or sets the of role claims.
+ ///
+ public DbSet RoleClaims { get; set; }
+
+ ///
+ /// Configures the schema needed for the identity framework.
+ ///
+ ///
+ /// The builder being used to construct the model for this context.
+ ///
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
+ builder.Entity(b =>
+ {
+ b.HasMany().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
+ });
+
builder.Entity(b =>
{
b.HasKey(r => r.Id);
@@ -173,35 +253,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
b.HasMany().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});
- builder.Entity(b =>
- {
- b.HasKey(uc => uc.Id);
- b.ToTable("AspNetUserClaims");
- });
-
- builder.Entity(b =>
+ builder.Entity(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("AspNetRoleClaims");
});
- builder.Entity(b =>
+ builder.Entity(b =>
{
b.HasKey(r => new { r.UserId, r.RoleId });
b.ToTable("AspNetUserRoles");
});
-
- builder.Entity(b =>
- {
- b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
- b.ToTable("AspNetUserLogins");
- });
-
- builder.Entity(b =>
- {
- b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name });
- b.ToTable("AspNetUserTokens");
- });
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs
index 6a25296f53..0c7d1d48fc 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs
@@ -30,32 +30,68 @@ namespace Microsoft.Extensions.DependencyInjection
private static void AddStores(IServiceCollection services, Type userType, Type roleType, Type contextType)
{
- var identityUserType = FindGenericBaseType(userType, typeof(IdentityUser<,,,,>));
+ var identityUserType = FindGenericBaseType(userType, typeof(IdentityUser<>));
if (identityUserType == null)
{
throw new InvalidOperationException(Resources.NotIdentityUser);
}
- var identityRoleType = FindGenericBaseType(roleType, typeof(IdentityRole<,,>));
- if (identityRoleType == null)
+
+ var keyType = identityUserType.GenericTypeArguments[0];
+
+ if (roleType != null)
{
- throw new InvalidOperationException(Resources.NotIdentityRole);
+ var identityRoleType = FindGenericBaseType(roleType, typeof(IdentityRole<>));
+ if (identityRoleType == null)
+ {
+ throw new InvalidOperationException(Resources.NotIdentityRole);
+ }
+
+ Type userStoreType = null;
+ Type roleStoreType = null;
+ var identityContext = FindGenericBaseType(contextType, typeof(IdentityDbContext<,,,,,,,>));
+ if (identityContext == null)
+ {
+ // If its a custom DbContext, we can only add the default POCOs
+ userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
+ roleStoreType = typeof(RoleStore<,,>).MakeGenericType(roleType, contextType, keyType);
+ }
+ else
+ {
+ userStoreType = typeof(UserStore<,,,,,,,,>).MakeGenericType(userType, roleType, contextType,
+ identityContext.GenericTypeArguments[2],
+ identityContext.GenericTypeArguments[3],
+ identityContext.GenericTypeArguments[4],
+ identityContext.GenericTypeArguments[5],
+ identityContext.GenericTypeArguments[7],
+ identityContext.GenericTypeArguments[6]);
+ roleStoreType = typeof(RoleStore<,,,,>).MakeGenericType(roleType, contextType,
+ identityContext.GenericTypeArguments[2],
+ identityContext.GenericTypeArguments[4],
+ identityContext.GenericTypeArguments[6]);
+ }
+ services.TryAddScoped(typeof(IUserStore<>).MakeGenericType(userType), userStoreType);
+ services.TryAddScoped(typeof(IRoleStore<>).MakeGenericType(roleType), roleStoreType);
+ }
+ else
+ { // No Roles
+ Type userStoreType = null;
+ var identityContext = FindGenericBaseType(contextType, typeof(IdentityDbContext<,,,,>));
+ if (identityContext == null)
+ {
+ // If its a custom DbContext, we can only add the default POCOs
+ userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
+ }
+ else
+ {
+ userStoreType = typeof(UserOnlyStore<,,,,,>).MakeGenericType(userType, roleType, contextType,
+ identityContext.GenericTypeArguments[1],
+ identityContext.GenericTypeArguments[2],
+ identityContext.GenericTypeArguments[3],
+ identityContext.GenericTypeArguments[4]);
+ }
+ services.TryAddScoped(typeof(IUserStore<>).MakeGenericType(userType), userStoreType);
}
- services.TryAddScoped(
- typeof(IUserStore<>).MakeGenericType(userType),
- typeof(UserStore<,,,,,,,,>).MakeGenericType(userType, roleType, contextType,
- identityUserType.GenericTypeArguments[0],
- identityUserType.GenericTypeArguments[1],
- identityUserType.GenericTypeArguments[2],
- identityUserType.GenericTypeArguments[3],
- identityUserType.GenericTypeArguments[4],
- identityRoleType.GenericTypeArguments[2]));
- services.TryAddScoped(
- typeof(IRoleStore<>).MakeGenericType(roleType),
- typeof(RoleStore<,,,,>).MakeGenericType(roleType, contextType,
- identityRoleType.GenericTypeArguments[0],
- identityRoleType.GenericTypeArguments[1],
- identityRoleType.GenericTypeArguments[2]));
}
private static TypeInfo FindGenericBaseType(Type currentType, Type genericBaseType)
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/Properties/Resources.Designer.cs
index 9862d64fb6..6e9c08a032 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/Properties/Resources.Designer.cs
@@ -15,64 +15,56 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
internal static string NotIdentityRole
{
- get { return GetString("NotIdentityRole"); }
+ get => GetString("NotIdentityRole");
}
///
/// AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey, TUserRole, TRoleClaim>.
///
internal static string FormatNotIdentityRole()
- {
- return GetString("NotIdentityRole");
- }
+ => GetString("NotIdentityRole");
///
/// AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>.
///
internal static string NotIdentityUser
{
- get { return GetString("NotIdentityUser"); }
+ get => GetString("NotIdentityUser");
}
///
/// AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>.
///
internal static string FormatNotIdentityUser()
- {
- return GetString("NotIdentityUser");
- }
+ => GetString("NotIdentityUser");
///
/// Role {0} does not exist.
///
internal static string RoleNotFound
{
- get { return GetString("RoleNotFound"); }
+ get => GetString("RoleNotFound");
}
///
/// Role {0} does not exist.
///
internal static string FormatRoleNotFound(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0);
///
/// Value cannot be null or empty.
///
internal static string ValueCannotBeNullOrEmpty
{
- get { return GetString("ValueCannotBeNullOrEmpty"); }
+ get => GetString("ValueCannotBeNullOrEmpty");
}
///
/// Value cannot be null or empty.
///
internal static string FormatValueCannotBeNullOrEmpty()
- {
- return GetString("ValueCannotBeNullOrEmpty");
- }
+ => GetString("ValueCannotBeNullOrEmpty");
private static string GetString(string name, params string[] formatterNames)
{
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs
index 31212b061f..140d51765e 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs
@@ -9,7 +9,6 @@ using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
@@ -77,7 +76,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
public class RoleStore :
IQueryableRoleStore,
IRoleClaimStore
- where TRole : IdentityRole
+ where TRole : IdentityRole
where TKey : IEquatable
where TContext : DbContext
where TUserRole : IdentityUserRole, new()
@@ -361,10 +360,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
/// Dispose the stores
///
- public void Dispose()
- {
- _disposed = true;
- }
+ public void Dispose() => _disposed = true;
///
/// Get the claims associated with the specified as an asynchronous operation.
@@ -434,10 +430,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
/// A navigation property for the roles the store contains.
///
- public virtual IQueryable Roles
- {
- get { return Context.Set(); }
- }
+ public virtual IQueryable Roles => Context.Set();
private DbSet RoleClaims { get { return Context.Set(); } }
@@ -448,8 +441,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// The associated claim.
/// The role claim entity.
protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim)
- {
- return new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
- }
+ => new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
}
}
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserOnlyStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserOnlyStore.cs
new file mode 100644
index 0000000000..e0a36a98cf
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserOnlyStore.cs
@@ -0,0 +1,567 @@
+// 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.Linq;
+using System.Security.Claims;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+
+namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
+{
+ ///
+ /// Creates a new instance of a persistence store for the specified user type.
+ ///
+ /// The type representing a user.
+ public class UserOnlyStore : UserOnlyStore where TUser : IdentityUser, new()
+ {
+ ///
+ /// Constructs a new instance of .
+ ///
+ /// The .
+ /// The .
+ public UserOnlyStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
+ }
+
+ ///
+ /// Represents a new instance of a persistence store for the specified user and role types.
+ ///
+ /// The type representing a user.
+ /// The type of the data context class used to access the store.
+ public class UserOnlyStore : UserOnlyStore
+ where TUser : IdentityUser
+ where TContext : DbContext
+ {
+ ///
+ /// Constructs a new instance of .
+ ///
+ /// The .
+ /// The .
+ public UserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
+ }
+
+ ///
+ /// Represents a new instance of a persistence store for the specified user and role types.
+ ///
+ /// The type representing a user.
+ /// The type of the data context class used to access the store.
+ /// The type of the primary key for a role.
+ public class UserOnlyStore : UserOnlyStore, IdentityUserLogin, IdentityUserToken>
+ where TUser : IdentityUser
+ where TContext : DbContext
+ where TKey : IEquatable
+ {
+ ///
+ /// Constructs a new instance of .
+ ///
+ /// The .
+ /// The .
+ public UserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
+ }
+
+ ///
+ /// Represents a new instance of a persistence store for the specified user and role types.
+ ///
+ /// The type representing a user.
+ /// The type of the data context class used to access the store.
+ /// The type of the primary key for a role.
+ /// The type representing a claim.
+ /// The type representing a user external login.
+ /// The type representing a user token.
+ public class UserOnlyStore :
+ UserStoreBase,
+ IUserLoginStore,
+ IUserClaimStore,
+ IUserPasswordStore,
+ IUserSecurityStampStore,
+ IUserEmailStore,
+ IUserLockoutStore,
+ IUserPhoneNumberStore,
+ IQueryableUserStore,
+ IUserTwoFactorStore,
+ IUserAuthenticationTokenStore,
+ IUserAuthenticatorKeyStore,
+ IUserTwoFactorRecoveryCodeStore
+ where TUser : IdentityUser
+ where TContext : DbContext
+ where TKey : IEquatable
+ where TUserClaim : IdentityUserClaim, new()
+ where TUserLogin : IdentityUserLogin, new()
+ where TUserToken : IdentityUserToken, new()
+ {
+ ///
+ /// Creates a new instance of the store.
+ ///
+ /// The context used to access the store.
+ /// The used to describe store errors.
+ public UserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber())
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+ Context = context;
+ }
+
+ ///
+ /// Gets the database context for this store.
+ ///
+ public TContext Context { get; private set; }
+
+ ///
+ /// DbSet of users.
+ ///
+ protected DbSet UsersSet { get { return Context.Set(); } }
+
+ ///
+ /// DbSet of user claims.
+ ///
+ protected DbSet UserClaims { get { return Context.Set(); } }
+
+ ///
+ /// DbSet of user logins.
+ ///
+ protected DbSet UserLogins { get { return Context.Set(); } }
+
+ ///
+ /// DbSet of user tokens.
+ ///
+ protected DbSet UserTokens { get { return Context.Set(); } }
+
+ ///
+ /// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
+ ///
+ ///
+ /// True if changes should be automatically persisted, otherwise false.
+ ///
+ public bool AutoSaveChanges { get; set; } = true;
+
+ /// Saves the current store.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ protected Task SaveChanges(CancellationToken cancellationToken)
+ {
+ return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.CompletedTask;
+ }
+
+ ///
+ /// Creates the specified in the user store.
+ ///
+ /// The user to create.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation, containing the of the creation operation.
+ public async override Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+ Context.Add(user);
+ await SaveChanges(cancellationToken);
+ return IdentityResult.Success;
+ }
+
+ ///
+ /// Updates the specified in the user store.
+ ///
+ /// The user to update.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation, containing the of the update operation.
+ public async override Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ Context.Attach(user);
+ user.ConcurrencyStamp = Guid.NewGuid().ToString();
+ Context.Update(user);
+ try
+ {
+ await SaveChanges(cancellationToken);
+ }
+ catch (DbUpdateConcurrencyException)
+ {
+ return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
+ }
+ return IdentityResult.Success;
+ }
+
+ ///
+ /// Deletes the specified from the user store.
+ ///
+ /// The user to delete.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation, containing the of the update operation.
+ public async override Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ Context.Remove(user);
+ try
+ {
+ await SaveChanges(cancellationToken);
+ }
+ catch (DbUpdateConcurrencyException)
+ {
+ return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
+ }
+ return IdentityResult.Success;
+ }
+
+ ///
+ /// Finds and returns a user, if any, who has the specified .
+ ///
+ /// The user ID to search for.
+ /// The used to propagate notifications that the operation should be canceled.
+ ///
+ /// The that represents the asynchronous operation, containing the user matching the specified if it exists.
+ ///
+ public override Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ var id = ConvertIdFromString(userId);
+ return UsersSet.FindAsync(new object[] { id }, cancellationToken);
+ }
+
+ ///
+ /// Finds and returns a user, if any, who has the specified normalized user name.
+ ///
+ /// The normalized user name to search for.
+ /// The used to propagate notifications that the operation should be canceled.
+ ///
+ /// The that represents the asynchronous operation, containing the user matching the specified if it exists.
+ ///
+ public override Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken);
+ }
+
+ ///
+ /// A navigation property for the users the store contains.
+ ///
+ public override IQueryable Users
+ {
+ get { return UsersSet; }
+ }
+
+ ///
+ /// Return a user with the matching userId if it exists.
+ ///
+ /// The user's id.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The user if it exists.
+ protected override Task FindUserAsync(TKey userId, CancellationToken cancellationToken)
+ {
+ return Users.SingleOrDefaultAsync(u => u.Id.Equals(userId), cancellationToken);
+ }
+
+ ///
+ /// Return a user login with the matching userId, provider, providerKey if it exists.
+ ///
+ /// The user's id.
+ /// The login provider name.
+ /// The key provided by the to identify a user.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The user login if it exists.
+ protected override Task FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken)
+ {
+ return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.UserId.Equals(userId) && userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken);
+ }
+
+ ///
+ /// Return a user login with provider, providerKey if it exists.
+ ///
+ /// The login provider name.
+ /// The key provided by the to identify a user.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The user login if it exists.
+ protected override Task FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
+ {
+ return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken);
+ }
+
+ ///
+ /// Get the claims associated with the specified as an asynchronous operation.
+ ///
+ /// The user whose claims should be retrieved.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// A that contains the claims granted to a user.
+ public async override Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken);
+ }
+
+ ///
+ /// Adds the given to the specified .
+ ///
+ /// The user to add the claim to.
+ /// The claim to add to the user.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ public override Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+ if (claims == null)
+ {
+ throw new ArgumentNullException(nameof(claims));
+ }
+ foreach (var claim in claims)
+ {
+ UserClaims.Add(CreateUserClaim(user, claim));
+ }
+ return Task.FromResult(false);
+ }
+
+ ///
+ /// Replaces the on the specified , with the .
+ ///
+ /// The user to replace the claim on.
+ /// The claim replace.
+ /// The new claim replacing the .
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ public async override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+ if (claim == null)
+ {
+ throw new ArgumentNullException(nameof(claim));
+ }
+ if (newClaim == null)
+ {
+ throw new ArgumentNullException(nameof(newClaim));
+ }
+
+ var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
+ foreach (var matchedClaim in matchedClaims)
+ {
+ matchedClaim.ClaimValue = newClaim.Value;
+ matchedClaim.ClaimType = newClaim.Type;
+ }
+ }
+
+ ///
+ /// Removes the given from the specified .
+ ///
+ /// The user to remove the claims from.
+ /// The claim to remove.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ public async override Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+ if (claims == null)
+ {
+ throw new ArgumentNullException(nameof(claims));
+ }
+ foreach (var claim in claims)
+ {
+ var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
+ foreach (var c in matchedClaims)
+ {
+ UserClaims.Remove(c);
+ }
+ }
+ }
+
+ ///
+ /// Adds the given to the specified .
+ ///
+ /// The user to add the login to.
+ /// The login to add to the user.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ public override Task AddLoginAsync(TUser user, UserLoginInfo login,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+ if (login == null)
+ {
+ throw new ArgumentNullException(nameof(login));
+ }
+ UserLogins.Add(CreateUserLogin(user, login));
+ return Task.FromResult(false);
+ }
+
+ ///
+ /// Removes the given from the specified .
+ ///
+ /// The user to remove the login from.
+ /// The login to remove from the user.
+ /// The key provided by the to identify a user.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ public override async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+ var entry = await FindUserLoginAsync(user.Id, loginProvider, providerKey, cancellationToken);
+ if (entry != null)
+ {
+ UserLogins.Remove(entry);
+ }
+ }
+
+ ///
+ /// Retrieves the associated logins for the specified .
+ ///
+ /// The user whose associated logins to retrieve.
+ /// The used to propagate notifications that the operation should be canceled.
+ ///
+ /// The for the asynchronous operation, containing a list of for the specified , if any.
+ ///
+ public async override Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+ var userId = user.Id;
+ return await UserLogins.Where(l => l.UserId.Equals(userId))
+ .Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync(cancellationToken);
+ }
+
+ ///
+ /// Retrieves the user associated with the specified login provider and login provider key.
+ ///
+ /// The login provider who provided the .
+ /// The key provided by the to identify a user.
+ /// The used to propagate notifications that the operation should be canceled.
+ ///
+ /// The for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
+ ///
+ public async override Task FindByLoginAsync(string loginProvider, string providerKey,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken);
+ if (userLogin != null)
+ {
+ return await FindUserAsync(userLogin.UserId, cancellationToken);
+ }
+ return null;
+ }
+
+ ///
+ /// Gets the user, if any, associated with the specified, normalized email address.
+ ///
+ /// The normalized email address to return the user for.
+ /// The used to propagate notifications that the operation should be canceled.
+ ///
+ /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
+ ///
+ public override Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ return Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken);
+ }
+
+ ///
+ /// Retrieves all users with the specified claim.
+ ///
+ /// The claim whose users should be retrieved.
+ /// The used to propagate notifications that the operation should be canceled.
+ ///
+ /// The contains a list of users, if any, that contain the specified claim.
+ ///
+ public async override Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+ if (claim == null)
+ {
+ throw new ArgumentNullException(nameof(claim));
+ }
+
+ var query = from userclaims in UserClaims
+ join user in Users on userclaims.UserId equals user.Id
+ where userclaims.ClaimValue == claim.Value
+ && userclaims.ClaimType == claim.Type
+ select user;
+
+ return await query.ToListAsync(cancellationToken);
+ }
+
+ ///
+ /// Find a user token if it exists.
+ ///
+ /// The token owner.
+ /// The login provider for the token.
+ /// The name of the token.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The user token if it exists.
+ protected override Task FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
+ => UserTokens.FindAsync(new object[] { user.Id, loginProvider, name }, cancellationToken);
+
+ ///
+ /// Add a new user token.
+ ///
+ /// The token to be added.
+ ///
+ protected override Task AddUserTokenAsync(TUserToken token)
+ {
+ UserTokens.Add(token);
+ return Task.CompletedTask;
+ }
+
+
+ ///
+ /// Remove a new user token.
+ ///
+ /// The token to be removed.
+ ///
+ protected override Task RemoveUserTokenAsync(TUserToken token)
+ {
+ UserTokens.Remove(token);
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs
index 20f8de152a..cedb9f4bae 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs
@@ -9,7 +9,6 @@ using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
@@ -95,22 +94,9 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// The type representing a user token.
/// The type representing a role claim.
public class UserStore :
- UserStoreBase,
- IUserLoginStore,
- IUserRoleStore,
- IUserClaimStore,
- IUserPasswordStore,
- IUserSecurityStampStore,
- IUserEmailStore,
- IUserLockoutStore,
- IUserPhoneNumberStore,
- IQueryableUserStore,
- IUserTwoFactorStore,
- IUserAuthenticationTokenStore,
- IUserAuthenticatorKeyStore,
- IUserTwoFactorRecoveryCodeStore
- where TUser : IdentityUser
- where TRole : IdentityRole
+ UserStoreBase
+ where TUser : IdentityUser
+ where TRole : IdentityRole
where TContext : DbContext
where TKey : IEquatable
where TUserClaim : IdentityUserClaim, new()
diff --git a/src/Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore/IdentityServiceDbContext.cs b/src/Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore/IdentityServiceDbContext.cs
index f14807c6bb..2cdd17f9a9 100644
--- a/src/Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore/IdentityServiceDbContext.cs
+++ b/src/Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore/IdentityServiceDbContext.cs
@@ -54,8 +54,8 @@ namespace Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore
TRedirectUri,
TApplicationKey> :
IdentityDbContext
- where TUser : IdentityUser
- where TRole : IdentityRole
+ where TUser : IdentityUser
+ where TRole : IdentityRole
where TUserKey : IEquatable
where TUserClaim : IdentityUserClaim
where TUserRole : IdentityUserRole
diff --git a/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs
index 220dd11818..80852ab575 100644
--- a/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs
+++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs
@@ -7,9 +7,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Security.Claims;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit;
@@ -32,32 +30,18 @@ namespace Microsoft.AspNetCore.Identity.Test
/// The type of the user.
/// The type of the role.
/// The primary key type.
- public abstract class IdentitySpecificationTestBase
+ public abstract class IdentitySpecificationTestBase : UserManagerSpecificationTestBase
where TUser : class
where TRole : class
where TKey : IEquatable
{
- private const string NullValue = "(null)";
-
- private readonly IdentityErrorDescriber _errorDescriber = new IdentityErrorDescriber();
-
- ///
- /// If true, tests that require a database will be skipped.
- ///
- ///
- protected virtual bool ShouldSkipDbTests()
- {
- return false;
- }
-
///
/// Configure the service collection used for tests.
///
///
///
- protected virtual void SetupIdentityServices(IServiceCollection services, object context = null)
+ protected override void SetupIdentityServices(IServiceCollection services, object context)
{
- services.AddSingleton(new ConfigurationBuilder().Build());
services.AddSingleton();
services.AddIdentity(options =>
{
@@ -75,25 +59,18 @@ namespace Microsoft.AspNetCore.Identity.Test
}
///
- /// Creates the user manager used for tests.
+ /// Setup the IdentityBuilder
///
- /// The context that will be passed into the store, typically a db context.
- /// The service collection to use, optional.
- /// Delegate used to configure the services, optional.
- /// The user manager to use for tests.
- protected virtual UserManager CreateManager(object context = null, IServiceCollection services = null, Action configureServices = null)
+ ///
+ ///
+ ///
+ protected override IdentityBuilder SetupBuilder(IServiceCollection services, object context)
{
- if (services == null)
- {
- services = new ServiceCollection();
- }
- if (context == null)
- {
- context = CreateTestContext();
- }
- SetupIdentityServices(services, context);
- configureServices?.Invoke(services);
- return services.BuildServiceProvider().GetService>();
+ var builder = base.SetupBuilder(services, context);
+ builder.AddRoles();
+ AddRoleStore(services, context);
+ services.AddSingleton>>(new TestLogger>());
+ return builder;
}
///
@@ -102,7 +79,7 @@ namespace Microsoft.AspNetCore.Identity.Test
/// The context that will be passed into the store, typically a db context.
/// The service collection to use, optional.
///
- protected RoleManager CreateRoleManager(object context = null, IServiceCollection services = null)
+ protected virtual RoleManager CreateRoleManager(object context = null, IServiceCollection services = null)
{
if (services == null)
{
@@ -116,19 +93,6 @@ namespace Microsoft.AspNetCore.Identity.Test
return services.BuildServiceProvider().GetService>();
}
- ///
- /// Creates the context object for a test, typically a DbContext.
- ///
- /// The context object for a test, typically a DbContext.
- protected abstract object CreateTestContext();
-
- ///
- /// Adds an IUserStore to services for the test.
- ///
- /// The service collection to add to.
- /// The context for the store to use, optional.
- protected abstract void AddUserStore(IServiceCollection services, object context = null);
-
///
/// Adds an IRoleStore to services for the test.
///
@@ -136,26 +100,6 @@ namespace Microsoft.AspNetCore.Identity.Test
/// The context for the store to use, optional.
protected abstract void AddRoleStore(IServiceCollection services, object context = null);
- ///
- /// Set the user's password hash.
- ///
- /// The user to set.
- /// The password hash to set.
- protected abstract void SetUserPasswordHash(TUser user, string hashedPassword);
-
- ///
- /// Create a new test user instance.
- ///
- /// Optional name prefix, name will be randomized.
- /// Optional email.
- /// Optional phone number.
- /// Optional lockout enabled.
- /// Optional lockout end.
- /// If true, the prefix should be used as the username without a random pad.
- /// The new test user instance.
- protected abstract TUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
- bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = null, bool useNamePrefixAsUserName = false);
-
///
/// Creates a new test role instance.
///
@@ -164,20 +108,6 @@ namespace Microsoft.AspNetCore.Identity.Test
///
protected abstract TRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false);
- ///
- /// Query used to do name equality checks.
- ///
- /// The user name to match.
- /// The query to use.
- protected abstract Expression> UserNameEqualsPredicate(string userName);
-
- ///
- /// Query used to do user name prefix matching.
- ///
- /// The user name to match.
- /// The query to use.
- protected abstract Expression> UserNameStartsWithPredicate(string userName);
-
///
/// Query used to do name equality checks.
///
@@ -192,1316 +122,6 @@ namespace Microsoft.AspNetCore.Identity.Test
/// The query to use.
protected abstract Expression> RoleNameStartsWithPredicate(string roleName);
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanDeleteUser()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var userId = await manager.GetUserIdAsync(user);
- IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
- Assert.Null(await manager.FindByIdAsync(userId));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanUpdateUserName()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var name = Guid.NewGuid().ToString();
- var user = CreateTestUser(name);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var newName = Guid.NewGuid().ToString();
- Assert.Null(await manager.FindByNameAsync(newName));
- IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newName));
- IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
- Assert.NotNull(await manager.FindByNameAsync(newName));
- Assert.Null(await manager.FindByNameAsync(name));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CheckSetUserNameValidatesUser()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var username = "UpdateAsync" + Guid.NewGuid().ToString();
- var newUsername = "New" + Guid.NewGuid().ToString();
- var user = CreateTestUser(username, useNamePrefixAsUserName: true);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.Null(await manager.FindByNameAsync(newUsername));
- IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newUsername));
- Assert.NotNull(await manager.FindByNameAsync(newUsername));
- Assert.Null(await manager.FindByNameAsync(username));
-
- var newUser = CreateTestUser(username, useNamePrefixAsUserName: true);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(newUser));
- var error = _errorDescriber.InvalidUserName("");
- IdentityResultAssert.IsFailure(await manager.SetUserNameAsync(newUser, ""), error);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(newUser)} validation failed: {error.Code}.");
-
- error = _errorDescriber.DuplicateUserName(newUsername);
- IdentityResultAssert.IsFailure(await manager.SetUserNameAsync(newUser, newUsername), error);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(newUser)} validation failed: {error.Code}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task SetUserNameUpdatesSecurityStamp()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var username = "UpdateAsync" + Guid.NewGuid().ToString();
- var newUsername = "New" + Guid.NewGuid().ToString();
- var user = CreateTestUser(username, useNamePrefixAsUserName: true);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.Null(await manager.FindByNameAsync(newUsername));
- IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newUsername));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CreateUpdatesSecurityStamp()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var username = "Create" + Guid.NewGuid().ToString();
- var user = CreateTestUser(username, useNamePrefixAsUserName: true);
- var stamp = await manager.GetSecurityStampAsync(user);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.NotNull(await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CheckSetEmailValidatesUser()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.Options.User.RequireUniqueEmail = true;
- manager.UserValidators.Add(new UserValidator());
- var random = new Random();
- var email = "foo" + random.Next() + "@example.com";
- var newEmail = "bar" + random.Next() + "@example.com";
- var user = CreateTestUser(email: email);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, newEmail));
-
- var newUser = CreateTestUser(email: email);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(newUser));
- IdentityResultAssert.IsFailure(await manager.SetEmailAsync(newUser, newEmail), _errorDescriber.DuplicateEmail(newEmail));
- IdentityResultAssert.IsFailure(await manager.SetEmailAsync(newUser, ""), _errorDescriber.InvalidEmail(""));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanUpdatePasswordUsingHasher()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser("UpdatePassword");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
- Assert.True(await manager.CheckPasswordAsync(user, "password"));
- var userId = await manager.GetUserIdAsync(user);
-
- SetUserPasswordHash(user, manager.PasswordHasher.HashPassword(user, "New"));
- IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
- Assert.False(await manager.CheckPasswordAsync(user, "password"));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Invalid password for user {await manager.GetUserIdAsync(user)}.");
- Assert.True(await manager.CheckPasswordAsync(user, "New"));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanFindById()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.NotNull(await manager.FindByIdAsync(await manager.GetUserIdAsync(user)));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task UserValidatorCanBlockCreate()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- manager.UserValidators.Clear();
- manager.UserValidators.Add(new AlwaysBadValidator());
- IdentityResultAssert.IsFailure(await manager.CreateAsync(user), AlwaysBadValidator.ErrorMessage);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task UserValidatorCanBlockUpdate()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- manager.UserValidators.Clear();
- manager.UserValidators.Add(new AlwaysBadValidator());
- IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanChainUserValidators()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.UserValidators.Clear();
- var user = CreateTestUser();
- manager.UserValidators.Add(new AlwaysBadValidator());
- manager.UserValidators.Add(new AlwaysBadValidator());
- var result = await manager.CreateAsync(user);
- IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code};{AlwaysBadValidator.ErrorMessage.Code}.");
- Assert.Equal(2, result.Errors.Count());
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Theory]
- [InlineData("")]
- [InlineData(null)]
- public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email)
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- manager.Options.User.RequireUniqueEmail = true;
- IdentityResultAssert.IsFailure(await manager.CreateAsync(user), _errorDescriber.InvalidEmail(email));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Theory]
- [InlineData("@@afd")]
- [InlineData("bogus")]
- public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email)
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser("UpdateBlocked", email);
- manager.Options.User.RequireUniqueEmail = true;
- IdentityResultAssert.IsFailure(await manager.CreateAsync(user), _errorDescriber.InvalidEmail(email));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task PasswordValidatorCanBlockAddPassword()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- manager.PasswordValidators.Clear();
- manager.PasswordValidators.Add(new AlwaysBadValidator());
- IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"),
- AlwaysBadValidator.ErrorMessage);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanChainPasswordValidators()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.PasswordValidators.Clear();
- manager.PasswordValidators.Add(new AlwaysBadValidator());
- manager.PasswordValidators.Add(new AlwaysBadValidator());
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var result = await manager.AddPasswordAsync(user, "pwd");
- IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage);
- Assert.Equal(2, result.Errors.Count());
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task PasswordValidatorCanBlockChangePassword()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
- manager.PasswordValidators.Clear();
- manager.PasswordValidators.Add(new AlwaysBadValidator());
- IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"),
- AlwaysBadValidator.ErrorMessage);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task PasswordValidatorCanBlockCreateUser()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- manager.PasswordValidators.Clear();
- manager.PasswordValidators.Add(new AlwaysBadValidator());
- IdentityResultAssert.IsFailure(await manager.CreateAsync(user, "password"), AlwaysBadValidator.ErrorMessage);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanCreateUserNoPassword()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var username = "CreateUserTest" + Guid.NewGuid();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(CreateTestUser(username, useNamePrefixAsUserName: true)));
- var user = await manager.FindByNameAsync(username);
- Assert.NotNull(user);
- Assert.False(await manager.HasPasswordAsync(user));
- Assert.False(await manager.CheckPasswordAsync(user, "whatever"));
- var logins = await manager.GetLoginsAsync(user);
- Assert.NotNull(logins);
- Assert.Equal(0, logins.Count());
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanCreateUserAddLogin()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- const string provider = "ZzAuth";
- const string display = "display";
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var providerKey = await manager.GetUserIdAsync(user);
- IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, new UserLoginInfo(provider, providerKey, display)));
- var logins = await manager.GetLoginsAsync(user);
- Assert.NotNull(logins);
- Assert.Equal(1, logins.Count());
- Assert.Equal(provider, logins.First().LoginProvider);
- Assert.Equal(providerKey, logins.First().ProviderKey);
- Assert.Equal(display, logins.First().ProviderDisplayName);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanCreateUserLoginAndAddPassword()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var userId = await manager.GetUserIdAsync(user);
- var login = new UserLoginInfo("Provider", userId, "display");
- IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
- Assert.False(await manager.HasPasswordAsync(user));
- IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password"));
- Assert.True(await manager.HasPasswordAsync(user));
- var logins = await manager.GetLoginsAsync(user);
- Assert.NotNull(logins);
- Assert.Equal(1, logins.Count());
- Assert.Equal(user, await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
- Assert.True(await manager.CheckPasswordAsync(user, "password"));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task AddPasswordFailsIfAlreadyHave()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password"));
- Assert.True(await manager.HasPasswordAsync(user));
- IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"),
- "User already has a password set.");
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} already has a password.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanCreateUserAddRemoveLogin()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- var result = await manager.CreateAsync(user);
- Assert.NotNull(user);
- var userId = await manager.GetUserIdAsync(user);
- var login = new UserLoginInfo("Provider", userId, "display");
- IdentityResultAssert.IsSuccess(result);
- IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
- Assert.Equal(user, await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
- var logins = await manager.GetLoginsAsync(user);
- Assert.NotNull(logins);
- Assert.Equal(1, logins.Count());
- Assert.Equal(login.LoginProvider, logins.Last().LoginProvider);
- Assert.Equal(login.ProviderKey, logins.Last().ProviderKey);
- Assert.Equal(login.ProviderDisplayName, logins.Last().ProviderDisplayName);
- var stamp = await manager.GetSecurityStampAsync(user);
- IdentityResultAssert.IsSuccess(await manager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey));
- Assert.Null(await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
- logins = await manager.GetLoginsAsync(user);
- Assert.NotNull(logins);
- Assert.Equal(0, logins.Count());
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanRemovePassword()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser("CanRemovePassword");
- const string password = "password";
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
- var stamp = await manager.GetSecurityStampAsync(user);
- var username = await manager.GetUserNameAsync(user);
- IdentityResultAssert.IsSuccess(await manager.RemovePasswordAsync(user));
- var u = await manager.FindByNameAsync(username);
- Assert.NotNull(u);
- Assert.False(await manager.HasPasswordAsync(user));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanChangePassword()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- const string password = "password";
- const string newPassword = "newpassword";
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword));
- Assert.False(await manager.CheckPasswordAsync(user, password));
- Assert.True(await manager.CheckPasswordAsync(user, newPassword));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanAddRemoveUserClaim()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") };
- foreach (Claim c in claims)
- {
- IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
- }
- var userId = await manager.GetUserIdAsync(user);
- var userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(3, userClaims.Count);
- IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
- userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(2, userClaims.Count);
- IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
- userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(1, userClaims.Count);
- IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
- userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(0, userClaims.Count);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task RemoveClaimOnlyAffectsUser()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- var user2 = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
- Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") };
- foreach (Claim c in claims)
- {
- IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
- IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, c));
- }
- var userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(3, userClaims.Count);
- IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
- userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(2, userClaims.Count);
- IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
- userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(1, userClaims.Count);
- IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
- userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(0, userClaims.Count);
- var userClaims2 = await manager.GetClaimsAsync(user2);
- Assert.Equal(3, userClaims2.Count);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanReplaceUserClaim()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a")));
- var userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(1, userClaims.Count);
- Claim claim = new Claim("c", "b");
- Claim oldClaim = userClaims.FirstOrDefault();
- IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
- var newUserClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(1, newUserClaims.Count);
- Claim newClaim = newUserClaims.FirstOrDefault();
- Assert.Equal(claim.Type, newClaim.Type);
- Assert.Equal(claim.Value, newClaim.Value);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ReplaceUserClaimOnlyAffectsUser()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- var user2 = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
- IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a")));
- IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, new Claim("c", "a")));
- var userClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(1, userClaims.Count);
- var userClaims2 = await manager.GetClaimsAsync(user);
- Assert.Equal(1, userClaims2.Count);
- Claim claim = new Claim("c", "b");
- Claim oldClaim = userClaims.FirstOrDefault();
- IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
- var newUserClaims = await manager.GetClaimsAsync(user);
- Assert.Equal(1, newUserClaims.Count);
- Claim newClaim = newUserClaims.FirstOrDefault();
- Assert.Equal(claim.Type, newClaim.Type);
- Assert.Equal(claim.Value, newClaim.Value);
- userClaims2 = await manager.GetClaimsAsync(user2);
- Assert.Equal(1, userClaims2.Count);
- Claim oldClaim2 = userClaims2.FirstOrDefault();
- Assert.Equal("c", oldClaim2.Type);
- Assert.Equal("a", oldClaim2.Value);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ChangePasswordFallsIfPasswordWrong()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
- var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword");
- IdentityResultAssert.IsFailure(result, "Incorrect password.");
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Change password failed for user {await manager.GetUserIdAsync(user)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task AddDupeUserNameFails()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var username = "AddDupeUserNameFails" + Guid.NewGuid();
- var user = CreateTestUser(username, useNamePrefixAsUserName: true);
- var user2 = CreateTestUser(username, useNamePrefixAsUserName: true);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateUserName(username));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task AddDupeEmailAllowedByDefault()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(email: "yup@yup.com");
- var user2 = CreateTestUser(email: "yup@yup.com");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user2, await manager.GetEmailAsync(user)));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task AddDupeEmailFailsWhenUniqueEmailRequired()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.Options.User.RequireUniqueEmail = true;
- var user = CreateTestUser(email: "FooUser@yup.com");
- var user2 = CreateTestUser(email: "FooUser@yup.com");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateEmail("FooUser@yup.com"));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task UpdateSecurityStampActuallyChanges()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- Assert.Null(await manager.GetSecurityStampAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task AddDupeLoginFails()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- var login = new UserLoginInfo("Provider", "key", "display");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
- var result = await manager.AddLoginAsync(user, login);
- IdentityResultAssert.IsFailure(result, _errorDescriber.LoginAlreadyAssociated());
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"AddLogin for user {await manager.GetUserIdAsync(user)} failed because it was already assocated with another user.");
- }
-
- // Email tests
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanFindByEmail()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var email = "foouser@test.com";
- var manager = CreateManager();
- var user = CreateTestUser(email: email);
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var fetch = await manager.FindByEmailAsync(email);
- Assert.Equal(user, fetch);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanFindUsersViaUserQuerable()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- if (mgr.SupportsQueryableUsers)
- {
- var users = GenerateUsers("CanFindUsersViaUserQuerable", 4);
- foreach (var u in users)
- {
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(u));
- }
- Assert.Equal(users.Count, mgr.Users.Count(UserNameStartsWithPredicate("CanFindUsersViaUserQuerable")));
- Assert.Null(mgr.Users.FirstOrDefault(UserNameEqualsPredicate("bogus")));
- }
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ConfirmEmailFalseByDefaultTest()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- }
-
- private class StaticTokenProvider : IUserTwoFactorTokenProvider
- {
- public async Task GenerateAsync(string purpose, UserManager manager, TUser user)
- {
- return MakeToken(purpose, await manager.GetUserIdAsync(user));
- }
-
- public async Task ValidateAsync(string purpose, string token, UserManager manager, TUser user)
- {
- return token == MakeToken(purpose, await manager.GetUserIdAsync(user));
- }
-
- public Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user)
- {
- return Task.FromResult(true);
- }
-
- private static string MakeToken(string purpose, string userId)
- {
- return string.Join(":", userId, purpose, "ImmaToken");
- }
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanResetPasswordWithStaticTokenProvider()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.RegisterTokenProvider("Static", new StaticTokenProvider());
- manager.Options.Tokens.PasswordResetTokenProvider = "Static";
- var user = CreateTestUser();
- const string password = "password";
- const string newPassword = "newpassword";
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- var token = await manager.GeneratePasswordResetTokenAsync(user);
- Assert.NotNull(token);
- var userId = await manager.GetUserIdAsync(user);
- IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword));
- Assert.False(await manager.CheckPasswordAsync(user, password));
- Assert.True(await manager.CheckPasswordAsync(user, newPassword));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.RegisterTokenProvider("Static", new StaticTokenProvider());
- manager.Options.Tokens.PasswordResetTokenProvider = "Static";
- var user = CreateTestUser();
- const string password = "password";
- const string newPassword = "newpassword";
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- var token = await manager.GeneratePasswordResetTokenAsync(user);
- Assert.NotNull(token);
- manager.PasswordValidators.Add(new AlwaysBadValidator());
- IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword),
- AlwaysBadValidator.ErrorMessage);
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
- Assert.True(await manager.CheckPasswordAsync(user, password));
- Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.RegisterTokenProvider("Static", new StaticTokenProvider());
- manager.Options.Tokens.PasswordResetTokenProvider = "Static";
- var user = CreateTestUser();
- const string password = "password";
- const string newPassword = "newpassword";
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token.");
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ResetPassword for user { await manager.GetUserIdAsync(user)}.");
- Assert.True(await manager.CheckPasswordAsync(user, password));
- Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.RegisterTokenProvider("Static", new StaticTokenProvider());
- var user = CreateTestUser();
- var user2 = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
- var userId = await manager.GetUserIdAsync(user);
- var token = await manager.GenerateUserTokenAsync(user, "Static", "test");
-
- Assert.True(await manager.VerifyUserTokenAsync(user, "Static", "test", token));
-
- Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test2", token));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test2 for user { await manager.GetUserIdAsync(user)}.");
-
- Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test", token + "a"));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test for user { await manager.GetUserIdAsync(user)}.");
-
- Assert.False(await manager.VerifyUserTokenAsync(user2, "Static", "test", token));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test for user { await manager.GetUserIdAsync(user2)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanConfirmEmailWithStaticToken()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.RegisterTokenProvider("Static", new StaticTokenProvider());
- manager.Options.Tokens.EmailConfirmationTokenProvider = "Static";
- var user = CreateTestUser();
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var token = await manager.GenerateEmailConfirmationTokenAsync(user);
- Assert.NotNull(token);
- var userId = await manager.GetUserIdAsync(user);
- IdentityResultAssert.IsSuccess(await manager.ConfirmEmailAsync(user, token));
- Assert.True(await manager.IsEmailConfirmedAsync(user));
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- manager.RegisterTokenProvider("Static", new StaticTokenProvider());
- manager.Options.Tokens.EmailConfirmationTokenProvider = "Static";
- var user = CreateTestUser();
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token.");
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user { await manager.GetUserIdAsync(user)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ConfirmTokenFailsAfterPasswordChange()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(namePrefix: "Test");
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
- var token = await manager.GenerateEmailConfirmationTokenAsync(user);
- Assert.NotNull(token);
- IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword"));
- IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token.");
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user { await manager.GetUserIdAsync(user)}.");
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- }
-
- // Lockout tests
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task SingleFailureLockout()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
- mgr.Options.Lockout.MaxFailedAccessAttempts = 0;
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
- Assert.True(await mgr.IsLockedOutAsync(user));
- Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out.");
-
- Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task TwoFailureLockout()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
- mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
- Assert.True(await mgr.IsLockedOutAsync(user));
- Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out.");
- Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ResetAccessCountPreventsLockout()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
- mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.ResetAccessFailedCountAsync(user));
- Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanEnableLockoutManuallyAndLockout()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
- mgr.Options.Lockout.AllowedForNewUsers = false;
- mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.False(await mgr.GetLockoutEnabledAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.SetLockoutEnabledAsync(user, true));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
- Assert.True(await mgr.IsLockedOutAsync(user));
- Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
- IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out.");
- Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()));
- Assert.False(await mgr.IsLockedOutAsync(user));
- Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task LockoutFailsIfNotEnabled()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- mgr.Options.Lockout.AllowedForNewUsers = false;
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.False(await mgr.GetLockoutEnabledAsync(user));
- IdentityResultAssert.IsFailure(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()),
- "Lockout is not enabled for this user.");
- IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"Lockout for user {await mgr.GetUserIdAsync(user)} failed because lockout is not enabled for this user.");
- Assert.False(await mgr.IsLockedOutAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- var user = CreateTestUser(lockoutEnd: DateTimeOffset.UtcNow.AddSeconds(-1));
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- Assert.False(await mgr.IsLockedOutAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddSeconds(-1)));
- Assert.False(await mgr.IsLockedOutAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- var lockoutEnd = DateTimeOffset.UtcNow.AddMinutes(5);
- var user = CreateTestUser(lockoutEnd: lockoutEnd);
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- Assert.True(await mgr.IsLockedOutAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task UserLockedOutWithDateTimeLocalKindNowPlus30()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var mgr = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
- Assert.True(await mgr.GetLockoutEnabledAsync(user));
- var lockoutEnd = new DateTimeOffset(DateTime.Now.AddMinutes(30).ToLocalTime());
- IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, lockoutEnd));
- Assert.True(await mgr.IsLockedOutAsync(user));
- var end = await mgr.GetLockoutEndDateAsync(user);
- Assert.Equal(lockoutEnd, end);
- }
-
///
/// Test.
///
@@ -2020,608 +640,6 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.Equal(roleName, await roleMgr.GetRoleNameAsync(await roleMgr.FindByNameAsync(roleName)));
}
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task SetPhoneNumberTest()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(phoneNumber: "123-456-7890");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user));
- IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
- Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanChangePhoneNumber()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(phoneNumber: "123-456-7890");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111");
- IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token1));
- Assert.True(await manager.IsPhoneNumberConfirmedAsync(user));
- Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ChangePhoneNumberFailsWithWrongToken()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(phoneNumber: "123-456-7890");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"),
- "Invalid token.");
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:111-111-1111 for user { await manager.GetUserIdAsync(user)}.");
- Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
- Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user));
- Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ChangePhoneNumberFailsWithWrongPhoneNumber()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(phoneNumber: "123-456-7890");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111");
- IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "bogus", token1),
- "Invalid token.");
- Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
- Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user));
- Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanVerifyPhoneNumber()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- const string num1 = "111-123-4567";
- const string num2 = "111-111-1111";
- var userId = await manager.GetUserIdAsync(user);
- var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, num1);
-
- var token2 = await manager.GenerateChangePhoneNumberTokenAsync(user, num2);
- Assert.NotEqual(token1, token2);
- Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num1));
- Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2));
- Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1));
- Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:111-123-4567 for user {await manager.GetUserIdAsync(user)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanChangeEmail()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser("foouser");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- var newEmail = await manager.GetUserNameAsync(user) + "@en.vec";
- var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail);
- IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1));
- Assert.True(await manager.IsEmailConfirmedAsync(user));
- Assert.Equal(await manager.GetEmailAsync(user), newEmail);
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanChangeEmailWithDifferentTokenProvider()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager(context: null, services: null,
- configureServices: s => s.Configure(
- o => o.Tokens.ProviderMap["NewProvider2"] = new TokenProviderDescriptor(typeof(EmailTokenProvider))));
- manager.Options.Tokens.ChangeEmailTokenProvider = "NewProvider2";
- var user = CreateTestUser("foouser");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- var newEmail = await manager.GetUserNameAsync(user) + "@en.vec";
- var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail);
- IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1));
- Assert.True(await manager.IsEmailConfirmedAsync(user));
- Assert.Equal(await manager.GetEmailAsync(user), newEmail);
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ChangeEmailTokensFailsAfterEmailChanged()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser("foouser");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- var newEmail = await manager.GetUserNameAsync(user) + "@en.vec";
- var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail);
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "another@email.com"));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, newEmail, token1));
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- Assert.Equal("another@email.com", await manager.GetEmailAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ChangeEmailFailsWithWrongToken()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser("foouser");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
- string oldEmail = email;
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "whatevah@foo.boop", "bogus"),
- "Invalid token.");
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangeEmail:whatevah@foo.boop for user { await manager.GetUserIdAsync(user)}.");
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- Assert.Equal(await manager.GetEmailAsync(user), oldEmail);
- Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task ChangeEmailFailsWithEmail()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser("foouser");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
- string oldEmail = email;
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- var token1 = await manager.GenerateChangeEmailTokenAsync(user, "forgot@alrea.dy");
- IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "oops@foo.boop", token1),
- "Invalid token.");
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangeEmail:oops@foo.boop for user { await manager.GetUserIdAsync(user)}.");
- Assert.False(await manager.IsEmailConfirmedAsync(user));
- Assert.Equal(await manager.GetEmailAsync(user), oldEmail);
- Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task EmailFactorFailsAfterSecurityStampChangeTest()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- string factorId = "Email"; //default
- var user = CreateTestUser("foouser");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
- var token = await manager.GenerateEmailConfirmationTokenAsync(user);
- await manager.ConfirmEmailAsync(user, token);
-
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- token = await manager.GenerateTwoFactorTokenAsync(user, factorId);
- Assert.NotNull(token);
- IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
- Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task EnableTwoFactorChangesSecurityStamp()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabledAsync(user, true));
- Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
- Assert.True(await manager.GetTwoFactorEnabledAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- const string error = "No IUserTokenProvider named 'bogus' is registered.";
- var ex = await
- Assert.ThrowsAsync(
- () => manager.GenerateTwoFactorTokenAsync(user, "bogus"));
- Assert.Equal(error, ex.Message);
- ex = await Assert.ThrowsAsync(
- () => manager.VerifyTwoFactorTokenAsync(user, "bogus", "bogus"));
- Assert.Equal(error, ex.Message);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task GetValidTwoFactorTestEmptyWithNoProviders()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var factors = await manager.GetValidTwoFactorProvidersAsync(user);
- Assert.NotNull(factors);
- Assert.True(!factors.Any());
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanGetSetUpdateAndRemoveUserToken()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.Null(await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
- IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "provider", "name", "value"));
- Assert.Equal("value", await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
-
- IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "provider", "name", "value2"));
- Assert.Equal("value2", await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
-
- IdentityResultAssert.IsSuccess(await manager.RemoveAuthenticationTokenAsync(user, "whatevs", "name"));
- Assert.Equal("value2", await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
-
- IdentityResultAssert.IsSuccess(await manager.RemoveAuthenticationTokenAsync(user, "provider", "name"));
- Assert.Null(await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanRedeemRecoveryCodeOnlyOnce()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
-
- var numCodes = 15;
- var newCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes);
- Assert.Equal(numCodes, newCodes.Count());
-
- foreach (var code in newCodes)
- {
- IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
- IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
- }
- // One last time to be sure
- foreach (var code in newCodes)
- {
- IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
- }
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task RecoveryCodesInvalidAfterReplace()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
-
- var numCodes = 15;
- var newCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes);
- Assert.Equal(numCodes, newCodes.Count());
- var realCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes);
- Assert.Equal(numCodes, realCodes.Count());
-
- foreach (var code in newCodes)
- {
- IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
- }
-
- foreach (var code in realCodes)
- {
- IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
- }
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanGetValidTwoFactor()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var userId = await manager.GetUserIdAsync(user);
- var factors = await manager.GetValidTwoFactorProvidersAsync(user);
- Assert.NotNull(factors);
- Assert.False(factors.Any());
- IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
- var token = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111");
- IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token));
- await manager.UpdateAsync(user);
- factors = await manager.GetValidTwoFactorProvidersAsync(user);
- Assert.NotNull(factors);
- Assert.Equal(1, factors.Count());
- Assert.Equal("Phone", factors[0]);
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com"));
- token = await manager.GenerateEmailConfirmationTokenAsync(user);
- await manager.ConfirmEmailAsync(user, token);
- factors = await manager.GetValidTwoFactorProvidersAsync(user);
- Assert.NotNull(factors);
- Assert.Equal(2, factors.Count());
- IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
- factors = await manager.GetValidTwoFactorProvidersAsync(user);
- Assert.NotNull(factors);
- Assert.Equal(1, factors.Count());
- Assert.Equal("Phone", factors[0]);
- IdentityResultAssert.IsSuccess(await manager.ResetAuthenticatorKeyAsync(user));
- factors = await manager.GetValidTwoFactorProvidersAsync(user);
- Assert.NotNull(factors);
- Assert.Equal(2, factors.Count());
- Assert.Equal("Authenticator", factors[1]);
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task PhoneFactorFailsAfterSecurityStampChangeTest()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var factorId = "Phone"; // default
- var user = CreateTestUser(phoneNumber: "4251234567");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var stamp = await manager.GetSecurityStampAsync(user);
- Assert.NotNull(stamp);
- var token = await manager.GenerateTwoFactorTokenAsync(user, factorId);
- Assert.NotNull(token);
- IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
- Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task VerifyTokenFromWrongTokenProviderFails()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(phoneNumber: "4251234567");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- var token = await manager.GenerateTwoFactorTokenAsync(user, "Phone");
- Assert.NotNull(token);
- Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Email", token));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task VerifyWithWrongSmsTokenFails()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
- var user = CreateTestUser(phoneNumber: "4251234567");
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
- Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Phone", "bogus"));
- IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task NullableDateTimeOperationTest()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var userMgr = CreateManager();
- var user = CreateTestUser(lockoutEnabled: true);
- IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
-
- Assert.Null(await userMgr.GetLockoutEndDateAsync(user));
-
- // set LockoutDateEndDate to null
- await userMgr.SetLockoutEndDateAsync(user, null);
- Assert.Null(await userMgr.GetLockoutEndDateAsync(user));
-
- // set to a valid value
- await userMgr.SetLockoutEndDateAsync(user, DateTimeOffset.Parse("01/01/2014"));
- Assert.Equal(DateTimeOffset.Parse("01/01/2014"), await userMgr.GetLockoutEndDateAsync(user));
- }
-
- ///
- /// Test.
- ///
- /// Task
- [Fact]
- public async Task CanGetUsersWithClaims()
- {
- if (ShouldSkipDbTests())
- {
- return;
- }
- var manager = CreateManager();
-
- for (int i = 0; i < 6; i++)
- {
- var user = CreateTestUser();
- IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
-
- if ((i % 2) == 0)
- {
- IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("foo", "bar")));
- }
- }
-
- Assert.Equal(3, (await manager.GetUsersForClaimAsync(new Claim("foo", "bar"))).Count);
-
- Assert.Equal(0, (await manager.GetUsersForClaimAsync(new Claim("123", "456"))).Count);
- }
-
///
/// Test.
///
@@ -2664,16 +682,6 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.Equal(0, (await manager.GetUsersInRoleAsync("123456")).Count);
}
- private List GenerateUsers(string userNamePrefix, int count)
- {
- var users = new List(count);
- for (var i = 0; i < count; i++)
- {
- users.Add(CreateTestUser(userNamePrefix + i));
- }
- return users;
- }
-
private List GenerateRoles(string namePrefix, int count)
{
var roles = new List(count);
diff --git a/src/Microsoft.AspNetCore.Identity.Specification.Tests/UserManagerSpecificationTests.cs b/src/Microsoft.AspNetCore.Identity.Specification.Tests/UserManagerSpecificationTests.cs
new file mode 100644
index 0000000000..0cc5f8e3b0
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/UserManagerSpecificationTests.cs
@@ -0,0 +1,2110 @@
+// 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.Linq;
+using System.Linq.Expressions;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Identity.Test
+{
+ ///
+ /// Base class for tests that exercise basic identity functionality that all stores should support.
+ ///
+ /// The type of the user.
+ public abstract class UserManagerSpecificationTestBase : UserManagerSpecificationTestBase where TUser : class { }
+
+ ///
+ /// Base class for tests that exercise basic identity functionality that all stores should support.
+ ///
+ /// The type of the user.
+ /// The primary key type.
+ public abstract class UserManagerSpecificationTestBase
+ where TUser : class
+ where TKey : IEquatable
+ {
+ ///
+ /// Null value.
+ ///
+ protected const string NullValue = "(null)";
+
+ ///
+ /// Error describer.
+ ///
+ protected readonly IdentityErrorDescriber _errorDescriber = new IdentityErrorDescriber();
+
+ ///
+ /// Configure the service collection used for tests.
+ ///
+ ///
+ ///
+ protected virtual void SetupIdentityServices(IServiceCollection services, object context)
+ => SetupBuilder(services, context);
+
+ ///
+ /// Configure the service collection used for tests.
+ ///
+ ///
+ ///
+ protected virtual IdentityBuilder SetupBuilder(IServiceCollection services, object context)
+ {
+ services.AddSingleton();
+ services.AddDataProtection();
+ var builder = services.AddIdentityCore(options =>
+ {
+ options.Password.RequireDigit = false;
+ options.Password.RequireLowercase = false;
+ options.Password.RequireNonAlphanumeric = false;
+ options.Password.RequireUppercase = false;
+ options.User.AllowedUserNameCharacters = null;
+ }).AddDefaultTokenProviders();
+ AddUserStore(services, context);
+ services.AddLogging();
+ services.AddSingleton>>(new TestLogger>());
+ return builder;
+ }
+
+ ///
+ /// If true, tests that require a database will be skipped.
+ ///
+ ///
+ protected virtual bool ShouldSkipDbTests() => false;
+
+ ///
+ /// Creates the user manager used for tests.
+ ///
+ /// The context that will be passed into the store, typically a db context.
+ /// The service collection to use, optional.
+ /// Delegate used to configure the services, optional.
+ /// The user manager to use for tests.
+ protected virtual UserManager CreateManager(object context = null, IServiceCollection services = null, Action configureServices = null)
+ {
+ if (services == null)
+ {
+ services = new ServiceCollection();
+ }
+ if (context == null)
+ {
+ context = CreateTestContext();
+ }
+ SetupIdentityServices(services, context);
+ configureServices?.Invoke(services);
+ return services.BuildServiceProvider().GetService>();
+ }
+
+ ///
+ /// Creates the context object for a test, typically a DbContext.
+ ///
+ /// The context object for a test, typically a DbContext.
+ protected abstract object CreateTestContext();
+
+ ///
+ /// Adds an IUserStore to services for the test.
+ ///
+ /// The service collection to add to.
+ /// The context for the store to use, optional.
+ protected abstract void AddUserStore(IServiceCollection services, object context = null);
+
+ ///
+ /// Set the user's password hash.
+ ///
+ /// The user to set.
+ /// The password hash to set.
+ protected abstract void SetUserPasswordHash(TUser user, string hashedPassword);
+
+ ///
+ /// Create a new test user instance.
+ ///
+ /// Optional name prefix, name will be randomized.
+ /// Optional email.
+ /// Optional phone number.
+ /// Optional lockout enabled.
+ /// Optional lockout end.
+ /// If true, the prefix should be used as the username without a random pad.
+ /// The new test user instance.
+ protected abstract TUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
+ bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = null, bool useNamePrefixAsUserName = false);
+
+ ///
+ /// Query used to do name equality checks.
+ ///
+ /// The user name to match.
+ /// The query to use.
+ protected abstract Expression> UserNameEqualsPredicate(string userName);
+
+ ///
+ /// Query used to do user name prefix matching.
+ ///
+ /// The user name to match.
+ /// The query to use.
+ protected abstract Expression> UserNameStartsWithPredicate(string userName);
+
+ private class AlwaysBadValidator : IUserValidator,
+ IPasswordValidator
+ {
+ public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad.", Code = "BadValidator" };
+
+ public Task ValidateAsync(UserManager manager, TUser user, string password)
+ {
+ return Task.FromResult(IdentityResult.Failed(ErrorMessage));
+ }
+
+ public Task ValidateAsync(UserManager manager, TUser user)
+ {
+ return Task.FromResult(IdentityResult.Failed(ErrorMessage));
+ }
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CreateUserWillSetCreateDateOnlyIfSupported()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var userId = await manager.GetUserIdAsync(user);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanDeleteUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var userId = await manager.GetUserIdAsync(user);
+ IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
+ Assert.Null(await manager.FindByIdAsync(userId));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanUpdateUserName()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var name = Guid.NewGuid().ToString();
+ var user = CreateTestUser(name);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var newName = Guid.NewGuid().ToString();
+ Assert.Null(await manager.FindByNameAsync(newName));
+ IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newName));
+ IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
+ Assert.NotNull(await manager.FindByNameAsync(newName));
+ Assert.Null(await manager.FindByNameAsync(name));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CheckSetUserNameValidatesUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var username = "UpdateAsync" + Guid.NewGuid().ToString();
+ var newUsername = "New" + Guid.NewGuid().ToString();
+ var user = CreateTestUser(username, useNamePrefixAsUserName: true);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.Null(await manager.FindByNameAsync(newUsername));
+ IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newUsername));
+ Assert.NotNull(await manager.FindByNameAsync(newUsername));
+ Assert.Null(await manager.FindByNameAsync(username));
+
+ var newUser = CreateTestUser(username, useNamePrefixAsUserName: true);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(newUser));
+ var error = _errorDescriber.InvalidUserName("");
+ IdentityResultAssert.IsFailure(await manager.SetUserNameAsync(newUser, ""), error);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(newUser)} validation failed: {error.Code}.");
+
+ error = _errorDescriber.DuplicateUserName(newUsername);
+ IdentityResultAssert.IsFailure(await manager.SetUserNameAsync(newUser, newUsername), error);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(newUser)} validation failed: {error.Code}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task SetUserNameUpdatesSecurityStamp()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var username = "UpdateAsync" + Guid.NewGuid().ToString();
+ var newUsername = "New" + Guid.NewGuid().ToString();
+ var user = CreateTestUser(username, useNamePrefixAsUserName: true);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.Null(await manager.FindByNameAsync(newUsername));
+ IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newUsername));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CreateUpdatesSecurityStamp()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var username = "Create" + Guid.NewGuid().ToString();
+ var user = CreateTestUser(username, useNamePrefixAsUserName: true);
+ var stamp = await manager.GetSecurityStampAsync(user);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.NotNull(await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CheckSetEmailValidatesUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.Options.User.RequireUniqueEmail = true;
+ manager.UserValidators.Add(new UserValidator());
+ var random = new Random();
+ var email = "foo" + random.Next() + "@example.com";
+ var newEmail = "bar" + random.Next() + "@example.com";
+ var user = CreateTestUser(email: email);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, newEmail));
+
+ var newUser = CreateTestUser(email: email);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(newUser));
+ IdentityResultAssert.IsFailure(await manager.SetEmailAsync(newUser, newEmail), _errorDescriber.DuplicateEmail(newEmail));
+ IdentityResultAssert.IsFailure(await manager.SetEmailAsync(newUser, ""), _errorDescriber.InvalidEmail(""));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanUpdatePasswordUsingHasher()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser("UpdatePassword");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
+ Assert.True(await manager.CheckPasswordAsync(user, "password"));
+ var userId = await manager.GetUserIdAsync(user);
+
+ SetUserPasswordHash(user, manager.PasswordHasher.HashPassword(user, "New"));
+ IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
+ Assert.False(await manager.CheckPasswordAsync(user, "password"));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Invalid password for user {await manager.GetUserIdAsync(user)}.");
+ Assert.True(await manager.CheckPasswordAsync(user, "New"));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanFindById()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.NotNull(await manager.FindByIdAsync(await manager.GetUserIdAsync(user)));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task UserValidatorCanBlockCreate()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ manager.UserValidators.Clear();
+ manager.UserValidators.Add(new AlwaysBadValidator());
+ IdentityResultAssert.IsFailure(await manager.CreateAsync(user), AlwaysBadValidator.ErrorMessage);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task UserValidatorCanBlockUpdate()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ manager.UserValidators.Clear();
+ manager.UserValidators.Add(new AlwaysBadValidator());
+ IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanChainUserValidators()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.UserValidators.Clear();
+ var user = CreateTestUser();
+ manager.UserValidators.Add(new AlwaysBadValidator());
+ manager.UserValidators.Add(new AlwaysBadValidator());
+ var result = await manager.CreateAsync(user);
+ IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code};{AlwaysBadValidator.ErrorMessage.Code}.");
+ Assert.Equal(2, result.Errors.Count());
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Theory]
+ [InlineData("")]
+ [InlineData(null)]
+ public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email)
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ manager.Options.User.RequireUniqueEmail = true;
+ IdentityResultAssert.IsFailure(await manager.CreateAsync(user), _errorDescriber.InvalidEmail(email));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Theory]
+ [InlineData("@@afd")]
+ [InlineData("bogus")]
+ public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email)
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser("UpdateBlocked", email);
+ manager.Options.User.RequireUniqueEmail = true;
+ IdentityResultAssert.IsFailure(await manager.CreateAsync(user), _errorDescriber.InvalidEmail(email));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task PasswordValidatorCanBlockAddPassword()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ manager.PasswordValidators.Clear();
+ manager.PasswordValidators.Add(new AlwaysBadValidator());
+ IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"),
+ AlwaysBadValidator.ErrorMessage);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanChainPasswordValidators()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.PasswordValidators.Clear();
+ manager.PasswordValidators.Add(new AlwaysBadValidator());
+ manager.PasswordValidators.Add(new AlwaysBadValidator());
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var result = await manager.AddPasswordAsync(user, "pwd");
+ IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage);
+ Assert.Equal(2, result.Errors.Count());
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task PasswordValidatorCanBlockChangePassword()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
+ manager.PasswordValidators.Clear();
+ manager.PasswordValidators.Add(new AlwaysBadValidator());
+ IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"),
+ AlwaysBadValidator.ErrorMessage);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task PasswordValidatorCanBlockCreateUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ manager.PasswordValidators.Clear();
+ manager.PasswordValidators.Add(new AlwaysBadValidator());
+ IdentityResultAssert.IsFailure(await manager.CreateAsync(user, "password"), AlwaysBadValidator.ErrorMessage);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanCreateUserNoPassword()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var username = "CreateUserTest" + Guid.NewGuid();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(CreateTestUser(username, useNamePrefixAsUserName: true)));
+ var user = await manager.FindByNameAsync(username);
+ Assert.NotNull(user);
+ Assert.False(await manager.HasPasswordAsync(user));
+ Assert.False(await manager.CheckPasswordAsync(user, "whatever"));
+ var logins = await manager.GetLoginsAsync(user);
+ Assert.NotNull(logins);
+ Assert.Equal(0, logins.Count());
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanCreateUserAddLogin()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ const string provider = "ZzAuth";
+ const string display = "display";
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var providerKey = await manager.GetUserIdAsync(user);
+ IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, new UserLoginInfo(provider, providerKey, display)));
+ var logins = await manager.GetLoginsAsync(user);
+ Assert.NotNull(logins);
+ Assert.Equal(1, logins.Count());
+ Assert.Equal(provider, logins.First().LoginProvider);
+ Assert.Equal(providerKey, logins.First().ProviderKey);
+ Assert.Equal(display, logins.First().ProviderDisplayName);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanCreateUserLoginAndAddPassword()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var userId = await manager.GetUserIdAsync(user);
+ var login = new UserLoginInfo("Provider", userId, "display");
+ IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
+ Assert.False(await manager.HasPasswordAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password"));
+ Assert.True(await manager.HasPasswordAsync(user));
+ var logins = await manager.GetLoginsAsync(user);
+ Assert.NotNull(logins);
+ Assert.Equal(1, logins.Count());
+ Assert.Equal(user, await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
+ Assert.True(await manager.CheckPasswordAsync(user, "password"));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task AddPasswordFailsIfAlreadyHave()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password"));
+ Assert.True(await manager.HasPasswordAsync(user));
+ IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"),
+ "User already has a password set.");
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} already has a password.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanCreateUserAddRemoveLogin()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ var result = await manager.CreateAsync(user);
+ Assert.NotNull(user);
+ var userId = await manager.GetUserIdAsync(user);
+ var login = new UserLoginInfo("Provider", userId, "display");
+ IdentityResultAssert.IsSuccess(result);
+ IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
+ Assert.Equal(user, await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
+ var logins = await manager.GetLoginsAsync(user);
+ Assert.NotNull(logins);
+ Assert.Equal(1, logins.Count());
+ Assert.Equal(login.LoginProvider, logins.Last().LoginProvider);
+ Assert.Equal(login.ProviderKey, logins.Last().ProviderKey);
+ Assert.Equal(login.ProviderDisplayName, logins.Last().ProviderDisplayName);
+ var stamp = await manager.GetSecurityStampAsync(user);
+ IdentityResultAssert.IsSuccess(await manager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey));
+ Assert.Null(await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
+ logins = await manager.GetLoginsAsync(user);
+ Assert.NotNull(logins);
+ Assert.Equal(0, logins.Count());
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanRemovePassword()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser("CanRemovePassword");
+ const string password = "password";
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ var username = await manager.GetUserNameAsync(user);
+ IdentityResultAssert.IsSuccess(await manager.RemovePasswordAsync(user));
+ var u = await manager.FindByNameAsync(username);
+ Assert.NotNull(u);
+ Assert.False(await manager.HasPasswordAsync(user));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanChangePassword()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ const string password = "password";
+ const string newPassword = "newpassword";
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword));
+ Assert.False(await manager.CheckPasswordAsync(user, password));
+ Assert.True(await manager.CheckPasswordAsync(user, newPassword));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanAddRemoveUserClaim()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") };
+ foreach (Claim c in claims)
+ {
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
+ }
+ var userId = await manager.GetUserIdAsync(user);
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(3, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(2, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(0, userClaims.Count);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task RemoveClaimOnlyAffectsUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ var user2 = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
+ Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") };
+ foreach (Claim c in claims)
+ {
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, c));
+ }
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(3, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(2, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(0, userClaims.Count);
+ var userClaims2 = await manager.GetClaimsAsync(user2);
+ Assert.Equal(3, userClaims2.Count);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanReplaceUserClaim()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a")));
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ Claim claim = new Claim("c", "b");
+ Claim oldClaim = userClaims.FirstOrDefault();
+ IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
+ var newUserClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, newUserClaims.Count);
+ Claim newClaim = newUserClaims.FirstOrDefault();
+ Assert.Equal(claim.Type, newClaim.Type);
+ Assert.Equal(claim.Value, newClaim.Value);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ReplaceUserClaimOnlyAffectsUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ var user2 = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a")));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, new Claim("c", "a")));
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ var userClaims2 = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims2.Count);
+ Claim claim = new Claim("c", "b");
+ Claim oldClaim = userClaims.FirstOrDefault();
+ IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
+ var newUserClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, newUserClaims.Count);
+ Claim newClaim = newUserClaims.FirstOrDefault();
+ Assert.Equal(claim.Type, newClaim.Type);
+ Assert.Equal(claim.Value, newClaim.Value);
+ userClaims2 = await manager.GetClaimsAsync(user2);
+ Assert.Equal(1, userClaims2.Count);
+ Claim oldClaim2 = userClaims2.FirstOrDefault();
+ Assert.Equal("c", oldClaim2.Type);
+ Assert.Equal("a", oldClaim2.Value);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ChangePasswordFallsIfPasswordWrong()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
+ var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword");
+ IdentityResultAssert.IsFailure(result, "Incorrect password.");
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Change password failed for user {await manager.GetUserIdAsync(user)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task AddDupeUserNameFails()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var username = "AddDupeUserNameFails" + Guid.NewGuid();
+ var user = CreateTestUser(username, useNamePrefixAsUserName: true);
+ var user2 = CreateTestUser(username, useNamePrefixAsUserName: true);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateUserName(username));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task AddDupeEmailAllowedByDefault()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(email: "yup@yup.com");
+ var user2 = CreateTestUser(email: "yup@yup.com");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user2, await manager.GetEmailAsync(user)));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task AddDupeEmailFailsWhenUniqueEmailRequired()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.Options.User.RequireUniqueEmail = true;
+ var user = CreateTestUser(email: "FooUser@yup.com");
+ var user2 = CreateTestUser(email: "FooUser@yup.com");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateEmail("FooUser@yup.com"));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task UpdateSecurityStampActuallyChanges()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ Assert.Null(await manager.GetSecurityStampAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task AddDupeLoginFails()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ var login = new UserLoginInfo("Provider", "key", "display");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
+ var result = await manager.AddLoginAsync(user, login);
+ IdentityResultAssert.IsFailure(result, _errorDescriber.LoginAlreadyAssociated());
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"AddLogin for user {await manager.GetUserIdAsync(user)} failed because it was already assocated with another user.");
+ }
+
+ // Email tests
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanFindByEmail()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var email = "foouser@test.com";
+ var manager = CreateManager();
+ var user = CreateTestUser(email: email);
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var fetch = await manager.FindByEmailAsync(email);
+ Assert.Equal(user, fetch);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanFindUsersViaUserQuerable()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ if (mgr.SupportsQueryableUsers)
+ {
+ var users = GenerateUsers("CanFindUsersViaUserQuerable", 4);
+ foreach (var u in users)
+ {
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(u));
+ }
+ Assert.Equal(users.Count, mgr.Users.Count(UserNameStartsWithPredicate("CanFindUsersViaUserQuerable")));
+ Assert.Null(mgr.Users.FirstOrDefault(UserNameEqualsPredicate("bogus")));
+ }
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ConfirmEmailFalseByDefaultTest()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ }
+
+ private class StaticTokenProvider : IUserTwoFactorTokenProvider
+ {
+ public async Task GenerateAsync(string purpose, UserManager manager, TUser user)
+ {
+ return MakeToken(purpose, await manager.GetUserIdAsync(user));
+ }
+
+ public async Task ValidateAsync(string purpose, string token, UserManager manager, TUser user)
+ {
+ return token == MakeToken(purpose, await manager.GetUserIdAsync(user));
+ }
+
+ public Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user)
+ {
+ return Task.FromResult(true);
+ }
+
+ private static string MakeToken(string purpose, string userId)
+ {
+ return string.Join(":", userId, purpose, "ImmaToken");
+ }
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanResetPasswordWithStaticTokenProvider()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.RegisterTokenProvider("Static", new StaticTokenProvider());
+ manager.Options.Tokens.PasswordResetTokenProvider = "Static";
+ var user = CreateTestUser();
+ const string password = "password";
+ const string newPassword = "newpassword";
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ var token = await manager.GeneratePasswordResetTokenAsync(user);
+ Assert.NotNull(token);
+ var userId = await manager.GetUserIdAsync(user);
+ IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword));
+ Assert.False(await manager.CheckPasswordAsync(user, password));
+ Assert.True(await manager.CheckPasswordAsync(user, newPassword));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.RegisterTokenProvider("Static", new StaticTokenProvider());
+ manager.Options.Tokens.PasswordResetTokenProvider = "Static";
+ var user = CreateTestUser();
+ const string password = "password";
+ const string newPassword = "newpassword";
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ var token = await manager.GeneratePasswordResetTokenAsync(user);
+ Assert.NotNull(token);
+ manager.PasswordValidators.Add(new AlwaysBadValidator());
+ IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword),
+ AlwaysBadValidator.ErrorMessage);
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
+ Assert.True(await manager.CheckPasswordAsync(user, password));
+ Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.RegisterTokenProvider("Static", new StaticTokenProvider());
+ manager.Options.Tokens.PasswordResetTokenProvider = "Static";
+ var user = CreateTestUser();
+ const string password = "password";
+ const string newPassword = "newpassword";
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token.");
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ResetPassword for user { await manager.GetUserIdAsync(user)}.");
+ Assert.True(await manager.CheckPasswordAsync(user, password));
+ Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.RegisterTokenProvider("Static", new StaticTokenProvider());
+ var user = CreateTestUser();
+ var user2 = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
+ var userId = await manager.GetUserIdAsync(user);
+ var token = await manager.GenerateUserTokenAsync(user, "Static", "test");
+
+ Assert.True(await manager.VerifyUserTokenAsync(user, "Static", "test", token));
+
+ Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test2", token));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test2 for user { await manager.GetUserIdAsync(user)}.");
+
+ Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test", token + "a"));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test for user { await manager.GetUserIdAsync(user)}.");
+
+ Assert.False(await manager.VerifyUserTokenAsync(user2, "Static", "test", token));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test for user { await manager.GetUserIdAsync(user2)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanConfirmEmailWithStaticToken()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.RegisterTokenProvider("Static", new StaticTokenProvider());
+ manager.Options.Tokens.EmailConfirmationTokenProvider = "Static";
+ var user = CreateTestUser();
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var token = await manager.GenerateEmailConfirmationTokenAsync(user);
+ Assert.NotNull(token);
+ var userId = await manager.GetUserIdAsync(user);
+ IdentityResultAssert.IsSuccess(await manager.ConfirmEmailAsync(user, token));
+ Assert.True(await manager.IsEmailConfirmedAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ manager.RegisterTokenProvider("Static", new StaticTokenProvider());
+ manager.Options.Tokens.EmailConfirmationTokenProvider = "Static";
+ var user = CreateTestUser();
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token.");
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user { await manager.GetUserIdAsync(user)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ConfirmTokenFailsAfterPasswordChange()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(namePrefix: "Test");
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
+ var token = await manager.GenerateEmailConfirmationTokenAsync(user);
+ Assert.NotNull(token);
+ IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword"));
+ IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token.");
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user { await manager.GetUserIdAsync(user)}.");
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ }
+
+ // Lockout tests
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task SingleFailureLockout()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
+ mgr.Options.Lockout.MaxFailedAccessAttempts = 0;
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
+ Assert.True(await mgr.IsLockedOutAsync(user));
+ Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out.");
+
+ Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task TwoFailureLockout()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
+ mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
+ Assert.True(await mgr.IsLockedOutAsync(user));
+ Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out.");
+ Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ResetAccessCountPreventsLockout()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
+ mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.ResetAccessFailedCountAsync(user));
+ Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanEnableLockoutManuallyAndLockout()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
+ mgr.Options.Lockout.AllowedForNewUsers = false;
+ mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.False(await mgr.GetLockoutEnabledAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.SetLockoutEnabledAsync(user, true));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
+ Assert.True(await mgr.IsLockedOutAsync(user));
+ Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
+ IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out.");
+ Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task LockoutFailsIfNotEnabled()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ mgr.Options.Lockout.AllowedForNewUsers = false;
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.False(await mgr.GetLockoutEnabledAsync(user));
+ IdentityResultAssert.IsFailure(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()),
+ "Lockout is not enabled for this user.");
+ IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"Lockout for user {await mgr.GetUserIdAsync(user)} failed because lockout is not enabled for this user.");
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ var user = CreateTestUser(lockoutEnd: DateTimeOffset.UtcNow.AddSeconds(-1));
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddSeconds(-1)));
+ Assert.False(await mgr.IsLockedOutAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ var lockoutEnd = DateTimeOffset.UtcNow.AddMinutes(5);
+ var user = CreateTestUser(lockoutEnd: lockoutEnd);
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ Assert.True(await mgr.IsLockedOutAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task UserLockedOutWithDateTimeLocalKindNowPlus30()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var mgr = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
+ Assert.True(await mgr.GetLockoutEnabledAsync(user));
+ var lockoutEnd = new DateTimeOffset(DateTime.Now.AddMinutes(30).ToLocalTime());
+ IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, lockoutEnd));
+ Assert.True(await mgr.IsLockedOutAsync(user));
+ var end = await mgr.GetLockoutEndDateAsync(user);
+ Assert.Equal(lockoutEnd, end);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task SetPhoneNumberTest()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(phoneNumber: "123-456-7890");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
+ Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanChangePhoneNumber()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(phoneNumber: "123-456-7890");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111");
+ IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token1));
+ Assert.True(await manager.IsPhoneNumberConfirmedAsync(user));
+ Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ChangePhoneNumberFailsWithWrongToken()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(phoneNumber: "123-456-7890");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"),
+ "Invalid token.");
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:111-111-1111 for user { await manager.GetUserIdAsync(user)}.");
+ Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
+ Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user));
+ Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ChangePhoneNumberFailsWithWrongPhoneNumber()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(phoneNumber: "123-456-7890");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111");
+ IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "bogus", token1),
+ "Invalid token.");
+ Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
+ Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user));
+ Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanVerifyPhoneNumber()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ const string num1 = "111-123-4567";
+ const string num2 = "111-111-1111";
+ var userId = await manager.GetUserIdAsync(user);
+ var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, num1);
+
+ var token2 = await manager.GenerateChangePhoneNumberTokenAsync(user, num2);
+ Assert.NotEqual(token1, token2);
+ Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num1));
+ Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2));
+ Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1));
+ Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:111-123-4567 for user {await manager.GetUserIdAsync(user)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanChangeEmail()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser("foouser");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ var newEmail = await manager.GetUserNameAsync(user) + "@en.vec";
+ var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail);
+ IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1));
+ Assert.True(await manager.IsEmailConfirmedAsync(user));
+ Assert.Equal(await manager.GetEmailAsync(user), newEmail);
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanChangeEmailWithDifferentTokenProvider()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager(context: null, services: null,
+ configureServices: s => s.Configure(
+ o => o.Tokens.ProviderMap["NewProvider2"] = new TokenProviderDescriptor(typeof(EmailTokenProvider))));
+ manager.Options.Tokens.ChangeEmailTokenProvider = "NewProvider2";
+ var user = CreateTestUser("foouser");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ var newEmail = await manager.GetUserNameAsync(user) + "@en.vec";
+ var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail);
+ IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1));
+ Assert.True(await manager.IsEmailConfirmedAsync(user));
+ Assert.Equal(await manager.GetEmailAsync(user), newEmail);
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ChangeEmailTokensFailsAfterEmailChanged()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser("foouser");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ var newEmail = await manager.GetUserNameAsync(user) + "@en.vec";
+ var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail);
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "another@email.com"));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, newEmail, token1));
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ Assert.Equal("another@email.com", await manager.GetEmailAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ChangeEmailFailsWithWrongToken()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser("foouser");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
+ string oldEmail = email;
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "whatevah@foo.boop", "bogus"),
+ "Invalid token.");
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangeEmail:whatevah@foo.boop for user { await manager.GetUserIdAsync(user)}.");
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ Assert.Equal(await manager.GetEmailAsync(user), oldEmail);
+ Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task ChangeEmailFailsWithEmail()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser("foouser");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
+ string oldEmail = email;
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ var token1 = await manager.GenerateChangeEmailTokenAsync(user, "forgot@alrea.dy");
+ IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "oops@foo.boop", token1),
+ "Invalid token.");
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangeEmail:oops@foo.boop for user { await manager.GetUserIdAsync(user)}.");
+ Assert.False(await manager.IsEmailConfirmedAsync(user));
+ Assert.Equal(await manager.GetEmailAsync(user), oldEmail);
+ Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task EmailFactorFailsAfterSecurityStampChangeTest()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ string factorId = "Email"; //default
+ var user = CreateTestUser("foouser");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var email = await manager.GetUserNameAsync(user) + "@diddly.bop";
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email));
+ var token = await manager.GenerateEmailConfirmationTokenAsync(user);
+ await manager.ConfirmEmailAsync(user, token);
+
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ token = await manager.GenerateTwoFactorTokenAsync(user, factorId);
+ Assert.NotNull(token);
+ IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
+ Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task EnableTwoFactorChangesSecurityStamp()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabledAsync(user, true));
+ Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
+ Assert.True(await manager.GetTwoFactorEnabledAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ const string error = "No IUserTokenProvider named 'bogus' is registered.";
+ var ex = await
+ Assert.ThrowsAsync(
+ () => manager.GenerateTwoFactorTokenAsync(user, "bogus"));
+ Assert.Equal(error, ex.Message);
+ ex = await Assert.ThrowsAsync(
+ () => manager.VerifyTwoFactorTokenAsync(user, "bogus", "bogus"));
+ Assert.Equal(error, ex.Message);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task GetValidTwoFactorTestEmptyWithNoProviders()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var factors = await manager.GetValidTwoFactorProvidersAsync(user);
+ Assert.NotNull(factors);
+ Assert.True(!factors.Any());
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanGetSetUpdateAndRemoveUserToken()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.Null(await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
+ IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "provider", "name", "value"));
+ Assert.Equal("value", await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
+
+ IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "provider", "name", "value2"));
+ Assert.Equal("value2", await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
+
+ IdentityResultAssert.IsSuccess(await manager.RemoveAuthenticationTokenAsync(user, "whatevs", "name"));
+ Assert.Equal("value2", await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
+
+ IdentityResultAssert.IsSuccess(await manager.RemoveAuthenticationTokenAsync(user, "provider", "name"));
+ Assert.Null(await manager.GetAuthenticationTokenAsync(user, "provider", "name"));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanRedeemRecoveryCodeOnlyOnce()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+
+ var numCodes = 15;
+ var newCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes);
+ Assert.Equal(numCodes, newCodes.Count());
+
+ foreach (var code in newCodes)
+ {
+ IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
+ IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
+ }
+ // One last time to be sure
+ foreach (var code in newCodes)
+ {
+ IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
+ }
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task RecoveryCodesInvalidAfterReplace()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+
+ var numCodes = 15;
+ var newCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes);
+ Assert.Equal(numCodes, newCodes.Count());
+ var realCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes);
+ Assert.Equal(numCodes, realCodes.Count());
+
+ foreach (var code in newCodes)
+ {
+ IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
+ }
+
+ foreach (var code in realCodes)
+ {
+ IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code));
+ }
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanGetValidTwoFactor()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var userId = await manager.GetUserIdAsync(user);
+ var factors = await manager.GetValidTwoFactorProvidersAsync(user);
+ Assert.NotNull(factors);
+ Assert.False(factors.Any());
+ IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
+ var token = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111");
+ IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token));
+ await manager.UpdateAsync(user);
+ factors = await manager.GetValidTwoFactorProvidersAsync(user);
+ Assert.NotNull(factors);
+ Assert.Equal(1, factors.Count());
+ Assert.Equal("Phone", factors[0]);
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com"));
+ token = await manager.GenerateEmailConfirmationTokenAsync(user);
+ await manager.ConfirmEmailAsync(user, token);
+ factors = await manager.GetValidTwoFactorProvidersAsync(user);
+ Assert.NotNull(factors);
+ Assert.Equal(2, factors.Count());
+ IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
+ factors = await manager.GetValidTwoFactorProvidersAsync(user);
+ Assert.NotNull(factors);
+ Assert.Equal(1, factors.Count());
+ Assert.Equal("Phone", factors[0]);
+ IdentityResultAssert.IsSuccess(await manager.ResetAuthenticatorKeyAsync(user));
+ factors = await manager.GetValidTwoFactorProvidersAsync(user);
+ Assert.NotNull(factors);
+ Assert.Equal(2, factors.Count());
+ Assert.Equal("Authenticator", factors[1]);
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task PhoneFactorFailsAfterSecurityStampChangeTest()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var factorId = "Phone"; // default
+ var user = CreateTestUser(phoneNumber: "4251234567");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var stamp = await manager.GetSecurityStampAsync(user);
+ Assert.NotNull(stamp);
+ var token = await manager.GenerateTwoFactorTokenAsync(user, factorId);
+ Assert.NotNull(token);
+ IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
+ Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task VerifyTokenFromWrongTokenProviderFails()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(phoneNumber: "4251234567");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ var token = await manager.GenerateTwoFactorTokenAsync(user, "Phone");
+ Assert.NotNull(token);
+ Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Email", token));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task VerifyWithWrongSmsTokenFails()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser(phoneNumber: "4251234567");
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Phone", "bogus"));
+ IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task NullableDateTimeOperationTest()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var userMgr = CreateManager();
+ var user = CreateTestUser(lockoutEnabled: true);
+ IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
+
+ Assert.Null(await userMgr.GetLockoutEndDateAsync(user));
+
+ // set LockoutDateEndDate to null
+ await userMgr.SetLockoutEndDateAsync(user, null);
+ Assert.Null(await userMgr.GetLockoutEndDateAsync(user));
+
+ // set to a valid value
+ await userMgr.SetLockoutEndDateAsync(user, DateTimeOffset.Parse("01/01/2014"));
+ Assert.Equal(DateTimeOffset.Parse("01/01/2014"), await userMgr.GetLockoutEndDateAsync(user));
+ }
+
+ ///
+ /// Test.
+ ///
+ /// Task
+ [Fact]
+ public async Task CanGetUsersWithClaims()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+
+ for (int i = 0; i < 6; i++)
+ {
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+
+ if ((i % 2) == 0)
+ {
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("foo", "bar")));
+ }
+ }
+
+ Assert.Equal(3, (await manager.GetUsersForClaimAsync(new Claim("foo", "bar"))).Count);
+
+ Assert.Equal(0, (await manager.GetUsersForClaimAsync(new Claim("123", "456"))).Count);
+ }
+
+ ///
+ /// Generate count users with a name prefix.
+ ///
+ ///
+ ///
+ ///
+ protected List GenerateUsers(string userNamePrefix, int count)
+ {
+ var users = new List(count);
+ for (var i = 0; i < count; i++)
+ {
+ users.Add(CreateTestUser(userNamePrefix + i));
+ }
+ return users;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Identity/Base32.cs b/src/Microsoft.AspNetCore.Identity/Base32.cs
deleted file mode 100644
index 579a58feac..0000000000
--- a/src/Microsoft.AspNetCore.Identity/Base32.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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.Text;
-
-namespace Microsoft.AspNetCore.Identity
-{
- // See http://tools.ietf.org/html/rfc3548#section-5
- internal static class Base32
- {
- private static readonly string _base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
-
- public static string ToBase32(byte[] input)
- {
- if (input == null)
- {
- throw new ArgumentNullException(nameof(input));
- }
-
- StringBuilder sb = new StringBuilder();
- for (int offset = 0; offset < input.Length;)
- {
- byte a, b, c, d, e, f, g, h;
- int numCharsToOutput = GetNextGroup(input, ref offset, out a, out b, out c, out d, out e, out f, out g, out h);
-
- sb.Append((numCharsToOutput >= 1) ? _base32Chars[a] : '=');
- sb.Append((numCharsToOutput >= 2) ? _base32Chars[b] : '=');
- sb.Append((numCharsToOutput >= 3) ? _base32Chars[c] : '=');
- sb.Append((numCharsToOutput >= 4) ? _base32Chars[d] : '=');
- sb.Append((numCharsToOutput >= 5) ? _base32Chars[e] : '=');
- sb.Append((numCharsToOutput >= 6) ? _base32Chars[f] : '=');
- sb.Append((numCharsToOutput >= 7) ? _base32Chars[g] : '=');
- sb.Append((numCharsToOutput >= 8) ? _base32Chars[h] : '=');
- }
-
- return sb.ToString();
- }
-
- public static byte[] FromBase32(string input)
- {
- if (input == null)
- {
- throw new ArgumentNullException(nameof(input));
- }
- input = input.TrimEnd('=').ToUpperInvariant();
- if (input.Length == 0)
- {
- return new byte[0];
- }
-
- var output = new byte[input.Length * 5 / 8];
- var bitIndex = 0;
- var inputIndex = 0;
- var outputBits = 0;
- var outputIndex = 0;
- while (outputIndex < output.Length)
- {
- var byteIndex = _base32Chars.IndexOf(input[inputIndex]);
- if (byteIndex < 0)
- {
- throw new FormatException();
- }
-
- var bits = Math.Min(5 - bitIndex, 8 - outputBits);
- output[outputIndex] <<= bits;
- output[outputIndex] |= (byte)(byteIndex >> (5 - (bitIndex + bits)));
-
- bitIndex += bits;
- if (bitIndex >= 5)
- {
- inputIndex++;
- bitIndex = 0;
- }
-
- outputBits += bits;
- if (outputBits >= 8)
- {
- outputIndex++;
- outputBits = 0;
- }
- }
- return output;
- }
-
- // returns the number of bytes that were output
- private static int GetNextGroup(byte[] input, ref int offset, out byte a, out byte b, out byte c, out byte d, out byte e, out byte f, out byte g, out byte h)
- {
- uint b1, b2, b3, b4, b5;
-
- int retVal;
- switch (offset - input.Length)
- {
- case 1: retVal = 2; break;
- case 2: retVal = 4; break;
- case 3: retVal = 5; break;
- case 4: retVal = 7; break;
- default: retVal = 8; break;
- }
-
- b1 = (offset < input.Length) ? input[offset++] : 0U;
- b2 = (offset < input.Length) ? input[offset++] : 0U;
- b3 = (offset < input.Length) ? input[offset++] : 0U;
- b4 = (offset < input.Length) ? input[offset++] : 0U;
- b5 = (offset < input.Length) ? input[offset++] : 0U;
-
- a = (byte)(b1 >> 3);
- b = (byte)(((b1 & 0x07) << 2) | (b2 >> 6));
- c = (byte)((b2 >> 1) & 0x1f);
- d = (byte)(((b2 & 0x01) << 4) | (b3 >> 4));
- e = (byte)(((b3 & 0x0f) << 1) | (b4 >> 7));
- f = (byte)((b4 >> 2) & 0x1f);
- g = (byte)(((b4 & 0x3) << 3) | (b5 >> 5));
- h = (byte)(b5 & 0x1f);
-
- return retVal;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Identity/IdentityBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity/IdentityBuilderExtensions.cs
new file mode 100644
index 0000000000..6fa60725a5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Identity/IdentityBuilderExtensions.cs
@@ -0,0 +1,57 @@
+// 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.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Identity
+{
+ ///
+ /// Helper functions for configuring identity services.
+ ///
+ public static class IdentityBuilderExtensions
+ {
+ ///
+ /// Adds the default token providers used to generate tokens for reset passwords, change email
+ /// and change telephone number operations, and for two factor authentication token generation.
+ ///
+ /// The current instance.
+ /// The current instance.
+ public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder)
+ {
+ var userType = builder.UserType;
+ var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(userType);
+ var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType);
+ var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(userType);
+ var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(userType);
+ return builder.AddTokenProvider(TokenOptions.DefaultProvider, dataProtectionProviderType)
+ .AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType)
+ .AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType)
+ .AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType);
+ }
+
+ ///
+ /// Adds a for the .
+ ///
+ /// The type of the sign in manager to add.
+ /// The current instance.
+ /// The current instance.
+ public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) where TSignInManager : class
+ {
+ var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType);
+
+ var customType = typeof(TSignInManager);
+ if (!managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
+ {
+ throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "SignInManager", builder.UserType.Name));
+ }
+ if (managerType != customType)
+ {
+ builder.Services.AddScoped(typeof(TSignInManager), services => services.GetRequiredService(managerType));
+ }
+ builder.Services.AddScoped(managerType, typeof(TSignInManager));
+ return builder;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs
index 35f03f932a..afd02fc138 100644
--- a/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs
@@ -5,58 +5,52 @@ namespace Microsoft.AspNetCore.Identity
using System.Reflection;
using System.Resources;
- internal static class AspNetIdentityResources
+ internal static class Resources
{
private static readonly ResourceManager _resourceManager
- = new ResourceManager("Microsoft.AspNetCore.Identity.Resources", typeof(AspNetIdentityResources).GetTypeInfo().Assembly);
+ = new ResourceManager("Microsoft.AspNetCore.Identity.Resources", typeof(Resources).GetTypeInfo().Assembly);
///
/// Type {0} must derive from {1}<{2}>.
///
internal static string InvalidManagerType
{
- get { return GetString("InvalidManagerType"); }
+ get => GetString("InvalidManagerType");
}
///
/// Type {0} must derive from {1}<{2}>.
///
internal static string FormatInvalidManagerType(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("InvalidManagerType"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidManagerType"), p0, p1, p2);
///
/// The provided PasswordHasherCompatibilityMode is invalid.
///
internal static string InvalidPasswordHasherCompatibilityMode
{
- get { return GetString("InvalidPasswordHasherCompatibilityMode"); }
+ get => GetString("InvalidPasswordHasherCompatibilityMode");
}
///
/// The provided PasswordHasherCompatibilityMode is invalid.
///
internal static string FormatInvalidPasswordHasherCompatibilityMode()
- {
- return GetString("InvalidPasswordHasherCompatibilityMode");
- }
+ => GetString("InvalidPasswordHasherCompatibilityMode");
///
/// The iteration count must be a positive integer.
///
internal static string InvalidPasswordHasherIterationCount
{
- get { return GetString("InvalidPasswordHasherIterationCount"); }
+ get => GetString("InvalidPasswordHasherIterationCount");
}
///
/// The iteration count must be a positive integer.
///
internal static string FormatInvalidPasswordHasherIterationCount()
- {
- return GetString("InvalidPasswordHasherIterationCount");
- }
+ => GetString("InvalidPasswordHasherIterationCount");
private static string GetString(string name, params string[] formatterNames)
{
diff --git a/src/Microsoft.AspNetCore.Identity/AuthenticatorTokenProvider.cs b/src/Microsoft.Extensions.Identity.Core/AuthenticatorTokenProvider.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/AuthenticatorTokenProvider.cs
rename to src/Microsoft.Extensions.Identity.Core/AuthenticatorTokenProvider.cs
diff --git a/src/Microsoft.AspNetCore.Identity/EmailTokenProvider.cs b/src/Microsoft.Extensions.Identity.Core/EmailTokenProvider.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/EmailTokenProvider.cs
rename to src/Microsoft.Extensions.Identity.Core/EmailTokenProvider.cs
diff --git a/src/Microsoft.AspNetCore.Identity/IdentityBuilder.cs b/src/Microsoft.Extensions.Identity.Core/IdentityBuilder.cs
similarity index 60%
rename from src/Microsoft.AspNetCore.Identity/IdentityBuilder.cs
rename to src/Microsoft.Extensions.Identity.Core/IdentityBuilder.cs
index 486958e5cc..7146ef4e0c 100644
--- a/src/Microsoft.AspNetCore.Identity/IdentityBuilder.cs
+++ b/src/Microsoft.Extensions.Identity.Core/IdentityBuilder.cs
@@ -5,6 +5,8 @@ using System;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Identity.Core;
namespace Microsoft.AspNetCore.Identity
{
@@ -17,15 +19,22 @@ namespace Microsoft.AspNetCore.Identity
/// Creates a new instance of .
///
/// The to use for the users.
- /// The to use for the roles.
/// The to attach to.
- public IdentityBuilder(Type user, Type role, IServiceCollection services)
+ public IdentityBuilder(Type user, IServiceCollection services)
{
UserType = user;
- RoleType = role;
Services = services;
}
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The to use for the users.
+ /// The to use for the roles.
+ /// The to attach to.
+ public IdentityBuilder(Type user, Type role, IServiceCollection services) : this(user, services)
+ => RoleType = role;
+
///
/// Gets the used for users.
///
@@ -60,32 +69,18 @@ namespace Microsoft.AspNetCore.Identity
///
/// Adds an for the .
///
- /// The user validator type.
+ /// The user validator type.
/// The current instance.
- public virtual IdentityBuilder AddUserValidator() where T : class
- {
- return AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(T));
- }
-
- ///
- /// Adds an for the .
- ///
- /// The role validator type.
- /// The current instance.
- public virtual IdentityBuilder AddRoleValidator() where T : class
- {
- return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(T));
- }
+ public virtual IdentityBuilder AddUserValidator() where TUser : class
+ => AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(TUser));
///
/// Adds an for the .
///
- /// The type of the claims principal factory.
+ /// The type of the claims principal factory.
/// The current instance.
- public virtual IdentityBuilder AddClaimsPrincipalFactory() where T : class
- {
- return AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(T));
- }
+ public virtual IdentityBuilder AddClaimsPrincipalFactory() where TUser : class
+ => AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TUser));
///
/// Adds an .
@@ -101,32 +96,18 @@ namespace Microsoft.AspNetCore.Identity
///
/// Adds an for the .
///
- /// The user type whose password will be validated.
+ /// The user type whose password will be validated.
/// The current instance.
- public virtual IdentityBuilder AddPasswordValidator() where T : class
- {
- return AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(T));
- }
+ public virtual IdentityBuilder AddPasswordValidator() where TUser : class
+ => AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TUser));
///
/// Adds an for the .
///
- /// The user type held in the store.
+ /// The user type held in the store.
/// The current instance.
- public virtual IdentityBuilder AddUserStore() where T : class
- {
- return AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(T));
- }
-
- ///
- /// Adds a for the .
- ///
- /// The role type held in the store.
- /// The current instance.
- public virtual IdentityBuilder AddRoleStore() where T : class
- {
- return AddScoped(typeof(IRoleStore<>).MakeGenericType(RoleType), typeof(T));
- }
+ public virtual IdentityBuilder AddUserStore() where TUser : class
+ => AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TUser));
///
/// Adds a token provider.
@@ -135,9 +116,7 @@ namespace Microsoft.AspNetCore.Identity
/// The name of the provider to add.
/// The current instance.
public virtual IdentityBuilder AddTokenProvider(string providerName) where TProvider : class
- {
- return AddTokenProvider(providerName, typeof(TProvider));
- }
+ => AddTokenProvider(providerName, typeof(TProvider));
///
/// Adds a token provider for the .
@@ -149,7 +128,7 @@ namespace Microsoft.AspNetCore.Identity
{
if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).GetTypeInfo().IsAssignableFrom(provider.GetTypeInfo()))
{
- throw new InvalidOperationException(AspNetIdentityResources.FormatInvalidManagerType(provider.Name, "IUserTokenProvider", UserType.Name));
+ throw new InvalidOperationException(Resources.FormatInvalidManagerType(provider.Name, "IUserTokenProvider", UserType.Name));
}
Services.Configure(options =>
{
@@ -159,23 +138,6 @@ namespace Microsoft.AspNetCore.Identity
return this;
}
- ///
- /// Adds the default token providers used to generate tokens for reset passwords, change email
- /// and change telephone number operations, and for two factor authentication token generation.
- ///
- /// The current instance.
- public virtual IdentityBuilder AddDefaultTokenProviders()
- {
- var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(UserType);
- var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(UserType);
- var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(UserType);
- var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(UserType);
- return AddTokenProvider(TokenOptions.DefaultProvider, dataProtectionProviderType)
- .AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType)
- .AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType)
- .AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType);
- }
-
///
/// Adds a for the .
///
@@ -185,15 +147,60 @@ namespace Microsoft.AspNetCore.Identity
{
var userManagerType = typeof(UserManager<>).MakeGenericType(UserType);
var customType = typeof(TUserManager);
- if (userManagerType == customType ||
- !userManagerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
+ if (!userManagerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
{
- throw new InvalidOperationException(AspNetIdentityResources.FormatInvalidManagerType(customType.Name, "UserManager", UserType.Name));
+ throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "UserManager", UserType.Name));
+ }
+ if (userManagerType != customType)
+ {
+ Services.AddScoped(customType, services => services.GetRequiredService(userManagerType));
}
- Services.AddScoped(customType, services => services.GetRequiredService(userManagerType));
return AddScoped(userManagerType, customType);
}
+ ///
+ /// Adds Role related services for TRole, including IRoleStore, IRoleValidator, and RoleManager.
+ ///
+ /// The role type.
+ /// The current instance.
+ public virtual IdentityBuilder AddRoles() where TRole : class
+ {
+ RoleType = typeof(TRole);
+ AddRoleStore();
+ AddRoleValidator>();
+ Services.TryAddScoped, RoleManager>();
+ return this;
+ }
+
+ ///
+ /// Adds an for the .
+ ///
+ /// The role validator type.
+ /// The current instance.
+ public virtual IdentityBuilder AddRoleValidator() where TRole : class
+ {
+ if (RoleType == null)
+ {
+ throw new InvalidOperationException(Resources.NoRoleType);
+ }
+ return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(TRole));
+ }
+
+
+ ///
+ /// Adds a for the .
+ ///
+ /// The role type held in the store.
+ /// The current instance.
+ public virtual IdentityBuilder AddRoleStore() where TRole : class
+ {
+ if (RoleType == null)
+ {
+ throw new InvalidOperationException(Resources.NoRoleType);
+ }
+ return AddScoped(typeof(IRoleStore<>).MakeGenericType(RoleType), typeof(TRole));
+ }
+
///
/// Adds a for the .
///
@@ -201,33 +208,21 @@ namespace Microsoft.AspNetCore.Identity
/// The current instance.
public virtual IdentityBuilder AddRoleManager() where TRoleManager : class
{
+ if (RoleType == null)
+ {
+ throw new InvalidOperationException(Resources.NoRoleType);
+ }
var managerType = typeof(RoleManager<>).MakeGenericType(RoleType);
var customType = typeof(TRoleManager);
- if (managerType == customType ||
- !managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
+ if (!managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
{
- throw new InvalidOperationException(AspNetIdentityResources.FormatInvalidManagerType(customType.Name, "RoleManager", RoleType.Name));
+ throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "RoleManager", RoleType.Name));
+ }
+ if (managerType != customType)
+ {
+ Services.AddScoped(typeof(TRoleManager), services => services.GetRequiredService(managerType));
}
- Services.AddScoped(typeof(TRoleManager), services => services.GetRequiredService(managerType));
return AddScoped(managerType, typeof(TRoleManager));
}
-
- ///
- /// Adds a for the .
- ///
- /// The type of the sign in manager to add.
- /// The current instance.
- public virtual IdentityBuilder AddSignInManager() where TSignInManager : class
- {
- var managerType = typeof(SignInManager<>).MakeGenericType(UserType);
- var customType = typeof(TSignInManager);
- if (managerType == customType ||
- !managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo()))
- {
- throw new InvalidOperationException(AspNetIdentityResources.FormatInvalidManagerType(customType.Name, "SignInManager", UserType.Name));
- }
- Services.AddScoped(typeof(TSignInManager), services => services.GetRequiredService(managerType));
- return AddScoped(managerType, typeof(TSignInManager));
- }
}
}
diff --git a/src/Microsoft.Extensions.Identity.Core/IdentityErrorDescriber.cs b/src/Microsoft.Extensions.Identity.Core/IdentityErrorDescriber.cs
index dd6ca26b6d..fdedb4ed0a 100644
--- a/src/Microsoft.Extensions.Identity.Core/IdentityErrorDescriber.cs
+++ b/src/Microsoft.Extensions.Identity.Core/IdentityErrorDescriber.cs
@@ -1,6 +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 Microsoft.Extensions.Identity.Core;
+
namespace Microsoft.AspNetCore.Identity
{
///
diff --git a/src/Microsoft.Extensions.Identity.Core/IdentityServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Identity.Core/IdentityServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..7f9b941875
--- /dev/null
+++ b/src/Microsoft.Extensions.Identity.Core/IdentityServiceCollectionExtensions.cs
@@ -0,0 +1,47 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ ///
+ /// Contains extension methods to for configuring identity services.
+ ///
+ public static class IdentityServiceCollectionExtensions
+ {
+ ///
+ /// Adds and configures the identity system for the specified User and Role types.
+ ///
+ /// The type representing a User in the system.
+ /// The services available in the application.
+ /// An action to configure the .
+ /// An for creating and configuring the identity system.
+ public static IdentityBuilder AddIdentityCore(this IServiceCollection services, Action setupAction)
+ where TUser : class
+ {
+ // Services identity depends on
+ services.AddOptions().AddLogging();
+
+ // Services used by identity
+ services.TryAddScoped, UserValidator>();
+ services.TryAddScoped, PasswordValidator>();
+ services.TryAddScoped, PasswordHasher>();
+ services.TryAddScoped();
+ // No interface for the error describer so we can add errors without rev'ing the interface
+ services.TryAddScoped();
+ services.TryAddScoped, UserClaimsPrincipalFactory>();
+ services.TryAddScoped, UserManager>();
+
+ if (setupAction != null)
+ {
+ services.Configure(setupAction);
+ }
+
+ return new IdentityBuilder(typeof(TUser), services);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Extensions.Identity.Core/Microsoft.Extensions.Identity.Core.csproj b/src/Microsoft.Extensions.Identity.Core/Microsoft.Extensions.Identity.Core.csproj
index b910f1e7b2..bf3e00d25b 100644
--- a/src/Microsoft.Extensions.Identity.Core/Microsoft.Extensions.Identity.Core.csproj
+++ b/src/Microsoft.Extensions.Identity.Core/Microsoft.Extensions.Identity.Core.csproj
@@ -11,6 +11,7 @@
+
diff --git a/src/Microsoft.AspNetCore.Identity/PasswordHasher.cs b/src/Microsoft.Extensions.Identity.Core/PasswordHasher.cs
similarity index 98%
rename from src/Microsoft.AspNetCore.Identity/PasswordHasher.cs
rename to src/Microsoft.Extensions.Identity.Core/PasswordHasher.cs
index 0f861aa94e..ebc0ded3dc 100644
--- a/src/Microsoft.AspNetCore.Identity/PasswordHasher.cs
+++ b/src/Microsoft.Extensions.Identity.Core/PasswordHasher.cs
@@ -5,6 +5,7 @@ using System;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
+using Microsoft.Extensions.Identity.Core;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Identity
@@ -53,12 +54,12 @@ namespace Microsoft.AspNetCore.Identity
_iterCount = options.IterationCount;
if (_iterCount < 1)
{
- throw new InvalidOperationException(AspNetIdentityResources.InvalidPasswordHasherIterationCount);
+ throw new InvalidOperationException(Resources.InvalidPasswordHasherIterationCount);
}
break;
default:
- throw new InvalidOperationException(AspNetIdentityResources.InvalidPasswordHasherCompatibilityMode);
+ throw new InvalidOperationException(Resources.InvalidPasswordHasherCompatibilityMode);
}
_rng = options.Rng;
diff --git a/src/Microsoft.AspNetCore.Identity/PasswordHasherOptions.cs b/src/Microsoft.Extensions.Identity.Core/PasswordHasherOptions.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/PasswordHasherOptions.cs
rename to src/Microsoft.Extensions.Identity.Core/PasswordHasherOptions.cs
diff --git a/src/Microsoft.AspNetCore.Identity/PasswordValidator.cs b/src/Microsoft.Extensions.Identity.Core/PasswordValidator.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/PasswordValidator.cs
rename to src/Microsoft.Extensions.Identity.Core/PasswordValidator.cs
diff --git a/src/Microsoft.AspNetCore.Identity/PhoneNumberTokenProvider.cs b/src/Microsoft.Extensions.Identity.Core/PhoneNumberTokenProvider.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/PhoneNumberTokenProvider.cs
rename to src/Microsoft.Extensions.Identity.Core/PhoneNumberTokenProvider.cs
diff --git a/src/Microsoft.Extensions.Identity.Core/Properties/Resources.Designer.cs b/src/Microsoft.Extensions.Identity.Core/Properties/Resources.Designer.cs
index d39c98abad..5cef01d32d 100644
--- a/src/Microsoft.Extensions.Identity.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.Extensions.Identity.Core/Properties/Resources.Designer.cs
@@ -1,5 +1,5 @@
//
-namespace Microsoft.AspNetCore.Identity
+namespace Microsoft.Extensions.Identity.Core
{
using System.Globalization;
using System.Reflection;
@@ -15,753 +15,673 @@ namespace Microsoft.AspNetCore.Identity
///
internal static string ConcurrencyFailure
{
- get { return GetString("ConcurrencyFailure"); }
+ get => GetString("ConcurrencyFailure");
}
///
/// Optimistic concurrency failure, object has been modified.
///
internal static string FormatConcurrencyFailure()
- {
- return GetString("ConcurrencyFailure");
- }
+ => GetString("ConcurrencyFailure");
///
/// An unknown failure has occurred.
///
internal static string DefaultError
{
- get { return GetString("DefaultError"); }
+ get => GetString("DefaultError");
}
///
/// An unknown failure has occurred.
///
internal static string FormatDefaultError()
- {
- return GetString("DefaultError");
- }
+ => GetString("DefaultError");
///
/// Email '{0}' is already taken.
///
internal static string DuplicateEmail
{
- get { return GetString("DuplicateEmail"); }
+ get => GetString("DuplicateEmail");
}
///
/// Email '{0}' is already taken.
///
internal static string FormatDuplicateEmail(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateEmail"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateEmail"), p0);
///
/// Role name '{0}' is already taken.
///
internal static string DuplicateRoleName
{
- get { return GetString("DuplicateRoleName"); }
+ get => GetString("DuplicateRoleName");
}
///
/// Role name '{0}' is already taken.
///
internal static string FormatDuplicateRoleName(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateRoleName"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateRoleName"), p0);
///
/// User name '{0}' is already taken.
///
internal static string DuplicateUserName
{
- get { return GetString("DuplicateUserName"); }
+ get => GetString("DuplicateUserName");
}
///
/// User name '{0}' is already taken.
///
internal static string FormatDuplicateUserName(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateUserName"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateUserName"), p0);
///
/// Email '{0}' is invalid.
///
internal static string InvalidEmail
{
- get { return GetString("InvalidEmail"); }
+ get => GetString("InvalidEmail");
}
///
/// Email '{0}' is invalid.
///
internal static string FormatInvalidEmail(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("InvalidEmail"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidEmail"), p0);
///
/// Type {0} must derive from {1}<{2}>.
///
internal static string InvalidManagerType
{
- get { return GetString("InvalidManagerType"); }
+ get => GetString("InvalidManagerType");
}
///
/// Type {0} must derive from {1}<{2}>.
///
internal static string FormatInvalidManagerType(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("InvalidManagerType"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidManagerType"), p0, p1, p2);
///
/// The provided PasswordHasherCompatibilityMode is invalid.
///
internal static string InvalidPasswordHasherCompatibilityMode
{
- get { return GetString("InvalidPasswordHasherCompatibilityMode"); }
+ get => GetString("InvalidPasswordHasherCompatibilityMode");
}
///
/// The provided PasswordHasherCompatibilityMode is invalid.
///
internal static string FormatInvalidPasswordHasherCompatibilityMode()
- {
- return GetString("InvalidPasswordHasherCompatibilityMode");
- }
+ => GetString("InvalidPasswordHasherCompatibilityMode");
///
/// The iteration count must be a positive integer.
///
internal static string InvalidPasswordHasherIterationCount
{
- get { return GetString("InvalidPasswordHasherIterationCount"); }
+ get => GetString("InvalidPasswordHasherIterationCount");
}
///
/// The iteration count must be a positive integer.
///
internal static string FormatInvalidPasswordHasherIterationCount()
- {
- return GetString("InvalidPasswordHasherIterationCount");
- }
+ => GetString("InvalidPasswordHasherIterationCount");
///
/// Role name '{0}' is invalid.
///
internal static string InvalidRoleName
{
- get { return GetString("InvalidRoleName"); }
+ get => GetString("InvalidRoleName");
}
///
/// Role name '{0}' is invalid.
///
internal static string FormatInvalidRoleName(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("InvalidRoleName"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidRoleName"), p0);
///
/// Invalid token.
///
internal static string InvalidToken
{
- get { return GetString("InvalidToken"); }
+ get => GetString("InvalidToken");
}
///
/// Invalid token.
///
internal static string FormatInvalidToken()
- {
- return GetString("InvalidToken");
- }
+ => GetString("InvalidToken");
///
/// User name '{0}' is invalid, can only contain letters or digits.
///
internal static string InvalidUserName
{
- get { return GetString("InvalidUserName"); }
+ get => GetString("InvalidUserName");
}
///
/// User name '{0}' is invalid, can only contain letters or digits.
///
internal static string FormatInvalidUserName(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("InvalidUserName"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidUserName"), p0);
///
/// A user with this login already exists.
///
internal static string LoginAlreadyAssociated
{
- get { return GetString("LoginAlreadyAssociated"); }
+ get => GetString("LoginAlreadyAssociated");
}
///
/// A user with this login already exists.
///
internal static string FormatLoginAlreadyAssociated()
- {
- return GetString("LoginAlreadyAssociated");
- }
+ => GetString("LoginAlreadyAssociated");
///
/// AddIdentity must be called on the service collection.
///
internal static string MustCallAddIdentity
{
- get { return GetString("MustCallAddIdentity"); }
+ get => GetString("MustCallAddIdentity");
}
///
/// AddIdentity must be called on the service collection.
///
internal static string FormatMustCallAddIdentity()
- {
- return GetString("MustCallAddIdentity");
- }
+ => GetString("MustCallAddIdentity");
///
/// No IUserTokenProvider named '{0}' is registered.
///
internal static string NoTokenProvider
{
- get { return GetString("NoTokenProvider"); }
+ get => GetString("NoTokenProvider");
}
///
/// No IUserTokenProvider named '{0}' is registered.
///
internal static string FormatNoTokenProvider(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("NoTokenProvider"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("NoTokenProvider"), p0);
///
/// User security stamp cannot be null.
///
internal static string NullSecurityStamp
{
- get { return GetString("NullSecurityStamp"); }
+ get => GetString("NullSecurityStamp");
}
///
/// User security stamp cannot be null.
///
internal static string FormatNullSecurityStamp()
- {
- return GetString("NullSecurityStamp");
- }
+ => GetString("NullSecurityStamp");
///
/// Incorrect password.
///
internal static string PasswordMismatch
{
- get { return GetString("PasswordMismatch"); }
+ get => GetString("PasswordMismatch");
}
///
/// Incorrect password.
///
internal static string FormatPasswordMismatch()
- {
- return GetString("PasswordMismatch");
- }
+ => GetString("PasswordMismatch");
///
/// Passwords must have at least one digit ('0'-'9').
///
internal static string PasswordRequiresDigit
{
- get { return GetString("PasswordRequiresDigit"); }
+ get => GetString("PasswordRequiresDigit");
}
///
/// Passwords must have at least one digit ('0'-'9').
///
internal static string FormatPasswordRequiresDigit()
- {
- return GetString("PasswordRequiresDigit");
- }
+ => GetString("PasswordRequiresDigit");
///
/// Passwords must have at least one lowercase ('a'-'z').
///
internal static string PasswordRequiresLower
{
- get { return GetString("PasswordRequiresLower"); }
+ get => GetString("PasswordRequiresLower");
}
///
/// Passwords must have at least one lowercase ('a'-'z').
///
internal static string FormatPasswordRequiresLower()
- {
- return GetString("PasswordRequiresLower");
- }
+ => GetString("PasswordRequiresLower");
///
/// Passwords must have at least one non alphanumeric character.
///
internal static string PasswordRequiresNonAlphanumeric
{
- get { return GetString("PasswordRequiresNonAlphanumeric"); }
+ get => GetString("PasswordRequiresNonAlphanumeric");
}
///
/// Passwords must have at least one non alphanumeric character.
///
internal static string FormatPasswordRequiresNonAlphanumeric()
- {
- return GetString("PasswordRequiresNonAlphanumeric");
- }
+ => GetString("PasswordRequiresNonAlphanumeric");
///
/// Passwords must have at least one uppercase ('A'-'Z').
///
internal static string PasswordRequiresUpper
{
- get { return GetString("PasswordRequiresUpper"); }
+ get => GetString("PasswordRequiresUpper");
}
///
/// Passwords must have at least one uppercase ('A'-'Z').
///
internal static string FormatPasswordRequiresUpper()
- {
- return GetString("PasswordRequiresUpper");
- }
+ => GetString("PasswordRequiresUpper");
///
/// Passwords must be at least {0} characters.
///
internal static string PasswordTooShort
{
- get { return GetString("PasswordTooShort"); }
+ get => GetString("PasswordTooShort");
}
///
/// Passwords must be at least {0} characters.
///
internal static string FormatPasswordTooShort(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("PasswordTooShort"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("PasswordTooShort"), p0);
///
/// Role {0} does not exist.
///
internal static string RoleNotFound
{
- get { return GetString("RoleNotFound"); }
+ get => GetString("RoleNotFound");
}
///
/// Role {0} does not exist.
///
internal static string FormatRoleNotFound(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0);
///
/// Store does not implement IQueryableRoleStore<TRole>.
///
internal static string StoreNotIQueryableRoleStore
{
- get { return GetString("StoreNotIQueryableRoleStore"); }
+ get => GetString("StoreNotIQueryableRoleStore");
}
///
/// Store does not implement IQueryableRoleStore<TRole>.
///
internal static string FormatStoreNotIQueryableRoleStore()
- {
- return GetString("StoreNotIQueryableRoleStore");
- }
+ => GetString("StoreNotIQueryableRoleStore");
///
/// Store does not implement IQueryableUserStore<TUser>.
///
internal static string StoreNotIQueryableUserStore
{
- get { return GetString("StoreNotIQueryableUserStore"); }
+ get => GetString("StoreNotIQueryableUserStore");
}
///
/// Store does not implement IQueryableUserStore<TUser>.
///
internal static string FormatStoreNotIQueryableUserStore()
- {
- return GetString("StoreNotIQueryableUserStore");
- }
+ => GetString("StoreNotIQueryableUserStore");
///
/// Store does not implement IRoleClaimStore<TRole>.
///
internal static string StoreNotIRoleClaimStore
{
- get { return GetString("StoreNotIRoleClaimStore"); }
+ get => GetString("StoreNotIRoleClaimStore");
}
///
/// Store does not implement IRoleClaimStore<TRole>.
///
internal static string FormatStoreNotIRoleClaimStore()
- {
- return GetString("StoreNotIRoleClaimStore");
- }
+ => GetString("StoreNotIRoleClaimStore");
///
/// Store does not implement IUserAuthenticationTokenStore<User>.
///
internal static string StoreNotIUserAuthenticationTokenStore
{
- get { return GetString("StoreNotIUserAuthenticationTokenStore"); }
+ get => GetString("StoreNotIUserAuthenticationTokenStore");
}
///
/// Store does not implement IUserAuthenticationTokenStore<User>.
///
internal static string FormatStoreNotIUserAuthenticationTokenStore()
- {
- return GetString("StoreNotIUserAuthenticationTokenStore");
- }
+ => GetString("StoreNotIUserAuthenticationTokenStore");
///
/// Store does not implement IUserClaimStore<TUser>.
///
internal static string StoreNotIUserClaimStore
{
- get { return GetString("StoreNotIUserClaimStore"); }
+ get => GetString("StoreNotIUserClaimStore");
}
///
/// Store does not implement IUserClaimStore<TUser>.
///
internal static string FormatStoreNotIUserClaimStore()
- {
- return GetString("StoreNotIUserClaimStore");
- }
+ => GetString("StoreNotIUserClaimStore");
///
/// Store does not implement IUserConfirmationStore<TUser>.
///
internal static string StoreNotIUserConfirmationStore
{
- get { return GetString("StoreNotIUserConfirmationStore"); }
+ get => GetString("StoreNotIUserConfirmationStore");
}
///
/// Store does not implement IUserConfirmationStore<TUser>.
///
internal static string FormatStoreNotIUserConfirmationStore()
- {
- return GetString("StoreNotIUserConfirmationStore");
- }
+ => GetString("StoreNotIUserConfirmationStore");
///
/// Store does not implement IUserEmailStore<TUser>.
///
internal static string StoreNotIUserEmailStore
{
- get { return GetString("StoreNotIUserEmailStore"); }
+ get => GetString("StoreNotIUserEmailStore");
}
///
/// Store does not implement IUserEmailStore<TUser>.
///
internal static string FormatStoreNotIUserEmailStore()
- {
- return GetString("StoreNotIUserEmailStore");
- }
+ => GetString("StoreNotIUserEmailStore");
///
/// Store does not implement IUserLockoutStore<TUser>.
///
internal static string StoreNotIUserLockoutStore
{
- get { return GetString("StoreNotIUserLockoutStore"); }
+ get => GetString("StoreNotIUserLockoutStore");
}
///
/// Store does not implement IUserLockoutStore<TUser>.
///
internal static string FormatStoreNotIUserLockoutStore()
- {
- return GetString("StoreNotIUserLockoutStore");
- }
+ => GetString("StoreNotIUserLockoutStore");
///
/// Store does not implement IUserLoginStore<TUser>.
///
internal static string StoreNotIUserLoginStore
{
- get { return GetString("StoreNotIUserLoginStore"); }
+ get => GetString("StoreNotIUserLoginStore");
}
///
/// Store does not implement IUserLoginStore<TUser>.
///
internal static string FormatStoreNotIUserLoginStore()
- {
- return GetString("StoreNotIUserLoginStore");
- }
+ => GetString("StoreNotIUserLoginStore");
///
/// Store does not implement IUserPasswordStore<TUser>.
///
internal static string StoreNotIUserPasswordStore
{
- get { return GetString("StoreNotIUserPasswordStore"); }
+ get => GetString("StoreNotIUserPasswordStore");
}
///
/// Store does not implement IUserPasswordStore<TUser>.
///
internal static string FormatStoreNotIUserPasswordStore()
- {
- return GetString("StoreNotIUserPasswordStore");
- }
+ => GetString("StoreNotIUserPasswordStore");
///
/// Store does not implement IUserPhoneNumberStore<TUser>.
///
internal static string StoreNotIUserPhoneNumberStore
{
- get { return GetString("StoreNotIUserPhoneNumberStore"); }
+ get => GetString("StoreNotIUserPhoneNumberStore");
}
///
/// Store does not implement IUserPhoneNumberStore<TUser>.
///
internal static string FormatStoreNotIUserPhoneNumberStore()
- {
- return GetString("StoreNotIUserPhoneNumberStore");
- }
+ => GetString("StoreNotIUserPhoneNumberStore");
///
/// Store does not implement IUserRoleStore<TUser>.
///
internal static string StoreNotIUserRoleStore
{
- get { return GetString("StoreNotIUserRoleStore"); }
+ get => GetString("StoreNotIUserRoleStore");
}
///
/// Store does not implement IUserRoleStore<TUser>.
///
internal static string FormatStoreNotIUserRoleStore()
- {
- return GetString("StoreNotIUserRoleStore");
- }
+ => GetString("StoreNotIUserRoleStore");
///
/// Store does not implement IUserSecurityStampStore<TUser>.
///
internal static string StoreNotIUserSecurityStampStore
{
- get { return GetString("StoreNotIUserSecurityStampStore"); }
+ get => GetString("StoreNotIUserSecurityStampStore");
}
///
/// Store does not implement IUserSecurityStampStore<TUser>.
///
internal static string FormatStoreNotIUserSecurityStampStore()
- {
- return GetString("StoreNotIUserSecurityStampStore");
- }
+ => GetString("StoreNotIUserSecurityStampStore");
///
/// Store does not implement IUserAuthenticatorKeyStore<User>.
///
internal static string StoreNotIUserAuthenticatorKeyStore
{
- get { return GetString("StoreNotIUserAuthenticatorKeyStore"); }
+ get => GetString("StoreNotIUserAuthenticatorKeyStore");
}
///
/// Store does not implement IUserAuthenticatorKeyStore<User>.
///
internal static string FormatStoreNotIUserAuthenticatorKeyStore()
- {
- return GetString("StoreNotIUserAuthenticatorKeyStore");
- }
+ => GetString("StoreNotIUserAuthenticatorKeyStore");
///
/// Store does not implement IUserTwoFactorStore<TUser>.
///
internal static string StoreNotIUserTwoFactorStore
{
- get { return GetString("StoreNotIUserTwoFactorStore"); }
+ get => GetString("StoreNotIUserTwoFactorStore");
}
///
/// Store does not implement IUserTwoFactorStore<TUser>.
///
internal static string FormatStoreNotIUserTwoFactorStore()
- {
- return GetString("StoreNotIUserTwoFactorStore");
- }
+ => GetString("StoreNotIUserTwoFactorStore");
///
/// Recovery code redemption failed.
///
internal static string RecoveryCodeRedemptionFailed
{
- get { return GetString("RecoveryCodeRedemptionFailed"); }
+ get => GetString("RecoveryCodeRedemptionFailed");
}
///
/// Recovery code redemption failed.
///
internal static string FormatRecoveryCodeRedemptionFailed()
- {
- return GetString("RecoveryCodeRedemptionFailed");
- }
+ => GetString("RecoveryCodeRedemptionFailed");
///
/// User already has a password set.
///
internal static string UserAlreadyHasPassword
{
- get { return GetString("UserAlreadyHasPassword"); }
+ get => GetString("UserAlreadyHasPassword");
}
///
/// User already has a password set.
///
internal static string FormatUserAlreadyHasPassword()
- {
- return GetString("UserAlreadyHasPassword");
- }
+ => GetString("UserAlreadyHasPassword");
///
/// User already in role '{0}'.
///
internal static string UserAlreadyInRole
{
- get { return GetString("UserAlreadyInRole"); }
+ get => GetString("UserAlreadyInRole");
}
///
/// User already in role '{0}'.
///
internal static string FormatUserAlreadyInRole(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("UserAlreadyInRole"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("UserAlreadyInRole"), p0);
///
/// User is locked out.
///
internal static string UserLockedOut
{
- get { return GetString("UserLockedOut"); }
+ get => GetString("UserLockedOut");
}
///
/// User is locked out.
///
internal static string FormatUserLockedOut()
- {
- return GetString("UserLockedOut");
- }
+ => GetString("UserLockedOut");
///
/// Lockout is not enabled for this user.
///
internal static string UserLockoutNotEnabled
{
- get { return GetString("UserLockoutNotEnabled"); }
+ get => GetString("UserLockoutNotEnabled");
}
///
/// Lockout is not enabled for this user.
///
internal static string FormatUserLockoutNotEnabled()
- {
- return GetString("UserLockoutNotEnabled");
- }
+ => GetString("UserLockoutNotEnabled");
///
/// User {0} does not exist.
///
internal static string UserNameNotFound
{
- get { return GetString("UserNameNotFound"); }
+ get => GetString("UserNameNotFound");
}
///
/// User {0} does not exist.
///
internal static string FormatUserNameNotFound(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("UserNameNotFound"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("UserNameNotFound"), p0);
///
/// User is not in role '{0}'.
///
internal static string UserNotInRole
{
- get { return GetString("UserNotInRole"); }
+ get => GetString("UserNotInRole");
}
///
/// User is not in role '{0}'.
///
internal static string FormatUserNotInRole(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("UserNotInRole"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("UserNotInRole"), p0);
///
/// Store does not implement IUserTwoFactorRecoveryCodeStore<User>.
///
internal static string StoreNotIUserTwoFactorRecoveryCodeStore
{
- get { return GetString("StoreNotIUserTwoFactorRecoveryCodeStore"); }
+ get => GetString("StoreNotIUserTwoFactorRecoveryCodeStore");
}
///
/// Store does not implement IUserTwoFactorRecoveryCodeStore<User>.
///
internal static string FormatStoreNotIUserTwoFactorRecoveryCodeStore()
- {
- return GetString("StoreNotIUserTwoFactorRecoveryCodeStore");
- }
+ => GetString("StoreNotIUserTwoFactorRecoveryCodeStore");
///
/// Passwords must use at least {0} different characters.
///
internal static string PasswordRequiresUniqueChars
{
- get { return GetString("PasswordRequiresUniqueChars"); }
+ get => GetString("PasswordRequiresUniqueChars");
}
///
/// Passwords must use at least {0} different characters.
///
internal static string FormatPasswordRequiresUniqueChars(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("PasswordRequiresUniqueChars"), p0);
+
+ ///
+ /// No RoleType was specified, try AddRoles<TRole>().
+ ///
+ internal static string NoRoleType
{
- return string.Format(CultureInfo.CurrentCulture, GetString("PasswordRequiresUniqueChars"), p0);
+ get => GetString("NoRoleType");
}
+ ///
+ /// No RoleType was specified, try AddRoles<TRole>().
+ ///
+ internal static string FormatNoRoleType()
+ => GetString("NoRoleType");
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.Extensions.Identity.Core/Resources.resx b/src/Microsoft.Extensions.Identity.Core/Resources.resx
index 39a1443a4b..50d6501478 100644
--- a/src/Microsoft.Extensions.Identity.Core/Resources.resx
+++ b/src/Microsoft.Extensions.Identity.Core/Resources.resx
@@ -305,4 +305,8 @@
Passwords must use at least {0} different characters.
Error message for passwords that are based on similar characters
+
+ No RoleType was specified, try AddRoles<TRole>().
+ Error when the IdentityBuilder.RoleType was not specified
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Identity/Rfc6238AuthenticationService.cs b/src/Microsoft.Extensions.Identity.Core/Rfc6238AuthenticationService.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/Rfc6238AuthenticationService.cs
rename to src/Microsoft.Extensions.Identity.Core/Rfc6238AuthenticationService.cs
diff --git a/src/Microsoft.Extensions.Identity.Core/RoleManager.cs b/src/Microsoft.Extensions.Identity.Core/RoleManager.cs
index fdccba2441..22d0b1a7b0 100644
--- a/src/Microsoft.Extensions.Identity.Core/RoleManager.cs
+++ b/src/Microsoft.Extensions.Identity.Core/RoleManager.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Identity.Core;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Identity
diff --git a/src/Microsoft.AspNetCore.Identity/RoleValidator.cs b/src/Microsoft.Extensions.Identity.Core/RoleValidator.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/RoleValidator.cs
rename to src/Microsoft.Extensions.Identity.Core/RoleValidator.cs
diff --git a/src/Microsoft.AspNetCore.Identity/TotpSecurityStampBasedTokenProvider.cs b/src/Microsoft.Extensions.Identity.Core/TotpSecurityStampBasedTokenProvider.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/TotpSecurityStampBasedTokenProvider.cs
rename to src/Microsoft.Extensions.Identity.Core/TotpSecurityStampBasedTokenProvider.cs
diff --git a/src/Microsoft.AspNetCore.Identity/UpperInvariantLookupNormalizer.cs b/src/Microsoft.Extensions.Identity.Core/UpperInvariantLookupNormalizer.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/UpperInvariantLookupNormalizer.cs
rename to src/Microsoft.Extensions.Identity.Core/UpperInvariantLookupNormalizer.cs
diff --git a/src/Microsoft.AspNetCore.Identity/UserClaimsPrincipalFactory.cs b/src/Microsoft.Extensions.Identity.Core/UserClaimsPrincipalFactory.cs
similarity index 70%
rename from src/Microsoft.AspNetCore.Identity/UserClaimsPrincipalFactory.cs
rename to src/Microsoft.Extensions.Identity.Core/UserClaimsPrincipalFactory.cs
index 20062e6470..d7e93fbdc1 100644
--- a/src/Microsoft.AspNetCore.Identity/UserClaimsPrincipalFactory.cs
+++ b/src/Microsoft.Extensions.Identity.Core/UserClaimsPrincipalFactory.cs
@@ -13,36 +13,27 @@ namespace Microsoft.AspNetCore.Identity
/// Provides methods to create a claims principal for a given user.
///
/// The type used to represent a user.
- /// The type used to represent a role.
- public class UserClaimsPrincipalFactory : IUserClaimsPrincipalFactory
+ public class UserClaimsPrincipalFactory : IUserClaimsPrincipalFactory
where TUser : class
- where TRole : class
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The to retrieve user information from.
- /// The to retrieve a user's roles from.
/// The configured .
public UserClaimsPrincipalFactory(
- UserManager userManager,
- RoleManager roleManager,
+ UserManager userManager,
IOptions optionsAccessor)
{
if (userManager == null)
{
throw new ArgumentNullException(nameof(userManager));
}
- if (roleManager == null)
- {
- throw new ArgumentNullException(nameof(roleManager));
- }
if (optionsAccessor == null || optionsAccessor.Value == null)
{
throw new ArgumentNullException(nameof(optionsAccessor));
}
UserManager = userManager;
- RoleManager = roleManager;
Options = optionsAccessor.Value;
}
@@ -54,14 +45,6 @@ namespace Microsoft.AspNetCore.Identity
///
public UserManager UserManager { get; private set; }
- ///
- /// Gets the for this factory.
- ///
- ///
- /// The current for this factory instance.
- ///
- public RoleManager RoleManager { get; private set; }
-
///
/// Gets the for this factory.
///
@@ -81,18 +64,78 @@ namespace Microsoft.AspNetCore.Identity
{
throw new ArgumentNullException(nameof(user));
}
+ var id = await GenerateClaimsAsync(user);
+ return new ClaimsPrincipal(id);
+ }
+
+ ///
+ /// Generate the claims for a user.
+ ///
+ /// The user to create a from.
+ /// The that represents the asynchronous creation operation, containing the created .
+ protected virtual async Task GenerateClaimsAsync(TUser user)
+ {
var userId = await UserManager.GetUserIdAsync(user);
var userName = await UserManager.GetUserNameAsync(user);
- var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme,
+ var id = new ClaimsIdentity("Identity.Application", // REVIEW: Used to match Application scheme
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType);
id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName));
if (UserManager.SupportsUserSecurityStamp)
{
- id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
+ id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
await UserManager.GetSecurityStampAsync(user)));
}
+ if (UserManager.SupportsUserClaim)
+ {
+ id.AddClaims(await UserManager.GetClaimsAsync(user));
+ }
+ return id;
+ }
+ }
+
+ ///
+ /// Provides methods to create a claims principal for a given user.
+ ///
+ /// The type used to represent a user.
+ /// The type used to represent a role.
+ public class UserClaimsPrincipalFactory : UserClaimsPrincipalFactory
+ where TUser : class
+ where TRole : class
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The to retrieve user information from.
+ /// The to retrieve a user's roles from.
+ /// The configured .
+ public UserClaimsPrincipalFactory(UserManager userManager, RoleManager roleManager, IOptions options)
+ : base(userManager, options)
+ {
+ if (roleManager == null)
+ {
+ throw new ArgumentNullException(nameof(roleManager));
+ }
+ RoleManager = roleManager;
+ }
+
+ ///
+ /// Gets the for this factory.
+ ///
+ ///
+ /// The current for this factory instance.
+ ///
+ public RoleManager RoleManager { get; private set; }
+
+ ///
+ /// Generate the claims for a user.
+ ///
+ /// The user to create a from.
+ /// The that represents the asynchronous creation operation, containing the created .
+ protected override async Task GenerateClaimsAsync(TUser user)
+ {
+ var id = await base.GenerateClaimsAsync(user);
if (UserManager.SupportsUserRole)
{
var roles = await UserManager.GetRolesAsync(user);
@@ -109,21 +152,7 @@ namespace Microsoft.AspNetCore.Identity
}
}
}
- if (UserManager.SupportsUserClaim)
- {
- id.AddClaims(await UserManager.GetClaimsAsync(user));
- }
- return await CreatePrincipalAsync(id);
- }
-
- ///
- /// Creates a from a .
- ///
- /// The with claims.
- /// The that represents the asynchronous creation operation, containing the with the .
- protected virtual Task CreatePrincipalAsync(ClaimsIdentity id)
- {
- return Task.FromResult(new ClaimsPrincipal(id));
+ return id;
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Extensions.Identity.Core/UserManager.cs b/src/Microsoft.Extensions.Identity.Core/UserManager.cs
index 6ab877124b..c4aac9a0b8 100644
--- a/src/Microsoft.Extensions.Identity.Core/UserManager.cs
+++ b/src/Microsoft.Extensions.Identity.Core/UserManager.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Identity.Core;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
diff --git a/src/Microsoft.AspNetCore.Identity/UserValidator.cs b/src/Microsoft.Extensions.Identity.Core/UserValidator.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Identity/UserValidator.cs
rename to src/Microsoft.Extensions.Identity.Core/UserValidator.cs
diff --git a/src/Microsoft.Extensions.Identity.Stores/IdentityRole.cs b/src/Microsoft.Extensions.Identity.Stores/IdentityRole.cs
index a5cb111b12..e3cbef5a24 100644
--- a/src/Microsoft.Extensions.Identity.Stores/IdentityRole.cs
+++ b/src/Microsoft.Extensions.Identity.Stores/IdentityRole.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.Collections.Generic;
namespace Microsoft.AspNetCore.Identity
{
@@ -39,34 +38,7 @@ namespace Microsoft.AspNetCore.Identity
/// Represents a role in the identity system
///
/// The type used for the primary key for the role.
- public class IdentityRole : IdentityRole, IdentityRoleClaim>
- where TKey : IEquatable
- {
- ///
- /// Initializes a new instance of .
- ///
- public IdentityRole() { }
-
- ///
- /// Initializes a new instance of .
- ///
- /// The role name.
- public IdentityRole(string roleName) : this()
- {
- Name = roleName;
- }
- }
-
- ///
- /// Represents a role in the identity system
- ///
- /// The type used for the primary key for the role.
- /// The type used for user roles.
- /// The type used for role claims.
- public class IdentityRole
- where TKey : IEquatable
- where TUserRole : IdentityUserRole
- where TRoleClaim : IdentityRoleClaim
+ public class IdentityRole where TKey : IEquatable
{
///
/// Initializes a new instance of .
diff --git a/src/Microsoft.Extensions.Identity.Stores/IdentityUser.cs b/src/Microsoft.Extensions.Identity.Stores/IdentityUser.cs
index efc8a9b713..7143fadc18 100644
--- a/src/Microsoft.Extensions.Identity.Stores/IdentityUser.cs
+++ b/src/Microsoft.Extensions.Identity.Stores/IdentityUser.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.Collections.Generic;
namespace Microsoft.AspNetCore.Identity
{
@@ -39,19 +38,7 @@ namespace Microsoft.AspNetCore.Identity
/// Represents a user in the identity system
///
/// The type used for the primary key for the user.
- public class IdentityUser : IdentityUser, IdentityUserRole, IdentityUserLogin, IdentityUserToken>
- where TKey : IEquatable
- { }
-
- ///
- /// Represents a user in the identity system
- ///
- /// The type used for the primary key for the user.
- /// The type representing a claim.
- /// The type representing a user role.
- /// The type representing a user external login.
- /// The type representing a user external login.
- public class IdentityUser where TKey : IEquatable
+ public class IdentityUser where TKey : IEquatable
{
///
/// Initializes a new instance of .
diff --git a/src/Microsoft.Extensions.Identity.Stores/RoleStoreBase.cs b/src/Microsoft.Extensions.Identity.Stores/RoleStoreBase.cs
index b65b5f9937..01ca59bb6c 100644
--- a/src/Microsoft.Extensions.Identity.Stores/RoleStoreBase.cs
+++ b/src/Microsoft.Extensions.Identity.Stores/RoleStoreBase.cs
@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Identity
public abstract class RoleStoreBase :
IQueryableRoleStore,
IRoleClaimStore
- where TRole : IdentityRole
+ where TRole : IdentityRole
where TKey : IEquatable
where TUserRole : IdentityUserRole, new()
where TRoleClaim : IdentityRoleClaim, new()
@@ -219,10 +219,7 @@ namespace Microsoft.AspNetCore.Identity
///
/// Dispose the stores
///
- public void Dispose()
- {
- _disposed = true;
- }
+ public void Dispose() => _disposed = true;
///
/// Get the claims associated with the specified as an asynchronous operation.
@@ -265,8 +262,6 @@ namespace Microsoft.AspNetCore.Identity
/// The associated claim.
/// The role claim entity.
protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim)
- {
- return new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
- }
+ => new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
}
}
diff --git a/src/Microsoft.Extensions.Identity.Stores/UserStoreBase.cs b/src/Microsoft.Extensions.Identity.Stores/UserStoreBase.cs
index 1a72f5bf87..749cbaf8e6 100644
--- a/src/Microsoft.Extensions.Identity.Stores/UserStoreBase.cs
+++ b/src/Microsoft.Extensions.Identity.Stores/UserStoreBase.cs
@@ -8,24 +8,19 @@ using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Identity
{
///
- /// Represents a new instance of a persistence store for the specified user and role types.
+ /// Represents a new instance of a persistence store for the specified user type.
///
/// The type representing a user.
- /// The type representing a role.
- /// The type of the primary key for a role.
+ /// The type of the primary key for a user.
/// The type representing a claim.
- /// The type representing a user role.
/// The type representing a user external login.
/// The type representing a user token.
- /// The type representing a role claim.
- public abstract class UserStoreBase :
+ public abstract class UserStoreBase :
IUserLoginStore,
- IUserRoleStore,
IUserClaimStore,
IUserPasswordStore,
IUserSecurityStampStore,
@@ -37,14 +32,11 @@ namespace Microsoft.AspNetCore.Identity
IUserAuthenticationTokenStore,
IUserAuthenticatorKeyStore,
IUserTwoFactorRecoveryCodeStore
- where TUser : IdentityUser
- where TRole : IdentityRole
+ where TUser : IdentityUser
where TKey : IEquatable
where TUserClaim : IdentityUserClaim, new()
- where TUserRole : IdentityUserRole, new()
where TUserLogin : IdentityUserLogin, new()
where TUserToken : IdentityUserToken, new()
- where TRoleClaim : IdentityRoleClaim, new()
{
///
/// Creates a new instance.
@@ -67,21 +59,6 @@ namespace Microsoft.AspNetCore.Identity
///
public IdentityErrorDescriber ErrorDescriber { get; set; }
- ///
- /// Called to create a new instance of a .
- ///
- /// The associated user.
- /// The associated role.
- ///
- protected virtual TUserRole CreateUserRole(TUser user, TRole role)
- {
- return new TUserRole()
- {
- UserId = user.Id,
- RoleId = role.Id
- };
- }
-
///
/// Called to create a new instance of a .
///
@@ -349,23 +326,6 @@ namespace Microsoft.AspNetCore.Identity
return Task.FromResult(user.PasswordHash != null);
}
- ///
- /// Return a role with the normalized name if it exists.
- ///
- /// The normalized role name.
- /// The used to propagate notifications that the operation should be canceled.
- /// The role if it exists.
- protected abstract Task FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken);
-
- ///
- /// Return a user role for the userId and roleId if it exists.
- ///
- /// The user's id.
- /// The role's id.
- /// The used to propagate notifications that the operation should be canceled.
- /// The user role if it exists.
- protected abstract Task FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken);
-
///
/// Return a user with the matching userId if it exists.
///
@@ -393,42 +353,6 @@ namespace Microsoft.AspNetCore.Identity
/// The user login if it exists.
protected abstract Task FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken);
- ///
- /// Adds the given to the specified .
- ///
- /// The user to add the role to.
- /// The role to add.
- /// The used to propagate notifications that the operation should be canceled.
- /// The that represents the asynchronous operation.
- public abstract Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
-
- ///
- /// Removes the given from the specified .
- ///
- /// The user to remove the role from.
- /// The role to remove.
- /// The used to propagate notifications that the operation should be canceled.
- /// The that represents the asynchronous operation.
- public abstract Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
-
- ///
- /// Retrieves the roles the specified is a member of.
- ///
- /// The user whose roles should be retrieved.
- /// The used to propagate notifications that the operation should be canceled.
- /// A that contains the roles the user is a member of.
- public abstract Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
-
- ///
- /// Returns a flag indicating if the specified user is a member of the give .
- ///
- /// The user whose role membership should be checked.
- /// The role to check membership of
- /// The used to propagate notifications that the operation should be canceled.
- /// A containing a flag indicating if the specified user is a member of the given group. If the
- /// user is a member of the group the returned value with be true, otherwise it will be false.
- public abstract Task IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
-
///
/// Throws if this class has been disposed.
///
@@ -957,16 +881,6 @@ namespace Microsoft.AspNetCore.Identity
///
public abstract Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken));
- ///
- /// Retrieves all users in the specified role.
- ///
- /// The role whose users should be retrieved.
- /// The used to propagate notifications that the operation should be canceled.
- ///
- /// The contains a list of users, if any, that are in the specified role.
- ///
- public abstract Task> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
-
///
/// Find a user token if it exists.
///
@@ -1139,4 +1053,113 @@ namespace Microsoft.AspNetCore.Identity
return false;
}
}
+
+ ///
+ /// Represents a new instance of a persistence store for the specified user and role types.
+ ///
+ /// The type representing a user.
+ /// The type representing a role.
+ /// The type of the primary key for a role.
+ /// The type representing a claim.
+ /// The type representing a user role.
+ /// The type representing a user external login.
+ /// The type representing a user token.
+ /// The type representing a role claim.
+ public abstract class UserStoreBase :
+ UserStoreBase,
+ IUserRoleStore
+ where TUser : IdentityUser
+ where TRole : IdentityRole
+ where TKey : IEquatable
+ where TUserClaim : IdentityUserClaim, new()
+ where TUserRole : IdentityUserRole, new()
+ where TUserLogin : IdentityUserLogin, new()
+ where TUserToken : IdentityUserToken, new()
+ where TRoleClaim : IdentityRoleClaim, new()
+ {
+ ///
+ /// Creates a new instance.
+ ///
+ /// The used to describe store errors.
+ public UserStoreBase(IdentityErrorDescriber describer) : base(describer) { }
+
+ ///
+ /// Called to create a new instance of a .
+ ///
+ /// The associated user.
+ /// The associated role.
+ ///
+ protected virtual TUserRole CreateUserRole(TUser user, TRole role)
+ {
+ return new TUserRole()
+ {
+ UserId = user.Id,
+ RoleId = role.Id
+ };
+ }
+
+
+ ///
+ /// Retrieves all users in the specified role.
+ ///
+ /// The role whose users should be retrieved.
+ /// The used to propagate notifications that the operation should be canceled.
+ ///
+ /// The contains a list of users, if any, that are in the specified role.
+ ///
+ public abstract Task> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
+
+ ///
+ /// Adds the given to the specified .
+ ///
+ /// The user to add the role to.
+ /// The role to add.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ public abstract Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
+
+ ///
+ /// Removes the given from the specified .
+ ///
+ /// The user to remove the role from.
+ /// The role to remove.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The that represents the asynchronous operation.
+ public abstract Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
+
+ ///
+ /// Retrieves the roles the specified is a member of.
+ ///
+ /// The user whose roles should be retrieved.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// A that contains the roles the user is a member of.
+ public abstract Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
+
+ ///
+ /// Returns a flag indicating if the specified user is a member of the give .
+ ///
+ /// The user whose role membership should be checked.
+ /// The role to check membership of
+ /// The used to propagate notifications that the operation should be canceled.
+ /// A containing a flag indicating if the specified user is a member of the given group. If the
+ /// user is a member of the group the returned value with be true, otherwise it will be false.
+ public abstract Task IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
+
+ ///
+ /// Return a role with the normalized name if it exists.
+ ///
+ /// The normalized role name.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The role if it exists.
+ protected abstract Task FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken);
+
+ ///
+ /// Return a user role for the userId and roleId if it exists.
+ ///
+ /// The user's id.
+ /// The role's id.
+ /// The used to propagate notifications that the operation should be canceled.
+ /// The user role if it exists.
+ protected abstract Task FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken);
+ }
}
diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs
index e1d2e349a0..d1191cfbbf 100644
--- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs
+++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs
@@ -14,11 +14,16 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
}
public class InMemoryContext :
- InMemoryContext
+ IdentityDbContext
where TUser : IdentityUser
{
public InMemoryContext(DbContextOptions options) : base(options)
{ }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ optionsBuilder.UseInMemoryDatabase("ScratchUsers");
+ }
}
public class InMemoryContext : IdentityDbContext
@@ -36,8 +41,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
}
public abstract class InMemoryContext : IdentityDbContext