Support for ApiAuth using Identity Server
This commit is contained in:
parent
da9318f431
commit
5e10eb1d1a
110
Identity.sln
110
Identity.sln
|
|
@ -45,8 +45,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
Directory.Build.targets = Directory.Build.targets
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeWPFClient", "samples\NativeWPFClient\NativeWPFClient.csproj", "{39AA4E4D-5E62-4213-8641-BF8012D45DE4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentitySample.DefaultUI", "samples\IdentitySample.DefaultUI\IdentitySample.DefaultUI.csproj", "{ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.UI", "src\UI\Microsoft.AspNetCore.Identity.UI.csproj", "{894E102D-56D4-4B02-8F13-8781F4324C3E}"
|
||||
|
|
@ -58,6 +56,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identi
|
|||
{894E102D-56D4-4B02-8F13-8781F4324C3E} = {894E102D-56D4-4B02-8F13-8781F4324C3E}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiAuthSample", "samples\ApiAuthSample\ApiAuthSample.csproj", "{7FA90737-4A2D-4BBB-8245-F6564D462FCB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ApiAuthorization.IdentityServer", "src\ApiAuth.IS\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj", "{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Test", "test\ApiAuth.IS.Test\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Test.csproj", "{ECFE11DD-1111-4557-8E28-42F8E9878823}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -338,28 +342,6 @@ Global
|
|||
{FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU
|
||||
{FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU
|
||||
{FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU
|
||||
{ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -426,8 +408,8 @@ Global
|
|||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|x86.Build.0 = Release|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -456,6 +438,78 @@ Global
|
|||
{B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.DebugNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.DebugNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.ReleaseNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.ReleaseNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.ReleaseNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB}.ReleaseNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.DebugNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.DebugNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.ReleaseNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.ReleaseNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.ReleaseNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4}.ReleaseNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.DebugNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.DebugNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Release|x64.Build.0 = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.Release|x86.Build.0 = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.ReleaseNoWPF|x64.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.ReleaseNoWPF|x64.Build.0 = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.ReleaseNoWPF|x86.ActiveCfg = Debug|Any CPU
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823}.ReleaseNoWPF|x86.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -473,11 +527,13 @@ Global
|
|||
{D5905D78-A32E-44B8-8F21-EDAEDC95D9B8} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
||||
{FADA11FC-DC06-4832-A569-7B2374A6CD42} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
||||
{1F83D453-E094-4D28-BCFA-9E537ABB5AD6} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||
{39AA4E4D-5E62-4213-8641-BF8012D45DE4} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}
|
||||
{ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}
|
||||
{894E102D-56D4-4B02-8F13-8781F4324C3E} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
||||
{CAE02AD2-F941-4ACB-B469-13EFF551BB74} = {1F83D453-E094-4D28-BCFA-9E537ABB5AD6}
|
||||
{B3616029-7DA6-4FB3-8722-D5AC69884B3F} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||
{7FA90737-4A2D-4BBB-8245-F6564D462FCB} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}
|
||||
{590C70E2-FCCC-49C2-93F3-60B7AA0533A4} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
||||
{ECFE11DD-1111-4557-8E28-42F8E9878823} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B3F2A592-CCE0-40C2-8CA4-7B1293DED874}
|
||||
|
|
|
|||
319
IdentityCore.sln
319
IdentityCore.sln
|
|
@ -1,319 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.10
|
||||
MinimumVisualStudioVersion = 15.0.26730.03
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\Directory.Build.props = src\Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{52D59F18-62D2-4D17-8CF2-BE192445AF8E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
test\Directory.Build.props = test\Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity", "src\Identity\Microsoft.AspNetCore.Identity.csproj", "{1729302E-A58E-4652-B639-5B6B68DA2748}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Test", "test\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\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\EF.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\EF.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\EF\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj", "{4490894C-3572-4E63-86F1-EE5105CE8A06}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.Identity.AspNetCoreCompat", "src\AspNetCoreCompat\Microsoft.AspNet.Identity.AspNetCoreCompat.csproj", "{6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Specification.Tests", "src\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\Core\Microsoft.Extensions.Identity.Core.csproj", "{D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Identity.Stores", "src\Stores\Microsoft.Extensions.Identity.Stores.csproj", "{FADA11FC-DC06-4832-A569-7B2374A6CD42}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2C657F6C-D8AD-4833-9C59-2301A16957BD}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentitySample.DefaultUI", "samples\IdentitySample.DefaultUI\IdentitySample.DefaultUI.csproj", "{D5FB2E24-4C71-430C-A289-59C8D59164B0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.UI", "src\UI\Microsoft.AspNetCore.Identity.UI.csproj", "{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.FunctionalTests", "test\Identity.FunctionalTests\Microsoft.AspNetCore.Identity.FunctionalTests.csproj", "{BAC36757-9A47-43CB-A6F3-86E8C4650A28}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{C47C1165-9F19-4DF8-ABA9-707ACEB3BDC7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.DefaultUI.WebSite", "test\WebSites\Identity.DefaultUI.WebSite\Identity.DefaultUI.WebSite.csproj", "{EA424B4D-0BE1-49AC-A106-CC6CC808A104}"
|
||||
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
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x64.Build.0 = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.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}
|
||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}
|
||||
{1FB3E9BB-E20A-4807-A4C3-F86A341304DB} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
||||
{BAC36757-9A47-43CB-A6F3-86E8C4650A28} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||
{C47C1165-9F19-4DF8-ABA9-707ACEB3BDC7} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104} = {C47C1165-9F19-4DF8-ABA9-707ACEB3BDC7}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {21D598B0-2383-4B22-826D-E7FB4921BD66}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
@ -5,11 +5,15 @@
|
|||
<PropertyGroup Label="Package Versions">
|
||||
<AngleSharpPackageVersion>0.9.9</AngleSharpPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview2-20181004.6</InternalAspNetCoreSdkPackageVersion>
|
||||
<IdentityServer4PackageVersion>2.3.0-preview1-update2</IdentityServer4PackageVersion>
|
||||
<IdentityServer4AspNetIdentityPackageVersion>2.3.0-preview1-update2</IdentityServer4AspNetIdentityPackageVersion>
|
||||
<IdentityServer4EntityFrameworkPackageVersion>2.3.0-preview1-update1</IdentityServer4EntityFrameworkPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationFacebookPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreAuthenticationFacebookPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationGooglePackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreAuthenticationGooglePackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationTwitterPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreAuthenticationTwitterPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthorizationPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreAuthorizationPackageVersion>
|
||||
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreCookiePolicyPackageVersion>
|
||||
<MicrosoftAspNetCoreCryptographyKeyDerivationPackageVersion>2.2.0-preview3-35425</MicrosoftAspNetCoreCryptographyKeyDerivationPackageVersion>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
<Project>
|
||||
<Import Project="dependencies.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ExcludeSolutions Include="$(RepositoryRoot)IdentityCore.sln" />
|
||||
<ExcludeSolutions Include="$(RepositoryRoot)Identity.Samples.sln" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- These properties are use by the automation that updates dependencies.props -->
|
||||
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.2;net461</TargetFrameworks>
|
||||
<UserSecretsId>aspnet-ApiAuthSample-12ED8ECC-9EF1-4D31-87B4-1405B3198E5E</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.CookiePolicy" Version="$(MicrosoftAspNetCoreCookiePolicyPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.HttpsPolicy" Version="$(MicrosoftAspNetCoreHttpsPolicyPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Extensions" Version="$(MicrosoftAspNetCoreDataProtectionExtensionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(MicrosoftAspNetCoreDiagnosticsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="$(MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(MicrosoftAspNetCoreMvcPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Rewrite" Version="$(MicrosoftAspNetCoreRewritePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(MicrosoftAspNetCoreStaticFilesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="$(MicrosoftAspNetCoreServerKestrelHttpsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(MicrosoftEntityFrameworkCoreSqlServerPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(MicrosoftEntityFrameworkCoreSqlitePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" PrivateAssets="All" Version="$(MicrosoftEntityFrameworkCoreToolsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftExtensionsLoggingDebugPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Sdk.Razor" Version="$(MicrosoftNETSdkRazorPackageVersion)" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\ApiAuth.IS\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj" />
|
||||
<ProjectReference Include="..\..\src\EF\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj" />
|
||||
<ProjectReference Include="..\..\src\UI\Microsoft.AspNetCore.Identity.UI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
@using Microsoft.AspNetCore.Identity
|
||||
@using ApiAuthSample.Models;
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
|
||||
</li>
|
||||
<li>
|
||||
<button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a asp-area="Identity" asp-page="/Account/Register">Register</a></li>
|
||||
<li><a asp-area="Identity" asp-page="/Account/Login">Login</a></li>
|
||||
</ul>
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@using Microsoft.AspNetCore.Identity
|
||||
@using ApiAuthSample.Areas.Identity
|
||||
@using ApiAuthSample.Models
|
||||
@namespace ApiAuthSample.Areas.Identity.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ApiAuthSample.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class ValuesController : ControllerBase
|
||||
{
|
||||
// GET api/values
|
||||
[HttpGet]
|
||||
public ActionResult<IEnumerable<string>> Get()
|
||||
{
|
||||
return new string[] { "value1", "value2" };
|
||||
}
|
||||
|
||||
// GET api/values/5
|
||||
[HttpGet("{id}")]
|
||||
public ActionResult<string> Get(int id)
|
||||
{
|
||||
return "value";
|
||||
}
|
||||
|
||||
// POST api/values
|
||||
[HttpPost]
|
||||
public void Post([FromBody] string value)
|
||||
{
|
||||
}
|
||||
|
||||
// PUT api/values/5
|
||||
[HttpPut("{id}")]
|
||||
public void Put(int id, [FromBody] string value)
|
||||
{
|
||||
}
|
||||
|
||||
// DELETE api/values/5
|
||||
[HttpDelete("{id}")]
|
||||
public void Delete(int id)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using IdentityServer4.EntityFramework.Options;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ApiAuthSample.Models;
|
||||
|
||||
namespace ApiAuthSample.Data
|
||||
{
|
||||
public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
|
||||
{
|
||||
public ApplicationDbContext(
|
||||
DbContextOptions<ApplicationDbContext> options,
|
||||
IOptions<OperationalStoreOptions> operationalStoreOptions)
|
||||
: base(options, operationalStoreOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
||||
base.OnModelCreating(builder);
|
||||
// Customize the ASP.NET Identity model and override the defaults if needed.
|
||||
// For example, you can rename the ASP.NET Identity table names and more.
|
||||
// Add your customizations after calling base.OnModelCreating(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
260
samples/ApiAuthSample/Data/Migrations/20180919224505_InitialMigration.Designer.cs
generated
Normal file
260
samples/ApiAuthSample/Data/Migrations/20180919224505_InitialMigration.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using ApiAuthSample.Data;
|
||||
|
||||
namespace ApiAuthSample.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20180919224505_InitialMigration")]
|
||||
partial class InitialMigration
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ApiAuthSample.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace ApiAuthSample.Data.Migrations
|
||||
{
|
||||
public partial class InitialMigration : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||
PasswordHash = table.Column<string>(nullable: true),
|
||||
SecurityStamp = table.Column<string>(nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||
PhoneNumber = table.Column<string>(nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||
AccessFailedCount = table.Column<int>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PersistedGrants",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(nullable: false),
|
||||
Expiration = table.Column<DateTime>(nullable: true),
|
||||
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
RoleId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
ClaimType = table.Column<string>(nullable: true),
|
||||
ClaimValue = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||
UserId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
RoleId = table.Column<string>(nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(nullable: false),
|
||||
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Name = table.Column<string>(maxLength: 128, nullable: false),
|
||||
Value = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
|
||||
table: "PersistedGrants",
|
||||
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PersistedGrants");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using ApiAuthSample.Data;
|
||||
|
||||
namespace ApiAuthSample.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
|
||||
|
||||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<DateTime>("CreationTime");
|
||||
|
||||
b.Property<string>("Data")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50000);
|
||||
|
||||
b.Property<DateTime?>("Expiration");
|
||||
|
||||
b.Property<string>("SubjectId")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||
|
||||
b.ToTable("PersistedGrants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ApiAuthSample.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("ApiAuthSample.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace ApiAuthSample.Models
|
||||
{
|
||||
// Add profile data for application users by adding properties to the ApplicationUser class
|
||||
public class ApplicationUser : IdentityUser
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
@page
|
||||
@addTagHelper *, Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
|
||||
@model ApiAuthSample.Pages.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Index";
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>@ViewData["Title"]</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ApiAuthSample SPA client</h1>
|
||||
<button id="login">Login</button>
|
||||
<button id="logout" disabled>Logout</button>
|
||||
<button id="call-api" disabled>Call API</button>
|
||||
<div id="login-result"></div>
|
||||
<div id="api-result"></div>
|
||||
<script src="js/oidc-client.js"></script>
|
||||
<script id="apiauth" type="text/javascript" asp-apiauth-parameters="ApiAuthSampleSPA">
|
||||
let $data = document.querySelector("#apiauth");
|
||||
let configuration = {};
|
||||
for (let key in $data.dataset) {
|
||||
configuration[key] = $data.dataset[key];
|
||||
}
|
||||
|
||||
let mgr = new Oidc.UserManager(configuration);
|
||||
</script>
|
||||
<script src="js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace ApiAuthSample.Pages
|
||||
{
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ApiAuthSample
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.UseKestrel((builderContext, options) =>
|
||||
{
|
||||
options.Configure(builderContext.Configuration.GetSection("Kestrel"));
|
||||
})
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
|
||||
if (appAssembly != null)
|
||||
{
|
||||
config.AddUserSecrets(appAssembly, optional: true);
|
||||
}
|
||||
}
|
||||
|
||||
config.AddEnvironmentVariables();
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
config.AddCommandLine(args);
|
||||
}
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
{
|
||||
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
||||
logging.AddConsole();
|
||||
logging.AddDebug();
|
||||
})
|
||||
.UseIISIntegration()
|
||||
.UseDefaultServiceProvider((context, options) =>
|
||||
{
|
||||
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
|
||||
});
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
|
||||
}
|
||||
|
||||
builder.UseStartup<Startup>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:14440",
|
||||
"sslPort": 44316
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"ApiAuthSample": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ApiAuthSample.Data;
|
||||
using ApiAuthSample.Models;
|
||||
|
||||
namespace ApiAuthSample
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
services.AddDefaultIdentity<ApplicationUser>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
services.AddIdentityServer()
|
||||
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddIdentityServerJwt();
|
||||
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
app.UseIdentityServer();
|
||||
|
||||
app.UseMvc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Debug",
|
||||
"Microsoft": "Debug"
|
||||
}
|
||||
},
|
||||
"IdentityServer": {
|
||||
"Key": {
|
||||
"Type": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "DataSource=app.db"
|
||||
},
|
||||
"IdentityServer": {
|
||||
"Clients": {
|
||||
"ApiAuthSampleSPA": {
|
||||
"Profile": "IdentityServerSPA"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"babel-polyfill": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
|
||||
"integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0",
|
||||
"core-js": "2.5.7",
|
||||
"regenerator-runtime": "0.10.5"
|
||||
}
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"core-js": "2.5.7",
|
||||
"regenerator-runtime": "0.11.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
|
||||
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
|
||||
},
|
||||
"jsrsasign": {
|
||||
"version": "8.0.12",
|
||||
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz",
|
||||
"integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY="
|
||||
},
|
||||
"oidc-client": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/oidc-client/-/oidc-client-1.5.2.tgz",
|
||||
"integrity": "sha512-2w4TOraEf4PEbuo8mR8tSRhtgAHQaghBWgt2qpnwebsdW87BRMC7XKAytHcbZ1GYjFH9jJn30Cav64zbYdjiCQ==",
|
||||
"requires": {
|
||||
"babel-polyfill": "6.26.0",
|
||||
"jsrsasign": "8.0.12"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.10.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
|
||||
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
|
||||
function invokeLogin() {
|
||||
// Redirects to the Authorization Server for sign in.
|
||||
return mgr.signinRedirect();
|
||||
}
|
||||
|
||||
function invokeLogout() {
|
||||
// Redirects to the Authorization Server for sign out.
|
||||
return mgr.signoutRedirect();
|
||||
}
|
||||
|
||||
async function handleAuthorizationServerCallback() {
|
||||
try {
|
||||
let user = await mgr.signinRedirectCallback();
|
||||
updateUserUI(user);
|
||||
} catch (error) {
|
||||
updateUserUI(undefined, error);
|
||||
}
|
||||
}
|
||||
|
||||
async function callApi() {
|
||||
try {
|
||||
let user = await mgr.getUser();
|
||||
let response = await fetch(
|
||||
window.location.origin + '/api/values',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${user.access_token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
} else {
|
||||
let text = await response.text();
|
||||
return text;
|
||||
}
|
||||
} catch (e) {
|
||||
return e.message;
|
||||
}
|
||||
}
|
||||
|
||||
// Code to update the UI
|
||||
|
||||
if (window.location.hash) {
|
||||
handleAuthorizationServerCallback();
|
||||
window.location.hash = '';
|
||||
}
|
||||
|
||||
let ids = {
|
||||
login: 'login',
|
||||
logout: 'logout',
|
||||
callApi: 'call-api',
|
||||
loginResult: 'login-result',
|
||||
apiResults: 'api-result'
|
||||
};
|
||||
|
||||
document.onreadystatechange = function () {
|
||||
if (document.readyState === 'complete') {
|
||||
let login = document.getElementById(ids.login);
|
||||
let logout = document.getElementById(ids.logout);
|
||||
let callApi = document.getElementById(ids.callApi);
|
||||
|
||||
login.addEventListener('click', invokeLogin);
|
||||
logout.addEventListener('click', invokeLogout);
|
||||
callApi.addEventListener('click', invokeCallApi);
|
||||
}
|
||||
};
|
||||
|
||||
function updateUserUI(user, error) {
|
||||
let loginResults = document.getElementById(ids.loginResult);
|
||||
let heading = document.createElement('h2');
|
||||
heading.innerText = 'Login result';
|
||||
if (user) {
|
||||
loginResults.appendChild(heading);
|
||||
loginResults.insertAdjacentText('beforeend', `Hello ${user.profile.name}`);
|
||||
updateButtons(true, false, false);
|
||||
} else {
|
||||
loginResults.innerText = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
function updateButtons(login, callApi, logout) {
|
||||
let loginB = document.getElementById(ids.login);
|
||||
let logoutB = document.getElementById(ids.logout);
|
||||
let callApiB = document.getElementById(ids.callApi);
|
||||
|
||||
loginB.disabled = login;
|
||||
logoutB.disabled = logout;
|
||||
callApiB.disabled = callApi;
|
||||
}
|
||||
|
||||
async function invokeCallApi() {
|
||||
let result = await callApi();
|
||||
let results = document.getElementById(ids.apiResults);
|
||||
if (Array.isArray(result)) {
|
||||
let list = document.createElement('ul');
|
||||
let listElements = result.map(e => createListElement(e));
|
||||
for (let element of listElements) {
|
||||
list.appendChild(element);
|
||||
}
|
||||
let heading = document.createElement('h2');
|
||||
heading.innerText = 'API call results';
|
||||
results.appendChild(heading);
|
||||
results.appendChild(list);
|
||||
} else {
|
||||
results.innerText = result;
|
||||
}
|
||||
|
||||
function createListElement(element) {
|
||||
let node = document.createElement('li');
|
||||
node.innerText = element;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<Application x:Class="NativeWPFClient.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:NativeWPFClient"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace NativeWPFClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<Window x:Class="NativeWPFClient.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:NativeWPFClient"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Width="525" SizeToContent="Height">
|
||||
<StackPanel Margin="0,0,0,0" VerticalAlignment="Stretch" Orientation="Vertical">
|
||||
<Label Content="Base address" Margin="5,0" />
|
||||
<TextBox Name="BaseAddress" Margin="5,0" TextWrapping="Wrap" Text="{Binding BaseAddress}" />
|
||||
<Label Content="Redirect URI" Margin="5,0" />
|
||||
<TextBox Name="RedirectUri" Margin="5,0" TextWrapping="Wrap" Text="{Binding RedirectUri}" />
|
||||
<Label Content="Tenant" Margin="5,0" />
|
||||
<TextBox Name="Tenant" Margin="5,0" TextWrapping="Wrap" Text="{Binding Tenant}" />
|
||||
<Label Content="Policy" HorizontalAlignment="Left" Margin="5,0" />
|
||||
<TextBox Name="Policy" Margin="5,0" TextWrapping="Wrap" Text="{Binding Policy}" />
|
||||
<Label Content="Client ID" Margin="5,0" />
|
||||
<TextBox Name="ClientID" Margin="5,0" TextWrapping="Wrap" Text="{Binding ClientId}" />
|
||||
<Label Content="Scopes" Margin="5,0" />
|
||||
<TextBox Name="Scopes" Margin="5,0" TextWrapping="Wrap" Text="{Binding Scopes}" />
|
||||
<Button Name="Authorize" Margin="5,3" Click="Authorize_Click">Authorize</Button>
|
||||
<Label Margin="5,0" Name="Result" Content="{Binding Result, Mode=TwoWay}"></Label>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
using Microsoft.Identity.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace NativeWPFClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private Task _currentAuthorization;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
// Local client
|
||||
DataContext = new NativeWPFClientViewModel
|
||||
{
|
||||
BaseAddress = "https://localhost:44324/",
|
||||
RedirectUri = "urn:ietf:wg:oauth:2.0:oob",
|
||||
Tenant = "Identity",
|
||||
Policy = "signinsignup",
|
||||
ClientId = "06D7C2FB-A66A-41AD-9509-77BDDFAB111B",
|
||||
Scopes = "https://localhost/DFC7191F-FF74-42B9-A292-08FEA80F5B20/v2.0/ProtectedApi/read"
|
||||
};
|
||||
|
||||
// DataContext = new NativeWPFClientViewModel
|
||||
// {
|
||||
// BaseAddress = "https://login.microsoftonline.com/",
|
||||
// RedirectUri = "urn:ietf:wg:oauth:2.0:oob",
|
||||
// Tenant = "jacalvarb2c.onmicrosoft.com",
|
||||
// Policy = "B2C_1_signinsignup",
|
||||
// ClientId = "42291769-0dc8-4497-9cbc-d3879783d6e7",
|
||||
// Scopes = "https://jacalvarb2c.onmicrosoft.com/ProtectedApi/read"
|
||||
// };
|
||||
|
||||
ViewModel.Result = "Hit authorize to sign in";
|
||||
}
|
||||
|
||||
NativeWPFClientViewModel ViewModel => (NativeWPFClientViewModel)DataContext;
|
||||
|
||||
protected override void OnInitialized(EventArgs e)
|
||||
{
|
||||
base.OnInitialized(e);
|
||||
}
|
||||
|
||||
private async void Authorize_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_currentAuthorization == null)
|
||||
{
|
||||
Authorize.IsEnabled = false;
|
||||
await AuthorizeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AuthorizeAsync()
|
||||
{
|
||||
var authority = $"{ViewModel.BaseAddress}tfp/{ViewModel.Tenant}/{ViewModel.Policy}";
|
||||
var client = new PublicClientApplication(ViewModel.ClientId, authority)
|
||||
{
|
||||
ValidateAuthority = false
|
||||
};
|
||||
try
|
||||
{
|
||||
var scope = new string[] { };
|
||||
var appScopes = ViewModel.Scopes.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var currentAuthorization = await client.AcquireTokenAsync(
|
||||
appScopes,
|
||||
user: null,
|
||||
behavior: UIBehavior.ForceLogin,
|
||||
extraQueryParameters: string.Empty,
|
||||
extraScopesToConsent: null,
|
||||
authority: authority);
|
||||
|
||||
ViewModel.Result = currentAuthorization.User?.Name ?? "Authenticated but no name";
|
||||
}
|
||||
catch (MsalException ex)
|
||||
{
|
||||
if (ex.ErrorCode != "authentication_canceled")
|
||||
{
|
||||
// An unexpected error occurred.
|
||||
string message = ex.Message;
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
message += "Inner Exception : " + ex.InnerException.Message;
|
||||
}
|
||||
|
||||
MessageBox.Show(message);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_currentAuthorization = null;
|
||||
Authorize.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class NativeWPFClientViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private string _result;
|
||||
|
||||
public string BaseAddress { get; set; }
|
||||
public string RedirectUri { get; set; }
|
||||
public string Tenant { get; set; }
|
||||
public string Policy { get; set; }
|
||||
public string ClientId { get; set; }
|
||||
public string Scopes { get; set; }
|
||||
|
||||
|
||||
public string Result
|
||||
{
|
||||
get => _result;
|
||||
set
|
||||
{
|
||||
_result = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result)));
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<ImportDirectoryBuildTargets>false</ImportDirectoryBuildTargets>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{39AA4E4D-5E62-4213-8641-BF8012D45DE4}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>NativeWPFClient</RootNamespace>
|
||||
<AssemblyName>NativeWPFClient</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<!-- Workaround for Visual Studio complaining about inexisting project.json file -->
|
||||
<RuntimeIdentifier Condition="'$(BaseNuGetRuntimeIdentifier)' != ''">$(BaseNuGetRuntimeIdentifier)</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Identity.Client">
|
||||
<Version>1.1.0-preview</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("NativeWPFClient")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NativeWPFClient")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace NativeWPFClient.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NativeWPFClient.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace NativeWPFClient.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// 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 IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Authentication;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods to configure authentication for existing APIs coexisting with an Authorization Server.
|
||||
/// </summary>
|
||||
public static class AuthenticationBuilderExtensions
|
||||
{
|
||||
private const string IdentityServerJwtNameSuffix = "API";
|
||||
|
||||
private static readonly PathString DefaultIdentityUIPathPrefix =
|
||||
new PathString("/Identity");
|
||||
|
||||
/// <summary>
|
||||
/// Adds an authentication handler for an API that coexists with an Authorization Server.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
|
||||
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
|
||||
public static AuthenticationBuilder AddIdentityServerJwt(this AuthenticationBuilder builder)
|
||||
{
|
||||
var services = builder.Services;
|
||||
services.TryAddSingleton<IIdentityServerJwtDescriptor, IdentityServerJwtDescriptor>();
|
||||
services.TryAddEnumerable(ServiceDescriptor
|
||||
.Transient<IConfigureOptions<JwtBearerOptions>, IdentityServerJwtBearerOptionsConfiguration>(JwtBearerOptionsFactory));
|
||||
|
||||
services.AddAuthentication(IdentityServerJwtConstants.IdentityServerJwtScheme)
|
||||
.AddPolicyScheme(IdentityServerJwtConstants.IdentityServerJwtScheme, null, options =>
|
||||
{
|
||||
options.ForwardDefaultSelector = new IdentityServerJwtPolicySchemeForwardSelector(
|
||||
DefaultIdentityUIPathPrefix,
|
||||
IdentityServerJwtConstants.IdentityServerJwtBearerScheme).SelectScheme;
|
||||
})
|
||||
.AddJwtBearer(IdentityServerJwtConstants.IdentityServerJwtBearerScheme, null, o => { });
|
||||
|
||||
|
||||
return builder;
|
||||
|
||||
IdentityServerJwtBearerOptionsConfiguration JwtBearerOptionsFactory(IServiceProvider sp)
|
||||
{
|
||||
var schemeName = IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
|
||||
|
||||
var localApiDescriptor = sp.GetRequiredService<IIdentityServerJwtDescriptor>();
|
||||
var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>();
|
||||
var apiName = hostingEnvironment.ApplicationName + IdentityServerJwtNameSuffix;
|
||||
|
||||
return new IdentityServerJwtBearerOptionsConfiguration(schemeName, apiName, localApiDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// 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 IdentityServer4.Extensions;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class IdentityServerJwtBearerOptionsConfiguration : IConfigureNamedOptions<JwtBearerOptions>
|
||||
{
|
||||
private readonly string _scheme;
|
||||
private readonly string _apiName;
|
||||
private readonly IIdentityServerJwtDescriptor _localApiDescriptor;
|
||||
|
||||
public IdentityServerJwtBearerOptionsConfiguration(
|
||||
string scheme,
|
||||
string apiName,
|
||||
IIdentityServerJwtDescriptor localApiDescriptor)
|
||||
{
|
||||
_scheme = scheme;
|
||||
_apiName = apiName;
|
||||
_localApiDescriptor = localApiDescriptor;
|
||||
}
|
||||
|
||||
public void Configure(string name, JwtBearerOptions options)
|
||||
{
|
||||
var definitions = _localApiDescriptor.GetResourceDefinitions();
|
||||
if (!definitions.ContainsKey(_apiName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(name, _scheme, StringComparison.Ordinal))
|
||||
{
|
||||
options.Events = options.Events ?? new JwtBearerEvents();
|
||||
options.Events.OnMessageReceived = ResolveAuthorityAndKeysAsync;
|
||||
options.Audience = _apiName;
|
||||
|
||||
var staticConfiguration = new OpenIdConnectConfiguration
|
||||
{
|
||||
Issuer = options.Authority
|
||||
};
|
||||
|
||||
var manager = new StaticConfigurationManager(staticConfiguration);
|
||||
options.ConfigurationManager = manager;
|
||||
options.TokenValidationParameters.ValidIssuer = options.Authority;
|
||||
options.TokenValidationParameters.NameClaimType = "name";
|
||||
options.TokenValidationParameters.RoleClaimType = "role";
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task ResolveAuthorityAndKeysAsync(MessageReceivedContext messageReceivedContext)
|
||||
{
|
||||
var options = messageReceivedContext.Options;
|
||||
if (options.TokenValidationParameters.ValidIssuer == null || options.TokenValidationParameters.IssuerSigningKey == null)
|
||||
{
|
||||
var store = messageReceivedContext.HttpContext.RequestServices.GetRequiredService<ISigningCredentialStore>();
|
||||
var credential = await store.GetSigningCredentialsAsync();
|
||||
options.Authority = options.Authority ?? messageReceivedContext.HttpContext.GetIdentityServerIssuerUri();
|
||||
options.TokenValidationParameters.IssuerSigningKey = credential.Key;
|
||||
options.TokenValidationParameters.ValidIssuer = options.Authority;
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(JwtBearerOptions options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for a default API authentication handler.
|
||||
/// </summary>
|
||||
public class IdentityServerJwtConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Scheme used for the default API policy authentication scheme.
|
||||
/// </summary>
|
||||
public const string IdentityServerJwtScheme = "IdentityServerJwt";
|
||||
|
||||
/// <summary>
|
||||
/// Scheme used for the underlying default API JwtBearer authentication scheme.
|
||||
/// </summary>
|
||||
public const string IdentityServerJwtBearerScheme = "IdentityServerJwtBearer";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Authentication
|
||||
{
|
||||
internal class IdentityServerJwtPolicySchemeForwardSelector
|
||||
{
|
||||
private readonly PathString _identityPath;
|
||||
private readonly string _IdentityServerJwtScheme;
|
||||
|
||||
public IdentityServerJwtPolicySchemeForwardSelector(
|
||||
string identityPath,
|
||||
string IdentityServerJwtScheme)
|
||||
{
|
||||
_identityPath = identityPath;
|
||||
_IdentityServerJwtScheme = IdentityServerJwtScheme;
|
||||
}
|
||||
|
||||
public string SelectScheme(HttpContext ctx)
|
||||
{
|
||||
if (ctx.Request.Path.StartsWithSegments(_identityPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return IdentityConstants.ApplicationScheme;
|
||||
}
|
||||
|
||||
return _IdentityServerJwtScheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class StaticConfigurationManager : IConfigurationManager<OpenIdConnectConfiguration>
|
||||
{
|
||||
private readonly Task<OpenIdConnectConfiguration> _configuration;
|
||||
|
||||
public StaticConfigurationManager(OpenIdConnectConfiguration configuration) => _configuration = Task.FromResult(configuration);
|
||||
|
||||
public Task<OpenIdConnectConfiguration> GetConfigurationAsync(CancellationToken cancel) => _configuration;
|
||||
|
||||
public void RequestRefresh()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for the different application profiles for applications in an authorization server.
|
||||
/// </summary>
|
||||
public static class ApplicationProfiles
|
||||
{
|
||||
/// <summary>
|
||||
/// The application is an external API registered with the authorization server.
|
||||
/// </summary>
|
||||
public const string API = "API";
|
||||
|
||||
/// <summary>
|
||||
/// The application is an API that coexists with the authorization server.
|
||||
/// </summary>
|
||||
public const string IdentityServerJwt = "IdentityServerJwt";
|
||||
|
||||
/// <summary>
|
||||
/// The application is an external single page application registered with the authorization server.
|
||||
/// </summary>
|
||||
public const string SPA = "SPA";
|
||||
|
||||
/// <summary>
|
||||
/// The application is a single page application that coexists with the authorization server.
|
||||
/// </summary>
|
||||
public const string IdentityServerSPA = "IdentityServerSPA";
|
||||
|
||||
/// <summary>
|
||||
/// The application is a native application like a mobile or desktop application.
|
||||
/// </summary>
|
||||
public const string NativeApp = "NativeApp";
|
||||
|
||||
/// <summary>
|
||||
/// The application is a web application.
|
||||
/// </summary>
|
||||
internal const string WebApplication = "WebApplication";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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 IdentityServer4.Models;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants used for storing information about application profiles in the <see cref="Client.Properties"/> or <see cref="Resource.Properties"/>
|
||||
/// of a <see cref="Client"/> or <see cref="ApiResource"/> respectively.
|
||||
/// </summary>
|
||||
public static class ApplicationProfilesPropertyNames
|
||||
{
|
||||
/// <summary>
|
||||
/// Key to the Profile on <see cref="Client.Properties"/> or <see cref="Resource.Properties"/>.
|
||||
/// The Profile value will be one of the constants in <see cref="ApplicationProfiles"/>.
|
||||
/// </summary>
|
||||
public const string Profile = nameof(Profile);
|
||||
|
||||
/// <summary>
|
||||
/// Key to the Source on <see cref="Client.Properties"/> or <see cref="Resource.Properties"/>.
|
||||
/// The Source value will be Configuration if present.
|
||||
/// </summary>
|
||||
public const string Source = nameof(Source);
|
||||
|
||||
/// <summary>
|
||||
/// Key to the Clients on <see cref="Resource.Properties"/>.
|
||||
/// The Clients value will be <c>*</c> to indicate that all clients are allowed to access this resource or a space separated list of
|
||||
/// the client ids that are allowed to access this resource.
|
||||
/// </summary>
|
||||
public const string Clients = nameof(Clients);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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 IdentityServer4.Models;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for special values defined for specific <see cref="ApplicationProfilesPropertyNames" /> keys.
|
||||
/// </summary>
|
||||
public static class ApplicationProfilesPropertyValues
|
||||
{
|
||||
/// <summary>
|
||||
/// The value given to <see cref="ApplicationProfilesPropertyNames.Clients"/> in <see cref="Resource.Properties"/> to indicate that the
|
||||
/// resource can be accessed by all configured clients.
|
||||
/// </summary>
|
||||
public const string AllowAllApplications = "*";
|
||||
|
||||
/// <summary>
|
||||
/// The value given to <see cref="ApplicationProfilesPropertyNames.Source"/> in <see cref="Resource.Properties"/> or <see cref="Client.Properties"/>
|
||||
/// to indicate that the application was defined in configuration.
|
||||
/// </summary>
|
||||
public const string Configuration = nameof(Configuration);
|
||||
|
||||
/// <summary>
|
||||
/// The value given to <see cref="ApplicationProfilesPropertyNames.Source"/> in <see cref="Resource.Properties"/>
|
||||
/// to indicate that the resource was defined as a default identity resource.
|
||||
/// </summary>
|
||||
public const string Default = nameof(Default);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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 IdentityServer4.Configuration;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class AspNetConventionsConfigureOptions : IConfigureOptions<IdentityServerOptions>
|
||||
{
|
||||
public void Configure(IdentityServerOptions options)
|
||||
{
|
||||
options.Events.RaiseErrorEvents = true;
|
||||
options.Events.RaiseInformationEvents = true;
|
||||
options.Events.RaiseFailureEvents = true;
|
||||
options.Events.RaiseSuccessEvents = true;
|
||||
options.Authentication.CookieAuthenticationScheme = IdentityConstants.ApplicationScheme;
|
||||
options.UserInteraction.LoginUrl = "/Identity/Account/Login";
|
||||
options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
|
||||
options.UserInteraction.ErrorUrl = "/Identity/Error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class ClientDefinition : ServiceDefinition
|
||||
{
|
||||
public string RedirectUri { get; set; }
|
||||
public string LogoutUri { get; set; }
|
||||
public string ClientSecret { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// 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 IdentityServer4.Models;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
internal class ConfigureApiResources : IConfigureOptions<ApiAuthorizationOptions>
|
||||
{
|
||||
private static readonly char[] ScopesSeparator = new char[] { ' ' };
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ConfigureApiResources> _logger;
|
||||
private readonly IIdentityServerJwtDescriptor _localApiDescriptor;
|
||||
|
||||
public ConfigureApiResources(
|
||||
IConfiguration configuration,
|
||||
IIdentityServerJwtDescriptor localApiDescriptor,
|
||||
ILogger<ConfigureApiResources> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_localApiDescriptor = localApiDescriptor;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Configure(ApiAuthorizationOptions options)
|
||||
{
|
||||
var resources = GetApiResources();
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
options.ApiResources.Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<ApiResource> GetApiResources()
|
||||
{
|
||||
var data = _configuration
|
||||
.Get<Dictionary<string, ResourceDefinition>>();
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
foreach (var kvp in data)
|
||||
{
|
||||
_logger.LogInformation($"Configuring API resource '{kvp.Key}'.");
|
||||
yield return GetResource(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
var localResources = _localApiDescriptor.GetResourceDefinitions();
|
||||
if (localResources != null)
|
||||
{
|
||||
foreach (var kvp in localResources)
|
||||
{
|
||||
_logger.LogInformation($"Configuring local API resource '{kvp.Key}'.");
|
||||
yield return GetResource(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResource GetResource(string name, ResourceDefinition definition)
|
||||
{
|
||||
switch (definition.Profile)
|
||||
{
|
||||
case ApplicationProfiles.API:
|
||||
return GetAPI(name, definition);
|
||||
case ApplicationProfiles.IdentityServerJwt:
|
||||
return GetLocalAPI(name, definition);
|
||||
default:
|
||||
throw new InvalidOperationException($"Type '{definition.Profile}' is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private string[] ParseScopes(string scopes)
|
||||
{
|
||||
if (scopes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var parsed = scopes.Split(ScopesSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parsed.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
private ApiResource GetAPI(string name, ResourceDefinition definition) =>
|
||||
ApiResourceBuilder.ApiResource(name)
|
||||
.FromConfiguration()
|
||||
.WithAllowedClients(ApplicationProfilesPropertyValues.AllowAllApplications)
|
||||
.ReplaceScopes(ParseScopes(definition.Scopes) ?? new[] { name })
|
||||
.Build();
|
||||
|
||||
private ApiResource GetLocalAPI(string name, ResourceDefinition definition) =>
|
||||
ApiResourceBuilder.IdentityServerJwt(name)
|
||||
.FromConfiguration()
|
||||
.WithAllowedClients(ApplicationProfilesPropertyValues.AllowAllApplications)
|
||||
.ReplaceScopes(ParseScopes(definition.Scopes) ?? new[] { name })
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using IdentityServer4.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
internal class ConfigureClientScopes : IPostConfigureOptions<ApiAuthorizationOptions>
|
||||
{
|
||||
private static readonly char[] DefaultClientListSeparator = new char[] { ' ' };
|
||||
private readonly ILogger<ConfigureClientScopes> _logger;
|
||||
|
||||
public ConfigureClientScopes(ILogger<ConfigureClientScopes> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void PostConfigure(string name, ApiAuthorizationOptions options)
|
||||
{
|
||||
AddApiResourceScopesToClients(options);
|
||||
AddIdentityResourceScopesToClients(options);
|
||||
}
|
||||
|
||||
private void AddIdentityResourceScopesToClients(ApiAuthorizationOptions options)
|
||||
{
|
||||
foreach (var identityResource in options.IdentityResources)
|
||||
{
|
||||
if (!identityResource.Properties.TryGetValue(ApplicationProfilesPropertyNames.Clients, out var clientList))
|
||||
{
|
||||
_logger.LogInformation($"Identity resource '{identityResource.Name}' doesn't define a list of allowed applications.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var resourceClients = clientList.Split(DefaultClientListSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (resourceClients.Length == 0)
|
||||
{
|
||||
_logger.LogInformation($"Identity resource '{identityResource.Name}' doesn't define a list of allowed applications.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resourceClients.Length == 1 && resourceClients[0] == ApplicationProfilesPropertyValues.AllowAllApplications)
|
||||
{
|
||||
_logger.LogInformation($"Identity resource '{identityResource.Name}' allows all applications.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Identity resource '{identityResource.Name}' allows applications '{string.Join(" ", resourceClients)}'.");
|
||||
}
|
||||
|
||||
foreach (var client in options.Clients)
|
||||
{
|
||||
if ((resourceClients.Length == 1 && resourceClients[0] == ApplicationProfilesPropertyValues.AllowAllApplications) ||
|
||||
resourceClients.Contains(client.ClientId))
|
||||
{
|
||||
client.AllowedScopes.Add(identityResource.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddApiResourceScopesToClients(ApiAuthorizationOptions options)
|
||||
{
|
||||
foreach (var resource in options.ApiResources)
|
||||
{
|
||||
if (!resource.Properties.TryGetValue(ApplicationProfilesPropertyNames.Clients, out var clientList))
|
||||
{
|
||||
_logger.LogInformation($"Resource '{resource.Name}' doesn't define a list of allowed applications.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var resourceClients = clientList.Split(DefaultClientListSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (resourceClients.Length == 0)
|
||||
{
|
||||
_logger.LogInformation($"Resource '{resource.Name}' doesn't define a list of allowed applications.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resourceClients.Length == 1 && resourceClients[0] == ApplicationProfilesPropertyValues.AllowAllApplications)
|
||||
{
|
||||
_logger.LogInformation($"Resource '{resource.Name}' allows all applications.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Resource '{resource.Name}' allows applications '{string.Join(" ", resourceClients)}'.");
|
||||
}
|
||||
|
||||
foreach (var client in options.Clients)
|
||||
{
|
||||
if ((resourceClients.Length == 1 && resourceClients[0] == ApplicationProfilesPropertyValues.AllowAllApplications) ||
|
||||
resourceClients.Contains(client.ClientId))
|
||||
{
|
||||
AddScopes(resource, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddScopes(ApiResource resource, Client client)
|
||||
{
|
||||
foreach (var scope in resource.Scopes)
|
||||
{
|
||||
client.AllowedScopes.Add(scope.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
// 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 IdentityServer4.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class ConfigureClients : IConfigureOptions<ApiAuthorizationOptions>
|
||||
{
|
||||
private const string DefaultLocalSPARelativeRedirectUri = "";
|
||||
private const string DefaultLocalSPARelativePostLogoutRedirectUri = "";
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ConfigureClients> _logger;
|
||||
|
||||
public ConfigureClients(
|
||||
IConfiguration configuration,
|
||||
ILogger<ConfigureClients> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Configure(ApiAuthorizationOptions options)
|
||||
{
|
||||
foreach (var client in GetClients())
|
||||
{
|
||||
options.Clients.Add(client);
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<Client> GetClients()
|
||||
{
|
||||
var data = _configuration.Get<Dictionary<string, ClientDefinition>>();
|
||||
if (data != null)
|
||||
{
|
||||
foreach (var kvp in data)
|
||||
{
|
||||
_logger.LogInformation($"Configuring client '{kvp.Key}'.");
|
||||
var name = kvp.Key;
|
||||
var definition = kvp.Value;
|
||||
|
||||
switch (definition.Profile)
|
||||
{
|
||||
case ApplicationProfiles.SPA:
|
||||
yield return GetSPA(name, definition);
|
||||
break;
|
||||
//case ApplicationProfiles.WebApplication:
|
||||
// yield return GetWebApplication(name, definition);
|
||||
// break;
|
||||
case ApplicationProfiles.IdentityServerSPA:
|
||||
yield return GetLocalSPA(name, definition);
|
||||
break;
|
||||
case ApplicationProfiles.NativeApp:
|
||||
yield return GetNativeApp(name, definition);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Type '{definition.Profile}' is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Client GetWebApplication(string name, ClientDefinition definition)
|
||||
{
|
||||
if (definition.RedirectUri == null ||
|
||||
!Uri.TryCreate(definition.RedirectUri, UriKind.Absolute, out var redirectUri))
|
||||
{
|
||||
throw new InvalidOperationException($"The redirect uri " +
|
||||
$"'{definition.RedirectUri}' for '{name}' is invalid. " +
|
||||
$"The redirect URI must be an absolute url.");
|
||||
}
|
||||
|
||||
if (definition.LogoutUri == null ||
|
||||
!Uri.TryCreate(definition.LogoutUri, UriKind.Absolute, out var postLogouturi))
|
||||
{
|
||||
throw new InvalidOperationException($"The logout uri " +
|
||||
$"'{definition.LogoutUri}' for '{name}' is invalid. " +
|
||||
$"The logout URI must be an absolute url.");
|
||||
}
|
||||
|
||||
if (!string.Equals(
|
||||
redirectUri.GetLeftPart(UriPartial.Authority),
|
||||
postLogouturi.GetLeftPart(UriPartial.Authority),
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException($"The redirect uri and the logout uri " +
|
||||
$"for '{name}' have a different scheme, host or port.");
|
||||
}
|
||||
|
||||
if (definition.ClientSecret == null)
|
||||
{
|
||||
throw new InvalidOperationException($"The configuration for '{name}' does not contain a client secret. " +
|
||||
$"Client secrets are required for web applications.");
|
||||
}
|
||||
|
||||
return ClientBuilder.WebApplication(name)
|
||||
.WithRedirectUri(definition.RedirectUri)
|
||||
.WithLogoutRedirectUri(definition.LogoutUri)
|
||||
.FromConfiguration()
|
||||
.WithClientSecret(definition.ClientSecret)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private Client GetSPA(string name, ClientDefinition definition)
|
||||
{
|
||||
if (definition.RedirectUri == null ||
|
||||
!Uri.TryCreate(definition.RedirectUri, UriKind.Absolute, out var redirectUri))
|
||||
{
|
||||
throw new InvalidOperationException($"The redirect uri " +
|
||||
$"'{definition.RedirectUri}' for '{name}' is invalid. " +
|
||||
$"The redirect URI must be an absolute url.");
|
||||
}
|
||||
|
||||
if (definition.LogoutUri == null ||
|
||||
!Uri.TryCreate(definition.LogoutUri, UriKind.Absolute, out var postLogouturi))
|
||||
{
|
||||
throw new InvalidOperationException($"The logout uri " +
|
||||
$"'{definition.LogoutUri}' for '{name}' is invalid. " +
|
||||
$"The logout URI must be an absolute url.");
|
||||
}
|
||||
|
||||
if (!string.Equals(
|
||||
redirectUri.GetLeftPart(UriPartial.Authority),
|
||||
postLogouturi.GetLeftPart(UriPartial.Authority),
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException($"The redirect uri and the logout uri " +
|
||||
$"for '{name}' have a different scheme, host or port.");
|
||||
}
|
||||
|
||||
var client = ClientBuilder.SPA(name)
|
||||
.WithRedirectUri(definition.RedirectUri)
|
||||
.WithLogoutRedirectUri(definition.LogoutUri)
|
||||
.WithAllowedOrigins(redirectUri.GetLeftPart(UriPartial.Authority))
|
||||
.FromConfiguration();
|
||||
|
||||
return client.Build();
|
||||
}
|
||||
|
||||
private Client GetNativeApp(string name, ClientDefinition definition)
|
||||
{
|
||||
var client = ClientBuilder.NativeApp(name)
|
||||
.FromConfiguration();
|
||||
return client.Build();
|
||||
}
|
||||
|
||||
private Client GetLocalSPA(string name, ClientDefinition definition)
|
||||
{
|
||||
var client = ClientBuilder
|
||||
.IdentityServerSPA(name)
|
||||
.WithRedirectUri(definition.RedirectUri ?? DefaultLocalSPARelativeRedirectUri)
|
||||
.WithLogoutRedirectUri(definition.LogoutUri ?? DefaultLocalSPARelativePostLogoutRedirectUri)
|
||||
.WithAllowedOrigins()
|
||||
.FromConfiguration();
|
||||
|
||||
return client.Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// 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.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class ConfigureIdentityResources : IConfigureOptions<ApiAuthorizationOptions>
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ConfigureIdentityResources> _logger;
|
||||
private static readonly char[] ScopesSeparator = new char[] { ' ' };
|
||||
|
||||
public ConfigureIdentityResources(IConfiguration configuration, ILogger<ConfigureIdentityResources> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Configure(ApiAuthorizationOptions options)
|
||||
{
|
||||
var data = _configuration.Get<IdentityResourceDefinition>();
|
||||
if (data != null && data.Scopes != null)
|
||||
{
|
||||
var scopes = ParseScopes(data.Scopes);
|
||||
if (scopes != null && scopes.Length > 0)
|
||||
{
|
||||
ClearDefaultIdentityResources(options);
|
||||
}
|
||||
foreach (var scope in scopes)
|
||||
{
|
||||
switch (scope)
|
||||
{
|
||||
case IdentityServer4.IdentityServerConstants.StandardScopes.OpenId:
|
||||
options.IdentityResources.Add(IdentityResourceBuilder.OpenId()
|
||||
.AllowAllClients()
|
||||
.FromConfiguration()
|
||||
.Build());
|
||||
break;
|
||||
case IdentityServer4.IdentityServerConstants.StandardScopes.Profile:
|
||||
options.IdentityResources.Add(IdentityResourceBuilder.Profile()
|
||||
.AllowAllClients()
|
||||
.FromConfiguration()
|
||||
.Build());
|
||||
break;
|
||||
case IdentityServer4.IdentityServerConstants.StandardScopes.Address:
|
||||
options.IdentityResources.Add(IdentityResourceBuilder.Address()
|
||||
.AllowAllClients()
|
||||
.FromConfiguration()
|
||||
.Build());
|
||||
break;
|
||||
case IdentityServer4.IdentityServerConstants.StandardScopes.Email:
|
||||
options.IdentityResources.Add(IdentityResourceBuilder.Email()
|
||||
.AllowAllClients()
|
||||
.FromConfiguration()
|
||||
.Build());
|
||||
break;
|
||||
case IdentityServer4.IdentityServerConstants.StandardScopes.Phone:
|
||||
options.IdentityResources.Add(IdentityResourceBuilder.Phone()
|
||||
.AllowAllClients()
|
||||
.FromConfiguration()
|
||||
.Build());
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid identity resource name '{scope}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearDefaultIdentityResources(ApiAuthorizationOptions options)
|
||||
{
|
||||
var allDefault = true;
|
||||
foreach (var resource in options.IdentityResources)
|
||||
{
|
||||
if (!resource.Properties.TryGetValue(ApplicationProfilesPropertyNames.Source, out var source) ||
|
||||
!string.Equals(ApplicationProfilesPropertyValues.Default, source, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
allDefault = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allDefault)
|
||||
{
|
||||
options.IdentityResources.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private string[] ParseScopes(string scopes)
|
||||
{
|
||||
if (scopes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var parsed = scopes.Split(ScopesSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parsed.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class ConfigureSigningCredentials : IConfigureOptions<ApiAuthorizationOptions>
|
||||
{
|
||||
// We need to cast the underlying int value of the EphemeralKeySet to X509KeyStorageFlags
|
||||
// due to the fact that is not part of .NET Standard. This value is only used with non-windows
|
||||
// platforms (all .NET Core) for which the value is defined on the underlying platform.
|
||||
private const X509KeyStorageFlags UnsafeEphemeralKeySet = (X509KeyStorageFlags)32;
|
||||
private const string DefaultTempKeyRelativePath = "obj/tempkey.json";
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ConfigureSigningCredentials> _logger;
|
||||
|
||||
public ConfigureSigningCredentials(
|
||||
IConfiguration configuration,
|
||||
ILogger<ConfigureSigningCredentials> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Configure(ApiAuthorizationOptions options)
|
||||
{
|
||||
var key = LoadKey();
|
||||
options.SigningCredential = key;
|
||||
}
|
||||
|
||||
public SigningCredentials LoadKey()
|
||||
{
|
||||
var key = new KeyDefinition();
|
||||
_configuration.Bind(key);
|
||||
switch (key.Type)
|
||||
{
|
||||
case KeySources.Development:
|
||||
var developmentKeyPath = Path.Combine(Directory.GetCurrentDirectory(), key.FilePath ?? DefaultTempKeyRelativePath);
|
||||
var createIfMissing = key.Persisted ?? true;
|
||||
_logger.LogInformation($"Loading development key at '{developmentKeyPath}'.");
|
||||
var developmentKey = new RsaSecurityKey(SigningKeysLoader.LoadDevelopment(developmentKeyPath, createIfMissing))
|
||||
{
|
||||
KeyId = "Development"
|
||||
};
|
||||
return new SigningCredentials(developmentKey, "RS256");
|
||||
case KeySources.File:
|
||||
var pfxPath = Path.Combine(Directory.GetCurrentDirectory(), key.FilePath);
|
||||
var pfxPassword = key.Password;
|
||||
var storageFlags = GetStorageFlags(key);
|
||||
_logger.LogInformation($"Loading certificate file at '{pfxPath}' with storage flags '{key.StorageFlags}'.");
|
||||
return new SigningCredentials(new X509SecurityKey(SigningKeysLoader.LoadFromFile(pfxPath, key.Password, storageFlags)), "RS256");
|
||||
case KeySources.Store:
|
||||
if (!Enum.TryParse<StoreLocation>(key.StoreLocation, out var storeLocation))
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid certificate store location '{key.StoreLocation}'.");
|
||||
}
|
||||
_logger.LogInformation($"Loading certificate with subject '{key.Name}' in '{key.StoreLocation}\\{key.StoreName}'.");
|
||||
return new SigningCredentials(new X509SecurityKey(SigningKeysLoader.LoadFromStoreCert(key.Name, key.StoreName, storeLocation, GetCurrentTime())), "RS256");
|
||||
case null:
|
||||
throw new InvalidOperationException($"Key type not specified.");
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid key type '{key.Type ?? "(null)"}'.");
|
||||
}
|
||||
}
|
||||
|
||||
// for testing purposes only
|
||||
internal virtual DateTimeOffset GetCurrentTime() => DateTimeOffset.UtcNow;
|
||||
|
||||
private X509KeyStorageFlags GetStorageFlags(KeyDefinition key)
|
||||
{
|
||||
var defaultFlags = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
|
||||
UnsafeEphemeralKeySet : (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.PersistKeySet :
|
||||
X509KeyStorageFlags.DefaultKeySet);
|
||||
|
||||
if (key.StorageFlags == null)
|
||||
{
|
||||
return defaultFlags;
|
||||
}
|
||||
|
||||
var flagsList = key.StorageFlags.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (flagsList.Length == 0)
|
||||
{
|
||||
return defaultFlags;
|
||||
}
|
||||
|
||||
var result = ParseCurrentFlag(flagsList[0]);
|
||||
foreach (var flag in flagsList.Skip(1))
|
||||
{
|
||||
result |= ParseCurrentFlag(flag);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
X509KeyStorageFlags ParseCurrentFlag(string candidate)
|
||||
{
|
||||
if (Enum.TryParse<X509KeyStorageFlags>(candidate, out var flag))
|
||||
{
|
||||
return flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid storage flag '{candidate}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
internal interface IIdentityServerJwtDescriptor
|
||||
{
|
||||
IDictionary<string, ResourceDefinition> GetResourceDefinitions();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class IdentityResourceDefinition : ResourceDefinition
|
||||
{
|
||||
public IdentityResourceDefinition()
|
||||
{
|
||||
Profile = "API";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// 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.AspNetCore.Hosting;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
internal class IdentityServerJwtDescriptor : IIdentityServerJwtDescriptor
|
||||
{
|
||||
public IdentityServerJwtDescriptor(IHostingEnvironment environment)
|
||||
{
|
||||
Environment = environment;
|
||||
}
|
||||
|
||||
public IHostingEnvironment Environment { get; }
|
||||
|
||||
public IDictionary<string, ResourceDefinition> GetResourceDefinitions()
|
||||
{
|
||||
return new Dictionary<string, ResourceDefinition>
|
||||
{
|
||||
[Environment.ApplicationName + "API"] = new ResourceDefinition() { Profile = ApplicationProfiles.IdentityServerJwt }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
internal class KeyDefinition
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public bool? Persisted { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string StoreLocation { get; set; }
|
||||
public string StoreName { get; set; }
|
||||
public string StorageFlags { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
internal class KeySources
|
||||
{
|
||||
public const string File = nameof(File);
|
||||
public const string Development = nameof(Development);
|
||||
public const string Store = nameof(Store);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class ResourceDefinition : ServiceDefinition
|
||||
{
|
||||
public string Scopes { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class ServiceDefinition
|
||||
{
|
||||
public string Profile { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
// 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 Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal static class SigningKeysLoader
|
||||
{
|
||||
public static X509Certificate2 LoadFromFile(string path, string password, X509KeyStorageFlags keyStorageFlags)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new InvalidOperationException($"There was an error loading the certificate. The file '{path}' was not found.");
|
||||
}
|
||||
else if (password == null)
|
||||
{
|
||||
throw new InvalidOperationException("There was an error loading the certificate. No password was provided.");
|
||||
}
|
||||
|
||||
return new X509Certificate2(path, password, keyStorageFlags);
|
||||
}
|
||||
catch (CryptographicException e)
|
||||
{
|
||||
var message = "There was an error loading the certificate. Either the password is incorrect or the process does not have permisions to " +
|
||||
$"store the key in the Keyset '{keyStorageFlags}'";
|
||||
throw new InvalidOperationException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static X509Certificate2 LoadFromStoreCert(
|
||||
string subject,
|
||||
string storeName,
|
||||
StoreLocation storeLocation,
|
||||
DateTimeOffset currentTime)
|
||||
{
|
||||
using (var store = new X509Store(storeName, storeLocation))
|
||||
{
|
||||
X509Certificate2Collection storeCertificates = null;
|
||||
X509Certificate2 foundCertificate = null;
|
||||
|
||||
try
|
||||
{
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
storeCertificates = store.Certificates;
|
||||
var foundCertificates = storeCertificates
|
||||
.Find(X509FindType.FindBySubjectDistinguishedName, subject, validOnly: false);
|
||||
|
||||
foundCertificate = foundCertificates
|
||||
.OfType<X509Certificate2>()
|
||||
.Where(certificate => certificate.NotBefore <= currentTime && certificate.NotAfter > currentTime)
|
||||
.OrderBy(certificate => certificate.NotAfter)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (foundCertificate == null)
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't find a valid certificate with " +
|
||||
$"subject '{subject}' on the '{storeLocation}\\{storeName}'");
|
||||
}
|
||||
|
||||
return foundCertificate;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DisposeCertificates(storeCertificates, except: foundCertificate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static RSA LoadDevelopment(string path, bool createIfMissing)
|
||||
{
|
||||
var fileExists = File.Exists(path);
|
||||
if (!fileExists && !createIfMissing)
|
||||
{
|
||||
throw new InvalidOperationException($"Couldn't find the file '{path}' and creation of a development key was not requested.");
|
||||
}
|
||||
|
||||
if (fileExists)
|
||||
{
|
||||
var rsa = JsonConvert.DeserializeObject<RSAKeyParameters>(File.ReadAllText(path));
|
||||
return rsa.GetRSA();
|
||||
}
|
||||
else
|
||||
{
|
||||
var parameters = RSAKeyParameters.Create();
|
||||
var directory = Path.GetDirectoryName(path);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(parameters));
|
||||
return parameters.GetRSA();
|
||||
}
|
||||
}
|
||||
|
||||
private class RSAKeyParameters
|
||||
{
|
||||
public string D { get; set; }
|
||||
public string DP { get; set; }
|
||||
public string DQ { get; set; }
|
||||
public string E { get; set; }
|
||||
public string IQ { get; set; }
|
||||
public string M { get; set; }
|
||||
public string P { get; set; }
|
||||
public string Q { get; set; }
|
||||
|
||||
public static RSAKeyParameters Create()
|
||||
{
|
||||
using (var rsa = RSA.Create())
|
||||
{
|
||||
if (rsa is RSACryptoServiceProvider rSACryptoServiceProvider && rsa.KeySize < 2048)
|
||||
{
|
||||
rsa.KeySize = 2048;
|
||||
if (rsa.KeySize < 2048)
|
||||
{
|
||||
throw new InvalidOperationException("We can't generate an RSA key with at least 2048 bits. Key generation is not supported in this system.");
|
||||
}
|
||||
}
|
||||
|
||||
return GetParameters(rsa);
|
||||
}
|
||||
}
|
||||
|
||||
public static RSAKeyParameters GetParameters(RSA key)
|
||||
{
|
||||
var result = new RSAKeyParameters();
|
||||
var rawParameters = key.ExportParameters(includePrivateParameters: true);
|
||||
|
||||
if (rawParameters.D != null)
|
||||
{
|
||||
result.D = Convert.ToBase64String(rawParameters.D);
|
||||
}
|
||||
|
||||
if (rawParameters.DP != null)
|
||||
{
|
||||
result.DP = Convert.ToBase64String(rawParameters.DP);
|
||||
}
|
||||
|
||||
if (rawParameters.DQ != null)
|
||||
{
|
||||
result.DQ = Convert.ToBase64String(rawParameters.DQ);
|
||||
}
|
||||
|
||||
if (rawParameters.Exponent != null)
|
||||
{
|
||||
result.E = Convert.ToBase64String(rawParameters.Exponent);
|
||||
}
|
||||
|
||||
if (rawParameters.InverseQ != null)
|
||||
{
|
||||
result.IQ = Convert.ToBase64String(rawParameters.InverseQ);
|
||||
}
|
||||
|
||||
if (rawParameters.Modulus != null)
|
||||
{
|
||||
result.M = Convert.ToBase64String(rawParameters.Modulus);
|
||||
}
|
||||
|
||||
if (rawParameters.P != null)
|
||||
{
|
||||
result.P = Convert.ToBase64String(rawParameters.P);
|
||||
}
|
||||
|
||||
if (rawParameters.Q != null)
|
||||
{
|
||||
result.Q = Convert.ToBase64String(rawParameters.Q);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public RSA GetRSA()
|
||||
{
|
||||
var parameters = new RSAParameters();
|
||||
if (D != null)
|
||||
{
|
||||
parameters.D = Convert.FromBase64String(D);
|
||||
}
|
||||
|
||||
if (DP != null)
|
||||
{
|
||||
parameters.DP = Convert.FromBase64String(DP);
|
||||
}
|
||||
|
||||
if (DQ != null)
|
||||
{
|
||||
parameters.DQ = Convert.FromBase64String(DQ);
|
||||
}
|
||||
|
||||
if (E != null)
|
||||
{
|
||||
parameters.Exponent = Convert.FromBase64String(E);
|
||||
}
|
||||
|
||||
if (IQ != null)
|
||||
{
|
||||
parameters.InverseQ = Convert.FromBase64String(IQ);
|
||||
}
|
||||
|
||||
if (M != null)
|
||||
{
|
||||
parameters.Modulus = Convert.FromBase64String(M);
|
||||
}
|
||||
|
||||
if (P != null)
|
||||
{
|
||||
parameters.P = Convert.FromBase64String(P);
|
||||
}
|
||||
|
||||
if (Q != null)
|
||||
{
|
||||
parameters.Q = Convert.FromBase64String(Q);
|
||||
}
|
||||
|
||||
var rsa = RSA.Create();
|
||||
rsa.ImportParameters(parameters);
|
||||
|
||||
return rsa;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisposeCertificates(X509Certificate2Collection certificates, X509Certificate2 except)
|
||||
{
|
||||
if (certificates != null)
|
||||
{
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
if (!certificate.Equals(except))
|
||||
{
|
||||
certificate.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 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.Threading.Tasks;
|
||||
using IdentityServer4.EntityFramework.Entities;
|
||||
using IdentityServer4.EntityFramework.Extensions;
|
||||
using IdentityServer4.EntityFramework.Interfaces;
|
||||
using IdentityServer4.EntityFramework.Options;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Database abstraction for a combined <see cref="DbContext"/> using ASP.NET Identity and Identity Server.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser"></typeparam>
|
||||
public class ApiAuthorizationDbContext<TUser> : IdentityDbContext<TUser>, IPersistedGrantDbContext where TUser : IdentityUser
|
||||
{
|
||||
private readonly IOptions<OperationalStoreOptions> _operationalStoreOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ApiAuthorizationDbContext{TUser}"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">The <see cref="DbContextOptions"/>.</param>
|
||||
/// <param name="operationalStoreOptions">The <see cref="IOptions{OperationalStoreOptions}"/>.</param>
|
||||
public ApiAuthorizationDbContext(
|
||||
DbContextOptions options,
|
||||
IOptions<OperationalStoreOptions> operationalStoreOptions)
|
||||
: base(options)
|
||||
{
|
||||
_operationalStoreOptions = operationalStoreOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="PersistedGrant"/>.
|
||||
/// </summary>
|
||||
public DbSet<PersistedGrant> PersistedGrants { get; set; }
|
||||
|
||||
Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
||||
base.OnModelCreating(builder);
|
||||
builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 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.AspNetCore.Http;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class AbsoluteUrlFactory : IAbsoluteUrlFactory
|
||||
{
|
||||
public AbsoluteUrlFactory(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
// We need the context accessor here in order to produce an absolute url from a potentially relative url.
|
||||
ContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
public IHttpContextAccessor ContextAccessor { get; }
|
||||
|
||||
// Call this method when you are overriding a service that doesn't have an HttpContext instance available.
|
||||
public string GetAbsoluteUrl(string path)
|
||||
{
|
||||
var (process, result) = ShouldProcessPath(path);
|
||||
if (!process)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ContextAccessor.HttpContext?.Request == null)
|
||||
{
|
||||
throw new InvalidOperationException("The request is not currently available. This service can only be used within the context of an existing HTTP request.");
|
||||
}
|
||||
|
||||
return GetAbsoluteUrl(ContextAccessor.HttpContext, path);
|
||||
}
|
||||
|
||||
// Call this method when you are implementing a service that has an HttpContext instance available.
|
||||
public string GetAbsoluteUrl(HttpContext context, string path)
|
||||
{
|
||||
var (process, result) = ShouldProcessPath(path);
|
||||
if (!process)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var request = context.Request;
|
||||
return $"{request.Scheme}://{request.Host.ToUriComponent()}{request.PathBase.ToUriComponent()}{path}";
|
||||
}
|
||||
|
||||
private (bool, string) ShouldProcessPath(string path)
|
||||
{
|
||||
if (path == null || !Uri.IsWellFormedUriString(path, UriKind.RelativeOrAbsolute))
|
||||
{
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
if (Uri.IsWellFormedUriString(path, UriKind.Absolute))
|
||||
{
|
||||
return (false, path);
|
||||
}
|
||||
|
||||
return (true, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// 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.Specialized;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityServer4.Configuration;
|
||||
using IdentityServer4.Endpoints.Results;
|
||||
using IdentityServer4.Hosting;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class AutoRedirectEndSessionEndpoint : IEndpointHandler
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserSession _session;
|
||||
private readonly IOptions<IdentityServerOptions> _identityServerOptions;
|
||||
private readonly IEndSessionRequestValidator _requestvalidator;
|
||||
|
||||
public AutoRedirectEndSessionEndpoint(
|
||||
ILogger<AutoRedirectEndSessionEndpoint> logger,
|
||||
IEndSessionRequestValidator requestValidator,
|
||||
IOptions<IdentityServerOptions> identityServerOptions,
|
||||
IUserSession session)
|
||||
{
|
||||
_logger = logger;
|
||||
_session = session;
|
||||
_identityServerOptions = identityServerOptions;
|
||||
_requestvalidator = requestValidator;
|
||||
}
|
||||
|
||||
public async Task<IEndpointResult> ProcessAsync(HttpContext ctx)
|
||||
{
|
||||
var validtionResult = ValidateRequest(ctx.Request);
|
||||
if (validtionResult != null)
|
||||
{
|
||||
return validtionResult;
|
||||
}
|
||||
|
||||
var parameters = await GetParametersAsync(ctx.Request);
|
||||
var user = await _session.GetUserAsync();
|
||||
var result = await _requestvalidator.ValidateAsync(parameters, user);
|
||||
if (result.IsError)
|
||||
{
|
||||
_logger.LogError($"Error ending session {result.Error}");
|
||||
return new RedirectResult(_identityServerOptions.Value.UserInteraction.ErrorUrl);
|
||||
}
|
||||
|
||||
var client = result.ValidatedRequest?.Client;
|
||||
if (client != null &&
|
||||
client.Properties.TryGetValue(ApplicationProfilesPropertyNames.Profile, out var type))
|
||||
{
|
||||
var signInScheme = _identityServerOptions.Value.Authentication.CookieAuthenticationScheme;
|
||||
if (signInScheme != null)
|
||||
{
|
||||
await ctx.SignOutAsync(signInScheme);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.SignOutAsync();
|
||||
}
|
||||
|
||||
return new RedirectResult(result.ValidatedRequest.PostLogOutUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new RedirectResult(_identityServerOptions.Value.UserInteraction.LogoutUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<NameValueCollection> GetParametersAsync(HttpRequest request)
|
||||
{
|
||||
if (HttpMethods.IsGet(request.Method))
|
||||
{
|
||||
return request.Query.AsNameValueCollection();
|
||||
}
|
||||
else
|
||||
{
|
||||
var form = await request.ReadFormAsync();
|
||||
return form.AsNameValueCollection();
|
||||
}
|
||||
}
|
||||
|
||||
private IEndpointResult ValidateRequest(HttpRequest request)
|
||||
{
|
||||
if (!HttpMethods.IsPost(request.Method) && !HttpMethods.IsGet(request.Method))
|
||||
{
|
||||
return new StatusCodeResult(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
if (HttpMethods.IsPost(request.Method) &&
|
||||
!string.Equals(request.ContentType, "application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new StatusCodeResult(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal class RedirectResult : IEndpointResult
|
||||
{
|
||||
|
||||
public RedirectResult(string url)
|
||||
{
|
||||
Url = url;
|
||||
}
|
||||
|
||||
public string Url { get; }
|
||||
|
||||
public Task ExecuteAsync(HttpContext context)
|
||||
{
|
||||
context.Response.Redirect(Url);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// 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 IdentityServer4.Extensions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class DefaultClientRequestParametersProvider : IClientRequestParametersProvider
|
||||
{
|
||||
public DefaultClientRequestParametersProvider(
|
||||
IAbsoluteUrlFactory urlFactory,
|
||||
IOptions<ApiAuthorizationOptions> options)
|
||||
{
|
||||
UrlFactory = urlFactory;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public IAbsoluteUrlFactory UrlFactory { get; }
|
||||
|
||||
public IOptions<ApiAuthorizationOptions> Options { get; }
|
||||
|
||||
public IDictionary<string, string> GetClientParameters(HttpContext context, string clientId)
|
||||
{
|
||||
var client = Options.Value.Clients[clientId];
|
||||
var authority = context.GetIdentityServerIssuerUri();
|
||||
var responseType = "";
|
||||
if (!client.Properties.TryGetValue(ApplicationProfilesPropertyNames.Profile, out var type))
|
||||
{
|
||||
throw new InvalidOperationException($"Can't determine the type for the client '{clientId}'");
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ApplicationProfiles.IdentityServerSPA:
|
||||
case ApplicationProfiles.SPA:
|
||||
responseType = "id_token token";
|
||||
break;
|
||||
case ApplicationProfiles.NativeApp:
|
||||
responseType = "code";
|
||||
break;
|
||||
//case ApplicationProfiles.WebApplication:
|
||||
// responseType = "id_token code";
|
||||
// break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid application type '{type}' for '{clientId}'.");
|
||||
}
|
||||
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
["authority"] = authority,
|
||||
["client_id"] = client.ClientId,
|
||||
["redirect_uri"] = UrlFactory.GetAbsoluteUrl(context, client.RedirectUris.First()),
|
||||
["post_logout_redirect_uri"] = UrlFactory.GetAbsoluteUrl(context, client.RedirectUris.First()),
|
||||
["response_type"] = responseType,
|
||||
["scope"] = string.Join(" ", client.AllowedScopes)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal interface IAbsoluteUrlFactory
|
||||
{
|
||||
string GetAbsoluteUrl(string path);
|
||||
string GetAbsoluteUrl(HttpContext context, string path);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates oauth/openID parameter values for configured clients.
|
||||
/// </summary>
|
||||
public interface IClientRequestParametersProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets parameter values for the client with client id<paramref name="clientId"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The current <see cref="HttpContext"/>.</param>
|
||||
/// <param name="clientId">The client id for the client.</param>
|
||||
/// <returns>A <see cref="IDictionary{TKey, TValue}"/> containing the client parameters and their values.</returns>
|
||||
IDictionary<string, string> GetClientParameters(HttpContext context, string clientId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// 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.Threading.Tasks;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class RelativeRedirectUriValidator : StrictRedirectUriValidator
|
||||
{
|
||||
public RelativeRedirectUriValidator(IAbsoluteUrlFactory absoluteUrlFactory)
|
||||
{
|
||||
if (absoluteUrlFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(absoluteUrlFactory));
|
||||
}
|
||||
|
||||
AbsoluteUrlFactory = absoluteUrlFactory;
|
||||
}
|
||||
|
||||
public IAbsoluteUrlFactory AbsoluteUrlFactory { get; }
|
||||
|
||||
public override Task<bool> IsRedirectUriValidAsync(string requestedUri, Client client)
|
||||
{
|
||||
if (IsLocalSPA(client))
|
||||
{
|
||||
return ValidateRelativeUris(requestedUri, client.RedirectUris);
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.IsRedirectUriValidAsync(requestedUri, client);
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<bool> IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client)
|
||||
{
|
||||
if (IsLocalSPA(client))
|
||||
{
|
||||
return ValidateRelativeUris(requestedUri, client.PostLogoutRedirectUris);
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.IsPostLogoutRedirectUriValidAsync(requestedUri, client);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsLocalSPA(Client client) =>
|
||||
client.Properties.TryGetValue(ApplicationProfilesPropertyNames.Profile, out var clientType) &&
|
||||
ApplicationProfiles.IdentityServerSPA == clientType;
|
||||
|
||||
private Task<bool> ValidateRelativeUris(string requestedUri, IEnumerable<string> clientUris)
|
||||
{
|
||||
foreach (var url in clientUris)
|
||||
{
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.Relative))
|
||||
{
|
||||
var newUri = AbsoluteUrlFactory.GetAbsoluteUrl(url);
|
||||
if (string.Equals(newUri, requestedUri, StringComparison.Ordinal))
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
// 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 IdentityServer4.Configuration;
|
||||
using IdentityServer4.EntityFramework.Interfaces;
|
||||
using IdentityServer4.Hosting;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Stores;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for configuring Identity Server.
|
||||
/// </summary>
|
||||
public static class IdentityServerBuilderConfigurationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures defaults for Identity Server for ASP.NET Core scenarios.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The <typeparamref name="TUser"/> type.</typeparam>
|
||||
/// <typeparam name="TContext">The <typeparamref name="TContext"/> type.</typeparam>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddApiAuthorization<TUser, TContext>(
|
||||
this IIdentityServerBuilder builder) where TUser : class
|
||||
where TContext : DbContext, IPersistedGrantDbContext
|
||||
{
|
||||
builder.AddApiAuthorization<TUser, TContext>(o => { });
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures defaults on Identity Server for ASP.NET Core scenarios.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The <typeparamref name="TUser"/> type.</typeparam>
|
||||
/// <typeparam name="TContext">The <typeparamref name="TContext"/> type.</typeparam>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <param name="configure">The <see cref="Action{ApplicationsOptions}"/>
|
||||
/// to configure the <see cref="ApiAuthorizationOptions"/>.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddApiAuthorization<TUser, TContext>(
|
||||
this IIdentityServerBuilder builder,
|
||||
Action<ApiAuthorizationOptions> configure)
|
||||
where TUser : class
|
||||
where TContext : DbContext, IPersistedGrantDbContext
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
builder.AddAspNetIdentity<TUser>()
|
||||
.AddOperationalStore<TContext>()
|
||||
.ConfigureReplacedServices()
|
||||
.AddIdentityResources()
|
||||
.AddApiResources()
|
||||
.AddClients()
|
||||
.AddSigningCredentials();
|
||||
|
||||
builder.Services.Configure(configure);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds API resources from the default configuration to the server using the key
|
||||
/// IdentityServer:Resources
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddApiResources(
|
||||
this IIdentityServerBuilder builder) => builder.AddApiResources(configuration: null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds API resources from the given <paramref name="configuration"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <param name="configuration">The <see cref="IConfiguration"/> instance containing the API definitions.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddApiResources(
|
||||
this IIdentityServerBuilder builder,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
builder.ConfigureReplacedServices();
|
||||
builder.AddInMemoryApiResources(Enumerable.Empty<ApiResource>());
|
||||
builder.Services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IConfigureOptions<ApiAuthorizationOptions>, ConfigureApiResources>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<ILogger<ConfigureApiResources>>();
|
||||
var effectiveConfig = configuration ?? sp.GetRequiredService<IConfiguration>().GetSection("IdentityServer:Resources");
|
||||
var localApiDescriptor = sp.GetService<IIdentityServerJwtDescriptor>();
|
||||
return new ConfigureApiResources(effectiveConfig, localApiDescriptor, logger);
|
||||
}));
|
||||
|
||||
// We take over the setup for the API resources as Identity Server registers the enumerable as a singleton
|
||||
// and that prevents normal composition.
|
||||
builder.Services.AddSingleton<IEnumerable<ApiResource>>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ApiAuthorizationOptions>>();
|
||||
return options.Value.ApiResources;
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds identity resources from the default configuration to the server using the key
|
||||
/// IdentityServer:Resources
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddIdentityResources(
|
||||
this IIdentityServerBuilder builder) => builder.AddIdentityResources(configuration: null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds identity resources from the given <paramref name="configuration"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <param name="configuration">The <see cref="IConfiguration"/> instance containing the API definitions.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddIdentityResources(
|
||||
this IIdentityServerBuilder builder,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
builder.ConfigureReplacedServices();
|
||||
builder.AddInMemoryIdentityResources(Enumerable.Empty<IdentityResource>());
|
||||
builder.Services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IConfigureOptions<ApiAuthorizationOptions>, ConfigureIdentityResources>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<ILogger<ConfigureIdentityResources>>();
|
||||
var effectiveConfig = configuration ?? sp.GetRequiredService<IConfiguration>().GetSection("IdentityServer:Identity");
|
||||
return new ConfigureIdentityResources(effectiveConfig, logger);
|
||||
}));
|
||||
|
||||
// We take over the setup for the identity resources as Identity Server registers the enumerable as a singleton
|
||||
// and that prevents normal composition.
|
||||
builder.Services.AddSingleton<IEnumerable<IdentityResource>>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ApiAuthorizationOptions>>();
|
||||
return options.Value.IdentityResources;
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds clients from the default configuration to the server using the key
|
||||
/// IdentityServer:Clients
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddClients(
|
||||
this IIdentityServerBuilder builder) => builder.AddClients(configuration: null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds clients from the given <paramref name="configuration"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <param name="configuration">The <see cref="IConfiguration"/> instance containing the client definitions.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddClients(
|
||||
this IIdentityServerBuilder builder,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
builder.ConfigureReplacedServices();
|
||||
builder.AddInMemoryClients(Enumerable.Empty<Client>());
|
||||
|
||||
builder.Services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPostConfigureOptions<ApiAuthorizationOptions>, ConfigureClientScopes>());
|
||||
|
||||
builder.Services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IConfigureOptions<ApiAuthorizationOptions>, ConfigureClients>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<ILogger<ConfigureClients>>();
|
||||
var effectiveConfig = configuration ?? sp.GetRequiredService<IConfiguration>().GetSection("IdentityServer:Clients");
|
||||
return new ConfigureClients(effectiveConfig, logger);
|
||||
}));
|
||||
|
||||
// We take over the setup for the clients as Identity Server registers the enumerable as a singleton and that prevents normal composition.
|
||||
builder.Services.AddSingleton<IEnumerable<Client>>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ApiAuthorizationOptions>>();
|
||||
return options.Value.Clients;
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a signing key from the default configuration to the server using the configuration key
|
||||
/// IdentityServer:Key
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddSigningCredentials(
|
||||
this IIdentityServerBuilder builder) => builder.AddSigningCredentials(configuration: null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a signing key from the given <paramref name="configuration"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IIdentityServerBuilder"/>.</param>
|
||||
/// <param name="configuration">The <see cref="IConfiguration"/>.</param>
|
||||
/// <returns>The <see cref="IIdentityServerBuilder"/>.</returns>
|
||||
public static IIdentityServerBuilder AddSigningCredentials(
|
||||
this IIdentityServerBuilder builder,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
builder.ConfigureReplacedServices();
|
||||
builder.Services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IConfigureOptions<ApiAuthorizationOptions>, ConfigureSigningCredentials>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<ILogger<ConfigureSigningCredentials>>();
|
||||
var effectiveConfig = configuration ?? sp.GetRequiredService<IConfiguration>().GetSection("IdentityServer:Key");
|
||||
return new ConfigureSigningCredentials(effectiveConfig, logger);
|
||||
}));
|
||||
|
||||
// We take over the setup for the credentials store as Identity Server registers a singleton
|
||||
builder.Services.AddSingleton<ISigningCredentialStore>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ApiAuthorizationOptions>>();
|
||||
return new DefaultSigningCredentialsStore(options.Value.SigningCredential);
|
||||
});
|
||||
|
||||
// We take over the setup for the validation keys store as Identity Server registers a singleton
|
||||
builder.Services.AddSingleton<IValidationKeysStore>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ApiAuthorizationOptions>>();
|
||||
return new DefaultValidationKeysStore(new[] { options.Value.SigningCredential.Key });
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static IIdentityServerBuilder ConfigureReplacedServices(this IIdentityServerBuilder builder)
|
||||
{
|
||||
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<IdentityServerOptions>, AspNetConventionsConfigureOptions>());
|
||||
builder.Services.TryAddSingleton<IAbsoluteUrlFactory, AbsoluteUrlFactory>();
|
||||
builder.Services.AddSingleton<IRedirectUriValidator, RelativeRedirectUriValidator>();
|
||||
builder.Services.AddSingleton<IClientRequestParametersProvider, DefaultClientRequestParametersProvider>();
|
||||
ReplaceEndSessionEndpoint(builder);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static void ReplaceEndSessionEndpoint(IIdentityServerBuilder builder)
|
||||
{
|
||||
// We don't have a better way to replace the end session endpoint as far as we know other than looking the descriptor up
|
||||
// on the container and replacing the instance. This is due to the fact that we chain on AddIdentityServer which configures the
|
||||
// list of endpoints by default.
|
||||
var endSessionEndpointDescriptor = builder.Services
|
||||
.Single(s => s.ImplementationInstance is Endpoint e &&
|
||||
string.Equals(e.Name, "Endsession", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals("/connect/endsession", e.Path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
builder.Services.Remove(endSessionEndpointDescriptor);
|
||||
builder.AddEndpoint<AutoRedirectEndSessionEndpoint>("EndSession", "/connect/endsession");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Description>ASP.NET Core API Authorization package powered by Identity Server.</Description>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>aspnetcore;apiauth;identity</PackageTags>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4" Version="$(IdentityServer4PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(MicrosoftAspNetCoreMvcPackageVersion)" />
|
||||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="$(IdentityServer4AspNetIdentityPackageVersion)" />
|
||||
<PackageReference Include="IdentityServer4.EntityFramework" Version="$(IdentityServer4EntityFrameworkPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EF\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj" />
|
||||
<ProjectReference Include="..\UI\Microsoft.AspNetCore.Identity.UI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// 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.IdentityModel.Tokens;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for API authorization.
|
||||
/// </summary>
|
||||
public class ApiAuthorizationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IdentityResources"/>.
|
||||
/// </summary>
|
||||
public IdentityResourceCollection IdentityResources { get; set; } =
|
||||
new IdentityResourceCollection
|
||||
{
|
||||
IdentityResourceBuilder.OpenId()
|
||||
.AllowAllClients()
|
||||
.FromDefault()
|
||||
.Build(),
|
||||
IdentityResourceBuilder.Profile()
|
||||
.AllowAllClients()
|
||||
.FromDefault()
|
||||
.Build()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ApiResources"/>.
|
||||
/// </summary>
|
||||
public ApiResourceCollection ApiResources { get; set; } =
|
||||
new ApiResourceCollection();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Clients"/>.
|
||||
/// </summary>
|
||||
public ClientCollection Clients { get; set; } =
|
||||
new ClientCollection();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="SigningCredentials"/> to use for signing tokens.
|
||||
/// </summary>
|
||||
public SigningCredentials SigningCredential { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// A builder for API resources
|
||||
/// </summary>
|
||||
public class ApiResourceBuilder
|
||||
{
|
||||
private ApiResource _apiResource;
|
||||
private bool _built;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new builder for an externally registered API.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the API.</param>
|
||||
/// <returns>An <see cref="ApiResourceBuilder"/>.</returns>
|
||||
public static ApiResourceBuilder ApiResource(string name)
|
||||
{
|
||||
var apiResource = new ApiResource(name);
|
||||
return new ApiResourceBuilder(apiResource)
|
||||
.WithApplicationProfile(ApplicationProfiles.API);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new builder for an API that coexists with an authorization server.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the API.</param>
|
||||
/// <returns>An <see cref="ApiResourceBuilder"/>.</returns>
|
||||
public static ApiResourceBuilder IdentityServerJwt(string name)
|
||||
{
|
||||
var apiResource = new ApiResource(name);
|
||||
return new ApiResourceBuilder(apiResource)
|
||||
.WithApplicationProfile(ApplicationProfiles.IdentityServerJwt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ApiResourceBuilder"/>.
|
||||
/// </summary>
|
||||
public ApiResourceBuilder() : this(new ApiResource())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ApiResourceBuilder"/>.
|
||||
/// </summary>
|
||||
/// <param name="resource">A preconfigured resource.</param>
|
||||
public ApiResourceBuilder(ApiResource resource)
|
||||
{
|
||||
_apiResource = resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the application profile for the resource.
|
||||
/// </summary>
|
||||
/// <param name="profile">The the profile for the application from <see cref="ApplicationProfiles"/>.</param>
|
||||
/// <returns>The <see cref="ApiResourceBuilder"/>.</returns>
|
||||
public ApiResourceBuilder WithApplicationProfile(string profile)
|
||||
{
|
||||
_apiResource.Properties.Add(ApplicationProfilesPropertyNames.Profile, profile);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds additional scopes to the API resource.
|
||||
/// </summary>
|
||||
/// <param name="resourceScopes">The list of scopes.</param>
|
||||
/// <returns>The <see cref="ApiResourceBuilder"/>.</returns>
|
||||
public ApiResourceBuilder WithScopes(params string[] resourceScopes)
|
||||
{
|
||||
foreach (var scope in resourceScopes)
|
||||
{
|
||||
if (_apiResource.Scopes.Any(s => s.Name == scope))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_apiResource.Scopes.Add(new Scope(scope));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the scopes defined for the application with a new set of scopes.
|
||||
/// </summary>
|
||||
/// <param name="resourceScopes">The list of scopes.</param>
|
||||
/// <returns>The <see cref="ApiResourceBuilder"/>.</returns>
|
||||
public ApiResourceBuilder ReplaceScopes(params string[] resourceScopes)
|
||||
{
|
||||
_apiResource.Scopes.Clear();
|
||||
|
||||
return WithScopes(resourceScopes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the API resource to allow all clients to access it.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="ApiResourceBuilder"/>.</returns>
|
||||
public ApiResourceBuilder AllowAllClients()
|
||||
{
|
||||
_apiResource.Properties[ApplicationProfilesPropertyNames.Clients] = ApplicationProfilesPropertyValues.AllowAllApplications;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the API resource.
|
||||
/// </summary>
|
||||
/// <returns>The built <see cref="IdentityServer4.Models.ApiResource"/>.</returns>
|
||||
public ApiResource Build()
|
||||
{
|
||||
if (_built)
|
||||
{
|
||||
throw new InvalidOperationException("ApiResource already built.");
|
||||
}
|
||||
|
||||
_built = true;
|
||||
return _apiResource;
|
||||
}
|
||||
|
||||
internal ApiResourceBuilder WithAllowedClients(string clientList)
|
||||
{
|
||||
_apiResource.Properties[ApplicationProfilesPropertyNames.Clients] = clientList;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal ApiResourceBuilder FromConfiguration()
|
||||
{
|
||||
_apiResource.Properties[ApplicationProfilesPropertyNames.Source] = ApplicationProfilesPropertyValues.Configuration;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 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.Collections.ObjectModel;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of <see cref="ApiResource"/>.
|
||||
/// </summary>
|
||||
public class ApiResourceCollection : Collection<ApiResource>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ApiResourceCollection"/>.
|
||||
/// </summary>
|
||||
public ApiResourceCollection()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ApiResourceCollection"/> with the given
|
||||
/// API resources in <paramref name="list"/>.
|
||||
/// </summary>
|
||||
/// <param name="list">The initial list of <see cref="ApiResource"/>.</param>
|
||||
public ApiResourceCollection(IList<ApiResource> list) : base(list)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an API resource given its name.
|
||||
/// </summary>
|
||||
/// <param name="key">The name of the <see cref="ApiResource"/>.</param>
|
||||
/// <returns>The <see cref="ApiResource"/>.</returns>
|
||||
public ApiResource this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
{
|
||||
var candidate = Items[i];
|
||||
if (string.Equals(candidate.Name, key, StringComparison.Ordinal))
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"ApiResource '{key}' not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the resources in <paramref name="resources"/> to the collection.
|
||||
/// </summary>
|
||||
/// <param name="resources">The list of <see cref="ApiResource"/> to add.</param>
|
||||
public void AddRange(IEnumerable<ApiResource> resources)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the resources in <paramref name="resources"/> to the collection.
|
||||
/// </summary>
|
||||
/// <param name="resources">The list of <see cref="ApiResource"/> to add.</param>
|
||||
public void AddRange(params ApiResource[] resources)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new externally registered API.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the API.</param>
|
||||
/// <param name="configure">The <see cref="Action{ApiResourceBuilder}"/> to configure the externally registered API.</param>
|
||||
public void AddApiResource(string name, Action<ApiResourceBuilder> configure)
|
||||
{
|
||||
var apiResource = ApiResourceBuilder.ApiResource(name);
|
||||
configure(apiResource);
|
||||
Add(apiResource.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new API that coexists with an authorization server.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the API.</param>
|
||||
/// <param name="configure">The <see cref="Func{ApiResourceBuilder, ApiResource}"/> to configure the identity server jwt API.</param>
|
||||
public void AddIdentityServerJwt(string name, Action<ApiResourceBuilder> configure)
|
||||
{
|
||||
var apiResource = ApiResourceBuilder.IdentityServerJwt(name);
|
||||
configure(apiResource);
|
||||
Add(apiResource.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
// 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 IdentityServer4;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// A builder for Clients.
|
||||
/// </summary>
|
||||
public class ClientBuilder
|
||||
{
|
||||
private const string NativeAppClientRedirectUri = "urn:ietf:wg:oauth:2.0:oob";
|
||||
|
||||
Client _client;
|
||||
private bool _built = false;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new builder for a single page application that coexists with an authorization server.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the single page application.</param>
|
||||
/// <returns>A <see cref="ClientBuilder"/>.</returns>
|
||||
public static ClientBuilder IdentityServerSPA(string clientId)
|
||||
{
|
||||
var client = CreateClient(clientId);
|
||||
return new ClientBuilder(client)
|
||||
.WithApplicationProfile(ApplicationProfiles.IdentityServerSPA)
|
||||
.WithAllowedGrants(GrantTypes.Implicit)
|
||||
.WithAllowedOrigins(Array.Empty<string>())
|
||||
.AllowAccessTokensViaBrowser();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new builder for an externally registered single page application.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the single page application.</param>
|
||||
/// <returns>A <see cref="ClientBuilder"/>.</returns>
|
||||
public static ClientBuilder SPA(string clientId)
|
||||
{
|
||||
var client = CreateClient(clientId);
|
||||
return new ClientBuilder(client)
|
||||
.WithApplicationProfile(ApplicationProfiles.SPA)
|
||||
.WithAllowedGrants(GrantTypes.Implicit)
|
||||
.AllowAccessTokensViaBrowser();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new builder for an externally registered native application.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the native application.</param>
|
||||
/// <returns>A <see cref="ClientBuilder"/>.</returns>
|
||||
public static ClientBuilder NativeApp(string clientId)
|
||||
{
|
||||
var client = CreateClient(clientId);
|
||||
return new ClientBuilder(client)
|
||||
.WithApplicationProfile(ApplicationProfiles.NativeApp)
|
||||
.WithAllowedGrants(GrantTypes.Code)
|
||||
.WithRedirectUri(NativeAppClientRedirectUri)
|
||||
.WithLogoutRedirectUri(NativeAppClientRedirectUri)
|
||||
.WithPkce()
|
||||
.WithoutClientSecrets()
|
||||
.WithScopes(IdentityServerConstants.StandardScopes.OfflineAccess);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new builder for an externally registered web application.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the web application.</param>
|
||||
/// <returns>A <see cref="ClientBuilder"/>.</returns>
|
||||
internal static ClientBuilder WebApplication(string clientId)
|
||||
{
|
||||
var client = CreateClient(clientId);
|
||||
return new ClientBuilder(client)
|
||||
.WithApplicationProfile(ApplicationProfiles.WebApplication)
|
||||
.WithAllowedGrants(GrantTypes.HybridAndClientCredentials)
|
||||
.WithScopes(IdentityServerConstants.StandardScopes.OfflineAccess);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ClientBuilder"/>.
|
||||
/// </summary>
|
||||
public ClientBuilder() : this(new Client())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new intance of <see cref="ClientBuilder"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">A preconfigured client.</param>
|
||||
public ClientBuilder(Client client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the client id (and name) of the client.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The new client id.</param>
|
||||
/// <returns>The <see cref="ClientBuilder"/>.</returns>
|
||||
public ClientBuilder WithClientId(string clientId)
|
||||
{
|
||||
_client.ClientId = clientId;
|
||||
_client.ClientName = clientId;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the application profile for the client.
|
||||
/// </summary>
|
||||
/// <param name="profile">The the profile for the application from <see cref="ApplicationProfiles"/>.</param>
|
||||
/// <returns>The <see cref="ClientBuilder"/>.</returns>
|
||||
public ClientBuilder WithApplicationProfile(string profile)
|
||||
{
|
||||
_client.Properties.Add(ApplicationProfilesPropertyNames.Profile, profile);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="scopes"/> to the list of allowed scopes for the client.
|
||||
/// </summary>
|
||||
/// <param name="scopes">The list of scopes.</param>
|
||||
/// <returns>The <see cref="ClientBuilder"/>.</returns>
|
||||
public ClientBuilder WithScopes(params string[] scopes)
|
||||
{
|
||||
foreach (var scope in scopes)
|
||||
{
|
||||
_client.AllowedScopes.Add(scope);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="redirectUri"/> to the list of valid redirect uris for the client.
|
||||
/// </summary>
|
||||
/// <param name="redirectUri">The redirect uri to add.</param>
|
||||
/// <returns>The <see cref="ClientBuilder"/>.</returns>
|
||||
public ClientBuilder WithRedirectUri(string redirectUri)
|
||||
{
|
||||
_client.RedirectUris.Add(redirectUri);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="logoutUri"/> to the list of valid logout redirect uris for the client.
|
||||
/// </summary>
|
||||
/// <param name="logoutUri">The logout uri to add.</param>
|
||||
/// <returns>The <see cref="ClientBuilder"/>.</returns>
|
||||
public ClientBuilder WithLogoutRedirectUri(string logoutUri)
|
||||
{
|
||||
_client.PostLogoutRedirectUris.Add(logoutUri);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="clientSecret"/> to the list of client secrets for the client and configures the client to
|
||||
/// require using the secret when getting tokens from the token endpoint.
|
||||
/// </summary>
|
||||
/// <param name="clientSecret">The client secret to add.</param>
|
||||
/// <returns>The <see cref="ClientBuilder"/>.</returns>
|
||||
internal ClientBuilder WithClientSecret(string clientSecret)
|
||||
{
|
||||
_client.ClientSecrets.Add(new Secret(clientSecret));
|
||||
_client.RequireClientSecret = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any configured client secret from the client and configures it to not require a client secret for getting tokens
|
||||
/// from the token endpoint.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="ClientBuilder"/>.</returns>
|
||||
public ClientBuilder WithoutClientSecrets()
|
||||
{
|
||||
_client.RequireClientSecret = false;
|
||||
_client.ClientSecrets.Clear();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the client.
|
||||
/// </summary>
|
||||
/// <returns>The built <see cref="Client"/>.</returns>
|
||||
public Client Build()
|
||||
{
|
||||
if (_built)
|
||||
{
|
||||
throw new InvalidOperationException("Client already built.");
|
||||
}
|
||||
|
||||
_built = true;
|
||||
return _client;
|
||||
}
|
||||
|
||||
internal ClientBuilder WithPkce()
|
||||
{
|
||||
_client.RequirePkce = true;
|
||||
_client.AllowPlainTextPkce = false;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
internal ClientBuilder FromConfiguration()
|
||||
{
|
||||
_client.Properties[ApplicationProfilesPropertyNames.Source] = ApplicationProfilesPropertyValues.Configuration;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal ClientBuilder WithAllowedGrants(ICollection<string> grants)
|
||||
{
|
||||
_client.AllowedGrantTypes = grants;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal ClientBuilder WithAllowedOrigins(params string[] origins)
|
||||
{
|
||||
_client.AllowedCorsOrigins = origins;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal ClientBuilder AllowAccessTokensViaBrowser()
|
||||
{
|
||||
_client.AllowAccessTokensViaBrowser = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
private static Client CreateClient(string name)
|
||||
{
|
||||
var client = new Client
|
||||
{
|
||||
ClientId = name,
|
||||
ClientName = name,
|
||||
RequireConsent = false
|
||||
};
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// 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 IdentityServer4.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of <see cref="Client"/>.
|
||||
/// </summary>
|
||||
public class ClientCollection : Collection<Client>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ClientCollection"/>.
|
||||
/// </summary>
|
||||
public ClientCollection()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ClientCollection"/> with the given
|
||||
/// clients in <paramref name="list"/>.
|
||||
/// </summary>
|
||||
/// <param name="list">The initial list of <see cref="Client"/>.</param>
|
||||
public ClientCollection(IList<Client> list) : base(list)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a client given its client id.
|
||||
/// </summary>
|
||||
/// <param name="key">The name of the <see cref="Client"/>.</param>
|
||||
/// <returns>The <see cref="Client"/>.</returns>
|
||||
public Client this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
for (var i = 0; i < Items.Count; i++)
|
||||
{
|
||||
var candidate = Items[i];
|
||||
if (string.Equals(candidate.ClientId, key, StringComparison.Ordinal))
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Client '{key}' not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the clients in <paramref name="clients"/> to the collection.
|
||||
/// </summary>
|
||||
/// <param name="clients">The list of <see cref="Client"/> to add.</param>
|
||||
public void AddRange(IEnumerable<Client> clients)
|
||||
{
|
||||
foreach (var client in clients)
|
||||
{
|
||||
Add(client);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the clients in <paramref name="clients"/> to the collection.
|
||||
/// </summary>
|
||||
/// <param name="clients">The list of <see cref="Client"/> to add.</param>
|
||||
public void AddRange(params Client[] clients)
|
||||
{
|
||||
foreach (var client in clients)
|
||||
{
|
||||
Add(client);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a single page application that coexists with an authorization server.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the single page application.</param>
|
||||
/// <param name="configure">The <see cref="Action{ClientBuilder}"/> to configure the default single page application.</param>
|
||||
public void AddIdentityServerSPA(string clientId, Action<ClientBuilder> configure)
|
||||
{
|
||||
var app = ClientBuilder.IdentityServerSPA(clientId);
|
||||
configure(app);
|
||||
Add(app.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an externally registered single page application.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the single page application.</param>
|
||||
/// <param name="configure">The <see cref="Action{ClientBuilder}"/> to configure the default single page application.</param>
|
||||
public void AddSPA(string clientId, Action<ClientBuilder> configure)
|
||||
{
|
||||
var app = ClientBuilder.SPA(clientId);
|
||||
configure(app);
|
||||
Add(app.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an externally registered native application..
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the single page application.</param>
|
||||
/// <param name="configure">The <see cref="Action{ClientBuilder}"/> to configure the native application.</param>
|
||||
public void AddNativeApp(string clientId, Action<ClientBuilder> configure)
|
||||
{
|
||||
var app = ClientBuilder.NativeApp(clientId);
|
||||
configure(app);
|
||||
Add(app.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an externally registered web application..
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client id for the web application.</param>
|
||||
/// <param name="configure">The <see cref="Action{ClientBuilder}"/> to configure the web application.</param>
|
||||
public void AddWebApplication(string clientId, Action<ClientBuilder> configure)
|
||||
{
|
||||
var app = ClientBuilder.WebApplication(clientId);
|
||||
configure(app);
|
||||
Add(app.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
// 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 IdentityServer4;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// A builder for identity resources
|
||||
/// </summary>
|
||||
public class IdentityResourceBuilder
|
||||
{
|
||||
private IdentityResource _identityResource;
|
||||
private bool _built;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an openid resource.
|
||||
/// </summary>
|
||||
public static IdentityResourceBuilder OpenId() =>
|
||||
IdentityResource(IdentityServerConstants.StandardScopes.OpenId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a profile resource.
|
||||
/// </summary>
|
||||
public static IdentityResourceBuilder Profile() =>
|
||||
IdentityResource(IdentityServerConstants.StandardScopes.Profile);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an address resource.
|
||||
/// </summary>
|
||||
public static IdentityResourceBuilder Address() =>
|
||||
IdentityResource(IdentityServerConstants.StandardScopes.Address);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an email resource.
|
||||
/// </summary>
|
||||
public static IdentityResourceBuilder Email() =>
|
||||
IdentityResource(IdentityServerConstants.StandardScopes.Email);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a phone resource.
|
||||
/// </summary>
|
||||
public static IdentityResourceBuilder Phone() =>
|
||||
IdentityResource(IdentityServerConstants.StandardScopes.Phone);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="IdentityResourceBuilder"/>.
|
||||
/// </summary>
|
||||
public IdentityResourceBuilder() : this(new IdentityResource())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="IdentityResourceBuilder"/>.
|
||||
/// </summary>
|
||||
/// <param name="resource">A preconfigured resource.</param>
|
||||
public IdentityResourceBuilder(IdentityResource resource)
|
||||
{
|
||||
_identityResource = resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the API resource to allow all clients to access it.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IdentityResourceBuilder"/>.</returns>
|
||||
public IdentityResourceBuilder AllowAllClients()
|
||||
{
|
||||
_identityResource.Properties[ApplicationProfilesPropertyNames.Clients] = ApplicationProfilesPropertyValues.AllowAllApplications;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the API resource.
|
||||
/// </summary>
|
||||
/// <returns>The built <see cref="IdentityServer4.Models.IdentityResource"/>.</returns>
|
||||
public IdentityResource Build()
|
||||
{
|
||||
if (_built)
|
||||
{
|
||||
throw new InvalidOperationException("IdentityResource already built.");
|
||||
}
|
||||
|
||||
_built = true;
|
||||
return _identityResource;
|
||||
}
|
||||
|
||||
internal IdentityResourceBuilder WithAllowedClients(string clientList)
|
||||
{
|
||||
_identityResource.Properties[ApplicationProfilesPropertyNames.Clients] = clientList;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal IdentityResourceBuilder FromConfiguration()
|
||||
{
|
||||
_identityResource.Properties[ApplicationProfilesPropertyNames.Source] = ApplicationProfilesPropertyValues.Configuration;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal IdentityResourceBuilder FromDefault()
|
||||
{
|
||||
_identityResource.Properties[ApplicationProfilesPropertyNames.Source] = ApplicationProfilesPropertyValues.Default;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal static IdentityResourceBuilder IdentityResource(string name)
|
||||
{
|
||||
var identityResource = GetResource(name);
|
||||
return new IdentityResourceBuilder(identityResource);
|
||||
}
|
||||
|
||||
private static IdentityResource GetResource(string name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case IdentityServerConstants.StandardScopes.OpenId:
|
||||
return new IdentityResources.OpenId();
|
||||
case IdentityServerConstants.StandardScopes.Profile:
|
||||
return new IdentityResources.Profile();
|
||||
case IdentityServerConstants.StandardScopes.Address:
|
||||
return new IdentityResources.Address();
|
||||
case IdentityServerConstants.StandardScopes.Email:
|
||||
return new IdentityResources.Email();
|
||||
case IdentityServerConstants.StandardScopes.Phone:
|
||||
return new IdentityResources.Phone();
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid identity resource type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
// 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 IdentityServer4.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of <see cref="IdentityResource"/>.
|
||||
/// </summary>
|
||||
public class IdentityResourceCollection : Collection<IdentityResource>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="IdentityResourceCollection"/>.
|
||||
/// </summary>
|
||||
public IdentityResourceCollection()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="IdentityResourceCollection"/> with the given
|
||||
/// identity resources in <paramref name="list"/>.
|
||||
/// </summary>
|
||||
/// <param name="list">The initial list of <see cref="IdentityResource"/>.</param>
|
||||
public IdentityResourceCollection(IList<IdentityResource> list) : base(list)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an identity resource given its name.
|
||||
/// </summary>
|
||||
/// <param name="key">The name of the <see cref="IdentityResource"/>.</param>
|
||||
/// <returns>The <see cref="IdentityResource"/>.</returns>
|
||||
public IdentityResource this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
{
|
||||
var candidate = Items[i];
|
||||
if (string.Equals(candidate.Name, key, StringComparison.Ordinal))
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"IdentityResource '{key}' not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the identity resources in <paramref name="identityResources"/> to the collection.
|
||||
/// </summary>
|
||||
/// <param name="identityResources">The list of <see cref="IdentityResource"/> to add.</param>
|
||||
public void AddRange(IEnumerable<IdentityResource> identityResources)
|
||||
{
|
||||
foreach (var resource in identityResources)
|
||||
{
|
||||
Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the identity resources in <paramref name="identityResources"/> to the collection.
|
||||
/// </summary>
|
||||
/// <param name="identityResources">The list of <see cref="IdentityResource"/> to add.</param>
|
||||
public void AddRange(params IdentityResource[] identityResources)
|
||||
{
|
||||
foreach (var resource in identityResources)
|
||||
{
|
||||
Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an openid resource.
|
||||
/// </summary>
|
||||
public void AddOpenId() =>
|
||||
Add(IdentityResourceBuilder.OpenId().Build());
|
||||
|
||||
/// <summary>
|
||||
/// Adds an openid resource.
|
||||
/// </summary>
|
||||
/// <param name="configure">The <see cref="Action{IdentityResourceBuilder}"/> to configure the openid scope.</param>
|
||||
public void AddOpenId(Action<IdentityResourceBuilder> configure)
|
||||
{
|
||||
var resource = IdentityResourceBuilder.OpenId();
|
||||
configure(resource);
|
||||
Add(resource.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a profile resource.
|
||||
/// </summary>
|
||||
public void AddProfile() =>
|
||||
Add(IdentityResourceBuilder.Profile().Build());
|
||||
|
||||
/// <summary>
|
||||
/// Adds a profile resource.
|
||||
/// </summary>
|
||||
/// <param name="configure">The <see cref="Action{IdentityResourceBuilder}"/> to configure the profile scope.</param>
|
||||
public void AddProfile(Action<IdentityResourceBuilder> configure)
|
||||
{
|
||||
var resource = IdentityResourceBuilder.Profile();
|
||||
configure(resource);
|
||||
Add(resource.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an address resource.
|
||||
/// </summary>
|
||||
public void AddAddress() =>
|
||||
Add(IdentityResourceBuilder.Address().Build());
|
||||
|
||||
/// <summary>
|
||||
/// Adds an address resource.
|
||||
/// </summary>
|
||||
/// <param name="configure">The <see cref="Action{IdentityResourceBuilder}"/> to configure the address scope.</param>
|
||||
public void AddAddress(Action<IdentityResourceBuilder> configure)
|
||||
{
|
||||
var resource = IdentityResourceBuilder.Address();
|
||||
configure(resource);
|
||||
Add(resource.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an email resource.
|
||||
/// </summary>
|
||||
public void AddEmail() =>
|
||||
Add(IdentityResourceBuilder.Email().Build());
|
||||
|
||||
/// <summary>
|
||||
/// Adds an email resource.
|
||||
/// </summary>
|
||||
/// <param name="configure">The <see cref="Action{IdentityResourceBuilder}"/> to configure the email scope.</param>
|
||||
public void AddEmail(Action<IdentityResourceBuilder> configure)
|
||||
{
|
||||
var resource = IdentityResourceBuilder.Email();
|
||||
configure(resource);
|
||||
Add(resource.Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a phone resource.
|
||||
/// </summary>
|
||||
public void AddPhone() =>
|
||||
Add(IdentityResourceBuilder.Phone().Build());
|
||||
|
||||
/// <summary>
|
||||
/// Adds a phone resource.
|
||||
/// </summary>
|
||||
/// <param name="configure">The <see cref="Action{IdentityResourceBuilder}"/> to configure the phone scope.</param>
|
||||
public void AddPhone(Action<IdentityResourceBuilder> configure)
|
||||
{
|
||||
var resource = IdentityResourceBuilder.Phone();
|
||||
configure(resource);
|
||||
Add(resource.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey = 0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// 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.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
/// <summary>
|
||||
/// A tag helper for generating client parameters for a given oauth/openid client as data attributes.
|
||||
/// </summary>
|
||||
[HtmlTargetElement("*", Attributes = "[asp-apiauth-parameters]")]
|
||||
public class ClientParametersTagHelper : TagHelper
|
||||
{
|
||||
private readonly IClientRequestParametersProvider _clientRequestParametersProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ClientParametersTagHelper"/>.
|
||||
/// </summary>
|
||||
/// <param name="clientRequestParametersProvider">The <see cref="IClientRequestParametersProvider"/>.</param>
|
||||
public ClientParametersTagHelper(IClientRequestParametersProvider clientRequestParametersProvider)
|
||||
{
|
||||
_clientRequestParametersProvider = clientRequestParametersProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client id.
|
||||
/// </summary>
|
||||
[HtmlAttributeName("asp-apiauth-parameters")]
|
||||
public string ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ViewContext.
|
||||
/// </summary>
|
||||
[ViewContext]
|
||||
public ViewContext ViewContext { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
var parameters = _clientRequestParametersProvider.GetClientParameters(ViewContext.HttpContext, ClientId);
|
||||
if (parameters == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Parameters for client '{ClientId}' not found.");
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
output.Attributes.Add("data-" + parameter.Key, parameter.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityServer4.Configuration;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class IdentityServerJwtBearerOptionsConfigurationTest
|
||||
{
|
||||
[Fact]
|
||||
public void Configure_SetsUpBearerSchemeForTheLocalApi()
|
||||
{
|
||||
// Arrange
|
||||
var localApiDescriptor = new Mock<IIdentityServerJwtDescriptor>();
|
||||
localApiDescriptor.Setup(lad => lad.GetResourceDefinitions())
|
||||
.Returns(new Dictionary<string, ResourceDefinition>
|
||||
{
|
||||
["TestAPI"] = new ResourceDefinition { Profile = ApplicationProfiles.IdentityServerJwt }
|
||||
});
|
||||
|
||||
var bearerConfiguration = new IdentityServerJwtBearerOptionsConfiguration(
|
||||
"authScheme",
|
||||
"TestAPI",
|
||||
localApiDescriptor.Object);
|
||||
|
||||
var options = new JwtBearerOptions();
|
||||
|
||||
// Act
|
||||
bearerConfiguration.Configure("authScheme", options);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("name", options.TokenValidationParameters.NameClaimType);
|
||||
Assert.Equal("role", options.TokenValidationParameters.RoleClaimType);
|
||||
Assert.Equal("TestAPI", options.Audience);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAuthorityAndKeysAsync_SetsUpAuthorityAndKeysOnTheTokenValidationParametersAsync()
|
||||
{
|
||||
// Arrange
|
||||
var localApiDescriptor = new Mock<IIdentityServerJwtDescriptor>();
|
||||
localApiDescriptor.Setup(lad => lad.GetResourceDefinitions())
|
||||
.Returns(new Dictionary<string, ResourceDefinition>
|
||||
{
|
||||
["TestAPI"] = new ResourceDefinition { Profile = ApplicationProfiles.IdentityServerJwt }
|
||||
});
|
||||
|
||||
var credentialsStore = new Mock<ISigningCredentialStore>();
|
||||
var key = new RsaSecurityKey(RSA.Create());
|
||||
credentialsStore.Setup(cs => cs.GetSigningCredentialsAsync())
|
||||
.ReturnsAsync(new SigningCredentials(key, "RS256"));
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.Scheme = "https";
|
||||
context.Request.Host = new HostString("localhost");
|
||||
context.RequestServices = new ServiceCollection()
|
||||
.AddSingleton(new IdentityServerOptions())
|
||||
.AddSingleton(credentialsStore.Object)
|
||||
.BuildServiceProvider();
|
||||
|
||||
var options = new JwtBearerOptions();
|
||||
var args = new MessageReceivedContext(context, new AuthenticationScheme("TestAPI",null, Mock.Of<IAuthenticationHandler>().GetType()), options);
|
||||
|
||||
// Act
|
||||
await IdentityServerJwtBearerOptionsConfiguration.ResolveAuthorityAndKeysAsync(args);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("https://localhost", options.TokenValidationParameters.ValidIssuer);
|
||||
Assert.Equal(key, options.TokenValidationParameters.IssuerSigningKey);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_IgnoresOptionsForDifferentSchemes()
|
||||
{
|
||||
// Arrange
|
||||
var localApiDescriptor = new Mock<IIdentityServerJwtDescriptor>();
|
||||
localApiDescriptor.Setup(lad => lad.GetResourceDefinitions())
|
||||
.Returns(new Dictionary<string, ResourceDefinition>
|
||||
{
|
||||
["TestAPI"] = new ResourceDefinition { Profile = ApplicationProfiles.IdentityServerJwt }
|
||||
});
|
||||
|
||||
var bearerConfiguration = new IdentityServerJwtBearerOptionsConfiguration(
|
||||
"authScheme",
|
||||
"TestAPI",
|
||||
localApiDescriptor.Object);
|
||||
|
||||
var options = new JwtBearerOptions();
|
||||
|
||||
// Act
|
||||
bearerConfiguration.Configure("otherScheme", options);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual("name", options.TokenValidationParameters.NameClaimType);
|
||||
Assert.NotEqual("role", options.TokenValidationParameters.RoleClaimType);
|
||||
Assert.NotEqual("TestAPI", options.Audience);
|
||||
Assert.NotEqual("https://localhost", options.Authority);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_IgnoresOptionsForNonExistingAPIs()
|
||||
{
|
||||
// Arrange
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.Scheme = "https";
|
||||
context.Request.Host = new HostString("localhost");
|
||||
context.RequestServices = new ServiceCollection()
|
||||
.AddSingleton(new IdentityServerOptions())
|
||||
.BuildServiceProvider();
|
||||
contextAccessor.SetupGet(ca => ca.HttpContext).Returns(
|
||||
context);
|
||||
|
||||
var localApiDescriptor = new Mock<IIdentityServerJwtDescriptor>();
|
||||
localApiDescriptor.Setup(lad => lad.GetResourceDefinitions())
|
||||
.Returns(new Dictionary<string, ResourceDefinition>
|
||||
{
|
||||
["TestAPI"] = new ResourceDefinition { Profile = ApplicationProfiles.IdentityServerJwt }
|
||||
});
|
||||
|
||||
var credentialsStore = new Mock<ISigningCredentialStore>();
|
||||
var key = new RsaSecurityKey(RSA.Create());
|
||||
credentialsStore.Setup(cs => cs.GetSigningCredentialsAsync())
|
||||
.ReturnsAsync(new SigningCredentials(key, "RS256"));
|
||||
|
||||
var bearerConfiguration = new IdentityServerJwtBearerOptionsConfiguration(
|
||||
"authScheme",
|
||||
"NonExistingApi",
|
||||
localApiDescriptor.Object);
|
||||
|
||||
var options = new JwtBearerOptions();
|
||||
|
||||
// Act
|
||||
bearerConfiguration.Configure("authScheme", options);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual("name", options.TokenValidationParameters.NameClaimType);
|
||||
Assert.NotEqual("role", options.TokenValidationParameters.RoleClaimType);
|
||||
Assert.NotEqual(key, options.TokenValidationParameters.IssuerSigningKey);
|
||||
Assert.NotEqual("TestAPI", options.Audience);
|
||||
Assert.NotEqual("https://localhost", options.Authority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// 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.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Authentication
|
||||
{
|
||||
public class LocalApiPolicySchemeForwardSelectorTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("/Identity/Account/Login")]
|
||||
[InlineData("/Identity/Error")]
|
||||
[InlineData("/identity/Account/Manage")]
|
||||
[InlineData("/Identity/ACCOUNT/TwoFactor")]
|
||||
public void SelectScheme_ReturnsTheIdentityApplicationScheme_ForIdentityRelatedPaths(string path)
|
||||
{
|
||||
// Arrange
|
||||
var selector = new IdentityServerJwtPolicySchemeForwardSelector("/Identity", "Local");
|
||||
var ctx = new DefaultHttpContext();
|
||||
ctx.Request.Path = path;
|
||||
|
||||
// Act
|
||||
var scheme = selector.SelectScheme(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(IdentityConstants.ApplicationScheme, scheme);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/api/values")]
|
||||
[InlineData("/connect/openid")]
|
||||
public void SelectScheme_ReturnsTheDefaultScheme_ForOtherPaths(string path)
|
||||
{
|
||||
// Arrange
|
||||
var selector = new IdentityServerJwtPolicySchemeForwardSelector("/Identity", "Local");
|
||||
var ctx = new DefaultHttpContext();
|
||||
ctx.Request.Path = path;
|
||||
|
||||
// Act
|
||||
var scheme = selector.SelectScheme(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Local", scheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// 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 IdentityServer4.Configuration;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class AspNetConventionsConfigureOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Configure_SetsUpIdentityPathsAndCookie()
|
||||
{
|
||||
// Arrange
|
||||
var options = new IdentityServerOptions();
|
||||
var configure = new AspNetConventionsConfigureOptions();
|
||||
|
||||
// Act
|
||||
configure.Configure(options);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Identity/Account/Login", options.UserInteraction.LoginUrl);
|
||||
Assert.Equal("/Identity/Account/Logout", options.UserInteraction.LogoutUrl);
|
||||
Assert.Equal("/Identity/Error", options.UserInteraction.ErrorUrl);
|
||||
Assert.Equal(IdentityConstants.ApplicationScheme, options.Authentication.CookieAuthenticationScheme);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_SetsUpIdentityServerEvents()
|
||||
{
|
||||
// Arrange
|
||||
var options = new IdentityServerOptions();
|
||||
var configure = new AspNetConventionsConfigureOptions();
|
||||
|
||||
// Act
|
||||
configure.Configure(options);
|
||||
|
||||
// Assert
|
||||
Assert.True(options.Events.RaiseErrorEvents);
|
||||
Assert.True(options.Events.RaiseInformationEvents);
|
||||
Assert.True(options.Events.RaiseFailureEvents);
|
||||
Assert.True(options.Events.RaiseSuccessEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// 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.AspNetCore.ApiAuthorization.IdentityServer.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class ConfigureApiResourcesTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetApiResources_ReadsApisFromConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyAPI:Profile"] = "API"
|
||||
}).Build();
|
||||
var localApiDescriptor = new TestLocalApiDescriptor();
|
||||
var configurationLoader = new ConfigureApiResources(
|
||||
configuration,
|
||||
localApiDescriptor,
|
||||
new TestLogger<ConfigureApiResources>());
|
||||
|
||||
// Act
|
||||
var resources = configurationLoader.GetApiResources();
|
||||
|
||||
// Assert
|
||||
var resource = Assert.Single(resources);
|
||||
var scope = Assert.Single(resource.Scopes);
|
||||
Assert.Equal("MyAPI", resource.Name);
|
||||
Assert.Equal("MyAPI", scope.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetApiResources_ReadsApiScopesFromConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var expectedScopes = new[] { "First", "Second", "Third" };
|
||||
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyAPI:Profile"] = "API",
|
||||
["MyAPI:Scopes"] = "First Second Third"
|
||||
}).Build();
|
||||
var localApiDescriptor = new TestLocalApiDescriptor();
|
||||
var configurationLoader = new ConfigureApiResources(
|
||||
configuration,
|
||||
localApiDescriptor,
|
||||
new TestLogger<ConfigureApiResources>());
|
||||
// Act
|
||||
var resources = configurationLoader.GetApiResources();
|
||||
|
||||
// Assert
|
||||
var resource = Assert.Single(resources);
|
||||
Assert.Equal("MyAPI", resource.Name);
|
||||
Assert.NotNull(resource.Scopes);
|
||||
Assert.Equal(3, resource.Scopes.Count);
|
||||
Assert.Equal(expectedScopes, resource.Scopes.Select(s => s.Name).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetApiResources_DetectsLocallyRegisteredApis()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ConfigurationBuilder().Build();
|
||||
var localApiDescriptor = new TestLocalApiDescriptor(new Dictionary<string, ResourceDefinition>
|
||||
{
|
||||
["MyAPI"] = new ResourceDefinition { Profile = ApplicationProfiles.IdentityServerJwt }
|
||||
});
|
||||
var configurationLoader = new ConfigureApiResources(
|
||||
configuration,
|
||||
localApiDescriptor,
|
||||
new TestLogger<ConfigureApiResources>());
|
||||
|
||||
// Act
|
||||
var resources = configurationLoader.GetApiResources();
|
||||
|
||||
// Assert
|
||||
var resource = Assert.Single(resources);
|
||||
var scope = Assert.Single(resource.Scopes);
|
||||
Assert.Equal("MyAPI", resource.Name);
|
||||
Assert.Equal("MyAPI", scope.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_AddsResourcesToExistingResourceList()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyAPI:Profile"] = "API"
|
||||
}).Build();
|
||||
var localApiDescriptor = new TestLocalApiDescriptor();
|
||||
var configurationLoader = new ConfigureApiResources(
|
||||
configuration,
|
||||
localApiDescriptor,
|
||||
new TestLogger<ConfigureApiResources>());
|
||||
|
||||
var options = new ApiAuthorizationOptions();
|
||||
|
||||
// Act
|
||||
configurationLoader.Configure(options);
|
||||
|
||||
// Assert
|
||||
var resource = Assert.Single(options.ApiResources);
|
||||
var scope = Assert.Single(resource.Scopes);
|
||||
Assert.Equal("MyAPI", resource.Name);
|
||||
Assert.Equal("MyAPI", scope.Name);
|
||||
}
|
||||
|
||||
private class TestLocalApiDescriptor : IIdentityServerJwtDescriptor
|
||||
{
|
||||
private readonly IDictionary<string, ResourceDefinition> _definitions;
|
||||
|
||||
public TestLocalApiDescriptor()
|
||||
: this(new Dictionary<string, ResourceDefinition>())
|
||||
{
|
||||
}
|
||||
|
||||
public TestLocalApiDescriptor(IDictionary<string, ResourceDefinition> definitions)
|
||||
{
|
||||
_definitions = definitions;
|
||||
}
|
||||
|
||||
public IDictionary<string, ResourceDefinition> GetResourceDefinitions()
|
||||
{
|
||||
return _definitions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// 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.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
public class ConfigureClientScopesTests
|
||||
{
|
||||
[Fact]
|
||||
public void PostConfigure_AddResourcesScopesToClients()
|
||||
{
|
||||
// Arrange
|
||||
var configureClientScopes = new ConfigureClientScopes(new TestLogger<ConfigureClientScopes>());
|
||||
var options = new ApiAuthorizationOptions();
|
||||
options.Clients.AddRange(
|
||||
ClientBuilder
|
||||
.IdentityServerSPA("TestSPA")
|
||||
.FromConfiguration()
|
||||
.Build(),
|
||||
ClientBuilder
|
||||
.NativeApp("NativeApp")
|
||||
.FromConfiguration()
|
||||
.Build());
|
||||
|
||||
options.ApiResources.AddRange(
|
||||
ApiResourceBuilder.ApiResource("ResourceApi")
|
||||
.FromConfiguration()
|
||||
.AllowAllClients()
|
||||
.Build());
|
||||
|
||||
// Act
|
||||
configureClientScopes.PostConfigure(Extensions.Options.Options.DefaultName, options);
|
||||
|
||||
// Assert
|
||||
foreach (var client in options.Clients)
|
||||
{
|
||||
Assert.Contains("ResourceApi", client.AllowedScopes);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PostConfigure_AddIdentityResourcesScopesToClients()
|
||||
{
|
||||
// Arrange
|
||||
var configureClientScopes = new ConfigureClientScopes(new TestLogger<ConfigureClientScopes>());
|
||||
var options = new ApiAuthorizationOptions();
|
||||
options.Clients.AddRange(
|
||||
ClientBuilder
|
||||
.IdentityServerSPA("TestSPA")
|
||||
.FromConfiguration()
|
||||
.Build(),
|
||||
ClientBuilder
|
||||
.NativeApp("NativeApp")
|
||||
.FromConfiguration()
|
||||
.Build());
|
||||
|
||||
options.ApiResources.AddRange(
|
||||
ApiResourceBuilder.ApiResource("ResourceAPI")
|
||||
.FromConfiguration()
|
||||
.AllowAllClients()
|
||||
.Build());
|
||||
|
||||
// Act
|
||||
configureClientScopes.PostConfigure(Extensions.Options.Options.DefaultName, options);
|
||||
|
||||
// Assert
|
||||
var spaClient = Assert.Single(options.Clients, c => c.ClientId == "TestSPA");
|
||||
Assert.Equal(new[] { "openid", "profile", "ResourceAPI" }, spaClient.AllowedScopes.OrderBy(id => id).ToArray());
|
||||
|
||||
var nativeApp = Assert.Single(options.Clients, c => c.ClientId == "NativeApp");
|
||||
Assert.Equal(new[] { "offline_access", "openid", "profile", "ResourceAPI" }, nativeApp.AllowedScopes.OrderBy(id => id).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
// 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 IdentityServer4;
|
||||
using IdentityServer4.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
public class ConfigureClientsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetClients_DoesNothingIfThereAreNoConfiguredClients()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
|
||||
}).Build();
|
||||
|
||||
var resources = Array.Empty<ApiResource>();
|
||||
var clientLoader = new ConfigureClients(config, new TestLogger<ConfigureClients>());
|
||||
|
||||
// Act
|
||||
var clients = clientLoader.GetClients();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(clients);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClients_ReadsIdentityServerSPAFromConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyClient:Profile"] = "IdentityServerSPA"
|
||||
}).Build();
|
||||
|
||||
var resources = Array.Empty<ApiResource>();
|
||||
var expectedScopes = new[]
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile
|
||||
};
|
||||
|
||||
var clientLoader = new ConfigureClients(config, new TestLogger<ConfigureClients>());
|
||||
|
||||
// Act
|
||||
var clients = clientLoader.GetClients();
|
||||
|
||||
// Assert
|
||||
var client = Assert.Single(clients);
|
||||
Assert.Equal("MyClient", client.ClientId);
|
||||
Assert.Equal("MyClient", client.ClientName);
|
||||
Assert.True(client.AllowAccessTokensViaBrowser);
|
||||
Assert.Equal(new[] { "" }, client.RedirectUris.ToArray());
|
||||
Assert.Equal(new[] { "" }, client.PostLogoutRedirectUris.ToArray());
|
||||
Assert.Empty(client.AllowedCorsOrigins);
|
||||
Assert.False(client.RequireConsent);
|
||||
Assert.Empty(client.ClientSecrets);
|
||||
Assert.Equal(GrantTypes.Implicit.ToArray(), client.AllowedGrantTypes.ToArray());
|
||||
//Assert.Equal(expectedScopes, client.AllowedScopes.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClients_ReadsNativeAppFromConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyClient:Profile"] = "NativeApp"
|
||||
}).Build();
|
||||
|
||||
var resources = Array.Empty<ApiResource>();
|
||||
var clientLoader = new ConfigureClients(config, new TestLogger<ConfigureClients>());
|
||||
var expectedScopes = new[]
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
IdentityServerConstants.StandardScopes.OfflineAccess
|
||||
};
|
||||
|
||||
// Act
|
||||
var clients = clientLoader.GetClients();
|
||||
|
||||
// Assert
|
||||
var client = Assert.Single(clients);
|
||||
Assert.Equal("MyClient", client.ClientId);
|
||||
Assert.Equal("MyClient", client.ClientName);
|
||||
Assert.False(client.AllowAccessTokensViaBrowser);
|
||||
Assert.Equal(new[] { "urn:ietf:wg:oauth:2.0:oob" }, client.RedirectUris.ToArray());
|
||||
Assert.Equal(new[] { "urn:ietf:wg:oauth:2.0:oob" }, client.PostLogoutRedirectUris.ToArray());
|
||||
Assert.Empty(client.AllowedCorsOrigins);
|
||||
Assert.False(client.RequireConsent);
|
||||
Assert.Empty(client.ClientSecrets);
|
||||
Assert.Equal(GrantTypes.Code.ToArray(), client.AllowedGrantTypes.ToArray());
|
||||
Assert.True(client.RequirePkce);
|
||||
Assert.False(client.AllowPlainTextPkce);
|
||||
//Assert.Equal(expectedScopes, client.AllowedScopes.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClients_ReadsSPAFromConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRedirectUrl = "https://www.example.com/authenticate";
|
||||
var expectedLogoutUrl = "https://www.example.com/logout";
|
||||
var expectedAllowedOrigins = "https://www.example.com";
|
||||
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyClient:Profile"] = "SPA",
|
||||
["MyClient:RedirectUri"] = expectedRedirectUrl,
|
||||
["MyClient:LogoutUri"] = expectedLogoutUrl,
|
||||
}).Build();
|
||||
|
||||
var resources = Array.Empty<ApiResource>();
|
||||
var expectedScopes = new[]
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile
|
||||
};
|
||||
|
||||
var clientLoader = new ConfigureClients(config, new TestLogger<ConfigureClients>());
|
||||
|
||||
// Act
|
||||
var clients = clientLoader.GetClients();
|
||||
|
||||
// Assert
|
||||
var client = Assert.Single(clients);
|
||||
Assert.Equal("MyClient", client.ClientId);
|
||||
Assert.Equal("MyClient", client.ClientName);
|
||||
Assert.True(client.AllowAccessTokensViaBrowser);
|
||||
Assert.Equal(new[] { expectedRedirectUrl }, client.RedirectUris.ToArray());
|
||||
Assert.Equal(new[] { expectedLogoutUrl }, client.PostLogoutRedirectUris.ToArray());
|
||||
Assert.Equal(new[] { expectedAllowedOrigins }, client.AllowedCorsOrigins);
|
||||
Assert.False(client.RequireConsent);
|
||||
Assert.Empty(client.ClientSecrets);
|
||||
Assert.Equal(GrantTypes.Implicit.ToArray(), client.AllowedGrantTypes.ToArray());
|
||||
//Assert.Equal(expectedScopes, client.AllowedScopes.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClients_ReadsWebAppFromConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyClient:Profile"] = "IdentityServerSPA"
|
||||
}).Build();
|
||||
|
||||
var resources = Array.Empty<ApiResource>();
|
||||
var expectedScopes = new[]
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile
|
||||
};
|
||||
|
||||
var clientLoader = new ConfigureClients(config, new TestLogger<ConfigureClients>());
|
||||
|
||||
// Act
|
||||
var clients = clientLoader.GetClients();
|
||||
|
||||
// Assert
|
||||
var client = Assert.Single(clients);
|
||||
Assert.Equal("MyClient", client.ClientId);
|
||||
Assert.Equal("MyClient", client.ClientName);
|
||||
Assert.True(client.AllowAccessTokensViaBrowser);
|
||||
Assert.Equal(new[] { "" }, client.RedirectUris.ToArray());
|
||||
Assert.Equal(new[] { "" }, client.PostLogoutRedirectUris.ToArray());
|
||||
Assert.Empty(client.AllowedCorsOrigins);
|
||||
Assert.False(client.RequireConsent);
|
||||
Assert.Empty(client.ClientSecrets);
|
||||
Assert.Equal(GrantTypes.Implicit.ToArray(), client.AllowedGrantTypes.ToArray());
|
||||
//Assert.Equal(expectedScopes, client.AllowedScopes.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_AddsClientsToExistingClientsList()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>
|
||||
{
|
||||
["MyClient:Profile"] = "IdentityServerSPA"
|
||||
}).Build();
|
||||
|
||||
var resources = Array.Empty<ApiResource>();
|
||||
var expectedScopes = new[]
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile
|
||||
};
|
||||
|
||||
var clientLoader = new ConfigureClients(config, new TestLogger<ConfigureClients>());
|
||||
|
||||
var options = new ApiAuthorizationOptions();
|
||||
|
||||
// Act
|
||||
clientLoader.Configure(options);
|
||||
|
||||
// Assert
|
||||
var client = Assert.Single(options.Clients);
|
||||
Assert.Equal("MyClient", client.ClientId);
|
||||
Assert.Equal("MyClient", client.ClientName);
|
||||
Assert.True(client.AllowAccessTokensViaBrowser);
|
||||
Assert.Equal(new[] { "" }, client.RedirectUris.ToArray());
|
||||
Assert.Equal(new[] { "" }, client.PostLogoutRedirectUris.ToArray());
|
||||
Assert.Empty(client.AllowedCorsOrigins);
|
||||
Assert.False(client.RequireConsent);
|
||||
Assert.Empty(client.ClientSecrets);
|
||||
Assert.Equal(GrantTypes.Implicit.ToArray(), client.AllowedGrantTypes.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class ConfigureSigningCredentialsTests
|
||||
{
|
||||
// We need to cast the underlying int value of the EphemeralKeySet to X509KeyStorageFlags
|
||||
// due to the fact that is not part of .NET Standard. This value is only used with non-windows
|
||||
// platforms (all .NET Core) for which the value is defined on the underlying platform.
|
||||
private const X509KeyStorageFlags UnsafeEphemeralKeySet = (X509KeyStorageFlags)32;
|
||||
private static readonly X509KeyStorageFlags DefaultFlags = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
|
||||
UnsafeEphemeralKeySet : (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.PersistKeySet :
|
||||
X509KeyStorageFlags.DefaultKeySet);
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR)]
|
||||
public void Configure_AddsDevelopmentKeyFromConfiguration()
|
||||
{
|
||||
var expectedKeyPath = Path.Combine(Directory.GetCurrentDirectory(), "./testkey.json");
|
||||
try
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string>()
|
||||
{
|
||||
["Type"] = "Development",
|
||||
["FilePath"] = "testkey.json"
|
||||
}).Build();
|
||||
|
||||
var configureSigningCredentials = new ConfigureSigningCredentials(
|
||||
configuration,
|
||||
new TestLogger<ConfigureSigningCredentials>());
|
||||
|
||||
var options = new ApiAuthorizationOptions();
|
||||
|
||||
// Act
|
||||
configureSigningCredentials.Configure(options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(options);
|
||||
Assert.True(File.Exists(expectedKeyPath));
|
||||
Assert.NotNull(options.SigningCredential);
|
||||
Assert.Equal("Development", options.SigningCredential.Kid);
|
||||
Assert.IsType<RsaSecurityKey>(options.SigningCredential.Key);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(expectedKeyPath))
|
||||
{
|
||||
File.Delete(expectedKeyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_LoadsPfxCertificateCredentialFromConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string>()
|
||||
{
|
||||
["Type"] = "File",
|
||||
["FilePath"] = "test.pfx",
|
||||
["Password"] = "aspnetcore"
|
||||
}).Build();
|
||||
|
||||
var configureSigningCredentials = new ConfigureSigningCredentials(
|
||||
configuration,
|
||||
new TestLogger<ConfigureSigningCredentials>());
|
||||
|
||||
var options = new ApiAuthorizationOptions();
|
||||
|
||||
// Act
|
||||
configureSigningCredentials.Configure(options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(options);
|
||||
Assert.NotNull(options.SigningCredential);
|
||||
var key = Assert.IsType<X509SecurityKey>(options.SigningCredential.Key);
|
||||
Assert.NotNull(key.Certificate);
|
||||
Assert.Equal("AC8FDF4BD4C10841BD24DC88D983225D10B43BB2", key.Certificate.Thumbprint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_LoadsCertificateStoreCertificateCredentialFromConfiguration()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Arrange
|
||||
var x509Certificate = new X509Certificate2("test.pfx", "aspnetcore", DefaultFlags);
|
||||
SetupTestCertificate(x509Certificate);
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string>()
|
||||
{
|
||||
["Type"] = "Store",
|
||||
["StoreLocation"] = "CurrentUser",
|
||||
["StoreName"] = "My",
|
||||
["Name"] = "CN=Test"
|
||||
}).Build();
|
||||
|
||||
var configureSigningCredentials = new ConfigureSigningCredentials(
|
||||
configuration,
|
||||
new TestLogger<ConfigureSigningCredentials>());
|
||||
|
||||
var options = new ApiAuthorizationOptions();
|
||||
|
||||
// Act
|
||||
configureSigningCredentials.Configure(options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(options);
|
||||
Assert.NotNull(options.SigningCredential);
|
||||
var key = Assert.IsType<X509SecurityKey>(options.SigningCredential.Key);
|
||||
Assert.NotNull(key.Certificate);
|
||||
Assert.Equal("AC8FDF4BD4C10841BD24DC88D983225D10B43BB2", key.Certificate.Thumbprint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupTestCertificate();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupTestCertificate()
|
||||
{
|
||||
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
var certificates = store
|
||||
.Certificates
|
||||
.Find(X509FindType.FindByThumbprint, "1646CFBEE354788D7116DF86EFC35C0075A9C05D", validOnly: false);
|
||||
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
store.Certificates.Remove(certificate);
|
||||
}
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
certificate.Dispose();
|
||||
}
|
||||
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetupTestCertificate(X509Certificate2 x509Certificate)
|
||||
{
|
||||
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
var certificates = store
|
||||
.Certificates
|
||||
.Find(X509FindType.FindByThumbprint, "AC8FDF4BD4C10841BD24DC88D983225D10B43BB2", validOnly: false);
|
||||
if (certificates.Count == 0)
|
||||
{
|
||||
store.Add(x509Certificate);
|
||||
}
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
certificate.Dispose();
|
||||
}
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// 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.AspNetCore.Hosting;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
public class LocalApiDescriptorTests
|
||||
{
|
||||
[Fact]
|
||||
public void LocalApiDescriptor_DefinesApiResources()
|
||||
{
|
||||
// Arrange
|
||||
var environment = new Mock<IHostingEnvironment>();
|
||||
environment.SetupGet(e => e.ApplicationName).Returns("Test");
|
||||
var descriptor = new IdentityServerJwtDescriptor(environment.Object);
|
||||
|
||||
// Act
|
||||
var resources = descriptor.GetResourceDefinitions();
|
||||
|
||||
// Assert
|
||||
var apiResource = Assert.Single(resources);
|
||||
Assert.Equal("TestAPI", apiResource.Key);
|
||||
Assert.NotNull(apiResource.Value);
|
||||
Assert.Equal(ApplicationProfiles.IdentityServerJwt, apiResource.Value.Profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
|
||||
{
|
||||
public class SigningKeysLoaderTests
|
||||
{
|
||||
// We need to cast the underlying int value of the EphemeralKeySet to X509KeyStorageFlags
|
||||
// due to the fact that is not part of .NET Standard. This value is only used with non-windows
|
||||
// platforms (all .NET Core) for which the value is defined on the underlying platform.
|
||||
private const X509KeyStorageFlags UnsafeEphemeralKeySet = (X509KeyStorageFlags)32;
|
||||
private static readonly X509KeyStorageFlags DefaultFlags = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
|
||||
UnsafeEphemeralKeySet : (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.PersistKeySet :
|
||||
X509KeyStorageFlags.DefaultKeySet);
|
||||
|
||||
[Fact]
|
||||
public void LoadFromFile_ThrowsIfFileDoesNotExist()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => SigningKeysLoader.LoadFromFile("./nonexisting.pfx", "", DefaultFlags));
|
||||
Assert.Equal($"There was an error loading the certificate. The file './nonexisting.pfx' was not found.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadFromFile_ThrowsIfPasswordIsNull()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => SigningKeysLoader.LoadFromFile("test.pfx", null, DefaultFlags));
|
||||
Assert.Equal("There was an error loading the certificate. No password was provided.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadFromFile_ThrowsIfPasswordIsIncorrect()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => SigningKeysLoader.LoadFromFile("test.pfx", "incorrect", DefaultFlags));
|
||||
Assert.Equal(
|
||||
$"There was an error loading the certificate. Either the password is incorrect or the process does not have permisions to store the key in the Keyset '{DefaultFlags}'",
|
||||
exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void LoadFromStoreCert_ThrowsIfThereIsNoCertificateAvailable()
|
||||
{
|
||||
// Arrange
|
||||
var time = new DateTimeOffset(2018, 09, 25, 12, 0, 0, TimeSpan.Zero);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => SigningKeysLoader.LoadFromStoreCert("Invalid", "My", StoreLocation.CurrentUser, time));
|
||||
Assert.Equal("Couldn't find a valid certificate with subject 'Invalid' on the 'CurrentUser\\My'", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void LoadFromStoreCert_SkipsCertificatesNotYetValid()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetupCertificates("./current.pfx", "./future.pfx");
|
||||
// Arrange
|
||||
var time = new DateTimeOffset(2018, 10, 29, 12, 0, 0, TimeSpan.Zero);
|
||||
|
||||
// Act
|
||||
var certificate = SigningKeysLoader.LoadFromStoreCert("CN=SigningKeysLoaderTest", "My", StoreLocation.CurrentUser, time);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(certificate);
|
||||
Assert.Equal("C54CD513088C23EC2AFD256874CC6C0F81EA9D5E", certificate.Thumbprint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupCertificates();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void LoadFromStoreCert_PrefersCertificatesCloserToExpirationDate()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetupCertificates("./current.pfx", "./future.pfx");
|
||||
// Arrange
|
||||
var time = new DateTimeOffset(2020, 10, 29, 12, 0, 0, TimeSpan.Zero);
|
||||
|
||||
// Act
|
||||
var certificate = SigningKeysLoader.LoadFromStoreCert("CN=SigningKeysLoaderTest", "My", StoreLocation.CurrentUser, time);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(certificate);
|
||||
Assert.Equal("C54CD513088C23EC2AFD256874CC6C0F81EA9D5E", certificate.Thumbprint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupCertificates();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void LoadFromStoreCert_SkipsExpiredCertificates()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetupCertificates("./expired.pfx", "./current.pfx", "./future.pfx");
|
||||
// Arrange
|
||||
var time = new DateTimeOffset(2024, 01, 01, 12, 0, 0, TimeSpan.Zero);
|
||||
|
||||
// Act
|
||||
var certificate = SigningKeysLoader.LoadFromStoreCert("CN=SigningKeysLoaderTest", "My", StoreLocation.CurrentUser, time);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(certificate);
|
||||
Assert.Equal("35840DD366107B89D2885A6B4F42CCBBAE6BA8E3", certificate.Thumbprint);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupCertificates();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void LoadDevelopment_ThrowsIfKeyDoesNotExist()
|
||||
{
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => SigningKeysLoader.LoadDevelopment("c:/inexistent.json", createIfMissing: false));
|
||||
Assert.Equal("Couldn't find the file 'c:/inexistent.json' and creation of a development key was not requested.", exception.Message);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR)]
|
||||
public static void LoadDevelopment_CreatesKeyIfItDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var path = "./tempkeyfolder/tempkey.json";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
// Act
|
||||
var key = SigningKeysLoader.LoadDevelopment(path, createIfMissing: true);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(key);
|
||||
Assert.True(File.Exists(path));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR)]
|
||||
public static void LoadDevelopment_ReusesKeyIfExists()
|
||||
{
|
||||
// Arrange
|
||||
var path = "./tempkeyfolder/existing.json";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
var existingKey = SigningKeysLoader.LoadDevelopment(path, createIfMissing: true);
|
||||
var existingParameters = existingKey.ExportParameters(includePrivateParameters: true);
|
||||
|
||||
// Act
|
||||
var currentKey = SigningKeysLoader.LoadDevelopment(path, createIfMissing: true);
|
||||
var currentParameters = currentKey.ExportParameters(includePrivateParameters: true);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(currentKey);
|
||||
Assert.Equal(existingParameters.P, currentParameters.P);
|
||||
Assert.Equal(existingParameters.Q, currentParameters.Q);
|
||||
}
|
||||
|
||||
private static void CleanupCertificates()
|
||||
{
|
||||
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
store.RemoveRange(store.Certificates.Find(X509FindType.FindBySubjectName, "CN=SigningKeysLoaderTest", validOnly: false));
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetupCertificates(params string[] certificateFiles)
|
||||
{
|
||||
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
foreach (var certificate in certificateFiles)
|
||||
{
|
||||
var cert = new X509Certificate2(certificate, "aspnetcore", DefaultFlags);
|
||||
if (!(store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, validOnly: false).Count > 0))
|
||||
{
|
||||
store.Add(cert);
|
||||
}
|
||||
}
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 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.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class AbsoluteUrlFactoryTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetAbsoluteUrl_ReturnsNull_ForInvalidData()
|
||||
{
|
||||
// Arrange
|
||||
var accessor = new Mock<IHttpContextAccessor>();
|
||||
var factory = new AbsoluteUrlFactory(accessor.Object);
|
||||
var path = "something|invalid";
|
||||
|
||||
// Act
|
||||
var result = factory.GetAbsoluteUrl(path);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAbsoluteUrl_ReturnsUnmodifiedUrl_ForAbsoluteUrls()
|
||||
{
|
||||
// Arrange
|
||||
var accessor = new Mock<IHttpContextAccessor>();
|
||||
var factory = new AbsoluteUrlFactory(accessor.Object);
|
||||
var path = "https://localhost:5001/authenticate";
|
||||
|
||||
// Act
|
||||
var result = factory.GetAbsoluteUrl(path);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(path, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAbsoluteUrl_ReturnsContextBasedAbsoluteUrl_ForRelativeUrls()
|
||||
{
|
||||
// Arrange
|
||||
var ctx = new DefaultHttpContext();
|
||||
ctx.Request.Scheme = "https";
|
||||
ctx.Request.Host = new HostString("localhost:5001");
|
||||
ctx.Request.PathBase = "/virtual";
|
||||
|
||||
var accessor = new Mock<IHttpContextAccessor>();
|
||||
accessor.SetupGet(c => c.HttpContext).Returns(ctx);
|
||||
var factory = new AbsoluteUrlFactory(accessor.Object);
|
||||
var path = "/authenticate";
|
||||
|
||||
// Act
|
||||
var result = factory.GetAbsoluteUrl(path);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("https://localhost:5001/virtual/authenticate", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Specialized;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityServer4.Configuration;
|
||||
using IdentityServer4.Endpoints.Results;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class AutoRedirectEndSessionEndpointTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task AutoRedirectSessionEndpoint_AutoRedirectsValidatedPostLogoutRequests_ToApplicationsWithProfiles()
|
||||
{
|
||||
// Arrange
|
||||
var session = new Mock<IUserSession>();
|
||||
session.Setup(s => s.GetUserAsync()).ReturnsAsync(new ClaimsPrincipal());
|
||||
|
||||
var endSessionValidator = new Mock<IEndSessionRequestValidator>();
|
||||
endSessionValidator.Setup(esv => esv.ValidateAsync(It.IsAny<NameValueCollection>(), It.IsAny<ClaimsPrincipal>()))
|
||||
.ReturnsAsync(new EndSessionValidationResult()
|
||||
{
|
||||
IsError = false,
|
||||
ValidatedRequest = new ValidatedEndSessionRequest()
|
||||
{
|
||||
Client = ClientBuilder.IdentityServerSPA("MySPA").Build(),
|
||||
PostLogOutUri = "https://www.example.com/logout"
|
||||
}
|
||||
});
|
||||
|
||||
var identityServerOptions = Options.Create(new IdentityServerOptions());
|
||||
identityServerOptions.Value.Authentication.CookieAuthenticationScheme = IdentityConstants.ApplicationScheme;
|
||||
identityServerOptions.Value.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
|
||||
identityServerOptions.Value.UserInteraction.ErrorUrl = "/Identity/Error";
|
||||
|
||||
var endpoint = new AutoRedirectEndSessionEndpoint(new TestLogger<AutoRedirectEndSessionEndpoint>(), endSessionValidator.Object, identityServerOptions, session.Object);
|
||||
var ctx = new DefaultHttpContext();
|
||||
SetupRequestServices(ctx);
|
||||
ctx.Request.Method = HttpMethods.Post;
|
||||
ctx.Request.ContentType = "application/x-www-form-urlencoded";
|
||||
|
||||
// Act
|
||||
var response = await endpoint.ProcessAsync(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
var redirect = Assert.IsType<AutoRedirectEndSessionEndpoint.RedirectResult>(response);
|
||||
Assert.Equal("https://www.example.com/logout", redirect.Url);
|
||||
await response.ExecuteAsync(ctx);
|
||||
Assert.Equal(StatusCodes.Status302Found, ctx.Response.StatusCode);
|
||||
Assert.Equal("https://www.example.com/logout", ctx.Response.Headers[HeaderNames.Location]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoRedirectSessionEndpoint_AutoRedirectsValidatedGetLogoutRequests_ToApplicationsWithProfiles()
|
||||
{
|
||||
// Arrange
|
||||
var session = new Mock<IUserSession>();
|
||||
session.Setup(s => s.GetUserAsync()).ReturnsAsync(new ClaimsPrincipal());
|
||||
|
||||
var endSessionValidator = new Mock<IEndSessionRequestValidator>();
|
||||
endSessionValidator.Setup(esv => esv.ValidateAsync(It.IsAny<NameValueCollection>(), It.IsAny<ClaimsPrincipal>()))
|
||||
.ReturnsAsync(new EndSessionValidationResult()
|
||||
{
|
||||
IsError = false,
|
||||
ValidatedRequest = new ValidatedEndSessionRequest()
|
||||
{
|
||||
Client = ClientBuilder.IdentityServerSPA("MySPA").Build(),
|
||||
PostLogOutUri = "https://www.example.com/logout"
|
||||
}
|
||||
});
|
||||
|
||||
var identityServerOptions = Options.Create(new IdentityServerOptions());
|
||||
identityServerOptions.Value.Authentication.CookieAuthenticationScheme = IdentityConstants.ApplicationScheme;
|
||||
identityServerOptions.Value.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
|
||||
identityServerOptions.Value.UserInteraction.ErrorUrl = "/Identity/Error";
|
||||
|
||||
var endpoint = new AutoRedirectEndSessionEndpoint(new TestLogger<AutoRedirectEndSessionEndpoint>(), endSessionValidator.Object, identityServerOptions, session.Object);
|
||||
var ctx = new DefaultHttpContext();
|
||||
SetupRequestServices(ctx);
|
||||
ctx.Request.Method = HttpMethods.Get;
|
||||
|
||||
// Act
|
||||
var response = await endpoint.ProcessAsync(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
var redirect = Assert.IsType<AutoRedirectEndSessionEndpoint.RedirectResult>(response);
|
||||
Assert.Equal("https://www.example.com/logout", redirect.Url);
|
||||
await response.ExecuteAsync(ctx);
|
||||
Assert.Equal(StatusCodes.Status302Found, ctx.Response.StatusCode);
|
||||
Assert.Equal("https://www.example.com/logout", ctx.Response.Headers[HeaderNames.Location]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoRedirectSessionEndpoint_RedirectsToError_WhenValidationFails()
|
||||
{
|
||||
// Arrange
|
||||
var session = new Mock<IUserSession>();
|
||||
session.Setup(s => s.GetUserAsync()).ReturnsAsync(new ClaimsPrincipal());
|
||||
|
||||
var endSessionValidator = new Mock<IEndSessionRequestValidator>();
|
||||
endSessionValidator.Setup(esv => esv.ValidateAsync(It.IsAny<NameValueCollection>(), It.IsAny<ClaimsPrincipal>()))
|
||||
.ReturnsAsync(new EndSessionValidationResult()
|
||||
{
|
||||
IsError = true,
|
||||
Error = "SomeError"
|
||||
});
|
||||
|
||||
var identityServerOptions = Options.Create(new IdentityServerOptions());
|
||||
identityServerOptions.Value.Authentication.CookieAuthenticationScheme = IdentityConstants.ApplicationScheme;
|
||||
identityServerOptions.Value.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
|
||||
identityServerOptions.Value.UserInteraction.ErrorUrl = "/Identity/Error";
|
||||
|
||||
var endpoint = new AutoRedirectEndSessionEndpoint(new TestLogger<AutoRedirectEndSessionEndpoint>(), endSessionValidator.Object, identityServerOptions, session.Object);
|
||||
var ctx = new DefaultHttpContext();
|
||||
SetupRequestServices(ctx);
|
||||
ctx.Request.Method = HttpMethods.Post;
|
||||
ctx.Request.ContentType = "application/x-www-form-urlencoded";
|
||||
|
||||
// Act
|
||||
var response = await endpoint.ProcessAsync(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
var redirect = Assert.IsType<AutoRedirectEndSessionEndpoint.RedirectResult>(response);
|
||||
Assert.Equal("/Identity/Error", redirect.Url);
|
||||
await response.ExecuteAsync(ctx);
|
||||
Assert.Equal(StatusCodes.Status302Found, ctx.Response.StatusCode);
|
||||
Assert.Equal("/Identity/Error", ctx.Response.Headers[HeaderNames.Location]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoRedirectSessionEndpoint_RedirectsToLogoutUri_WhenClientDoesntHaveAProfile()
|
||||
{
|
||||
// Arrange
|
||||
var session = new Mock<IUserSession>();
|
||||
session.Setup(s => s.GetUserAsync()).ReturnsAsync(new ClaimsPrincipal());
|
||||
|
||||
var endSessionValidator = new Mock<IEndSessionRequestValidator>();
|
||||
endSessionValidator.Setup(esv => esv.ValidateAsync(It.IsAny<NameValueCollection>(), It.IsAny<ClaimsPrincipal>()))
|
||||
.ReturnsAsync(new EndSessionValidationResult()
|
||||
{
|
||||
IsError = false,
|
||||
ValidatedRequest = new ValidatedEndSessionRequest()
|
||||
{
|
||||
Client = new Client()
|
||||
}
|
||||
});
|
||||
|
||||
var identityServerOptions = Options.Create(new IdentityServerOptions());
|
||||
identityServerOptions.Value.Authentication.CookieAuthenticationScheme = IdentityConstants.ApplicationScheme;
|
||||
identityServerOptions.Value.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
|
||||
identityServerOptions.Value.UserInteraction.ErrorUrl = "/Identity/Error";
|
||||
|
||||
var endpoint = new AutoRedirectEndSessionEndpoint(new TestLogger<AutoRedirectEndSessionEndpoint>(), endSessionValidator.Object, identityServerOptions, session.Object);
|
||||
var ctx = new DefaultHttpContext();
|
||||
SetupRequestServices(ctx);
|
||||
ctx.Request.Method = HttpMethods.Post;
|
||||
ctx.Request.ContentType = "application/x-www-form-urlencoded";
|
||||
|
||||
// Act
|
||||
var response = await endpoint.ProcessAsync(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
var redirect = Assert.IsType<AutoRedirectEndSessionEndpoint.RedirectResult>(response);
|
||||
Assert.Equal("/Identity/Account/Logout", redirect.Url);
|
||||
await response.ExecuteAsync(ctx);
|
||||
Assert.Equal(StatusCodes.Status302Found, ctx.Response.StatusCode);
|
||||
Assert.Equal("/Identity/Account/Logout", ctx.Response.Headers[HeaderNames.Location]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoRedirectSessionEndpoint_RedirectsToLogoutUri_WhenTheValidationRequestDoesNotContainAClient()
|
||||
{
|
||||
// Arrange
|
||||
var session = new Mock<IUserSession>();
|
||||
session.Setup(s => s.GetUserAsync()).ReturnsAsync(new ClaimsPrincipal());
|
||||
|
||||
var endSessionValidator = new Mock<IEndSessionRequestValidator>();
|
||||
endSessionValidator.Setup(esv => esv.ValidateAsync(It.IsAny<NameValueCollection>(), It.IsAny<ClaimsPrincipal>()))
|
||||
.ReturnsAsync(new EndSessionValidationResult()
|
||||
{
|
||||
IsError = false,
|
||||
ValidatedRequest = new ValidatedEndSessionRequest()
|
||||
});
|
||||
|
||||
var identityServerOptions = Options.Create(new IdentityServerOptions());
|
||||
identityServerOptions.Value.Authentication.CookieAuthenticationScheme = IdentityConstants.ApplicationScheme;
|
||||
identityServerOptions.Value.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
|
||||
identityServerOptions.Value.UserInteraction.ErrorUrl = "/Identity/Error";
|
||||
|
||||
var endpoint = new AutoRedirectEndSessionEndpoint(new TestLogger<AutoRedirectEndSessionEndpoint>(), endSessionValidator.Object, identityServerOptions, session.Object);
|
||||
var ctx = new DefaultHttpContext();
|
||||
SetupRequestServices(ctx);
|
||||
ctx.Request.Method = HttpMethods.Post;
|
||||
ctx.Request.ContentType = "application/x-www-form-urlencoded";
|
||||
|
||||
// Act
|
||||
var response = await endpoint.ProcessAsync(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
var redirect = Assert.IsType<AutoRedirectEndSessionEndpoint.RedirectResult>(response);
|
||||
Assert.Equal("/Identity/Account/Logout", redirect.Url);
|
||||
await response.ExecuteAsync(ctx);
|
||||
Assert.Equal(StatusCodes.Status302Found, ctx.Response.StatusCode);
|
||||
Assert.Equal("/Identity/Account/Logout", ctx.Response.Headers[HeaderNames.Location]);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("PUT")]
|
||||
[InlineData("DELETE")]
|
||||
[InlineData("PATCH")]
|
||||
[InlineData("OPTIONS")]
|
||||
[InlineData("HEAD")]
|
||||
public async Task AutoRedirectSessionEndpoint_ReturnsBadRequest_WhenMethodIsNotPostOrGet(string method)
|
||||
{
|
||||
// Arrange
|
||||
var session = new Mock<IUserSession>();
|
||||
var endSessionValidator = new Mock<IEndSessionRequestValidator>();
|
||||
var identityServerOptions = Options.Create(new IdentityServerOptions());
|
||||
|
||||
var endpoint = new AutoRedirectEndSessionEndpoint(new TestLogger<AutoRedirectEndSessionEndpoint>(), endSessionValidator.Object, identityServerOptions, session.Object);
|
||||
var ctx = new DefaultHttpContext();
|
||||
SetupRequestServices(ctx);
|
||||
ctx.Request.Method = method;
|
||||
|
||||
// Act
|
||||
var response = await endpoint.ProcessAsync(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
var statusCode = Assert.IsType<StatusCodeResult>(response);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, statusCode.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoRedirectSessionEndpoint_ReturnsBadRequest_WhenCannotReadTheRequestBody()
|
||||
{
|
||||
// Arrange
|
||||
var session = new Mock<IUserSession>();
|
||||
var endSessionValidator = new Mock<IEndSessionRequestValidator>();
|
||||
var identityServerOptions = Options.Create(new IdentityServerOptions());
|
||||
|
||||
var endpoint = new AutoRedirectEndSessionEndpoint(new TestLogger<AutoRedirectEndSessionEndpoint>(), endSessionValidator.Object, identityServerOptions, session.Object);
|
||||
var ctx = new DefaultHttpContext();
|
||||
SetupRequestServices(ctx);
|
||||
ctx.Request.Method = HttpMethods.Post;
|
||||
|
||||
// Act & Assert
|
||||
var response = await endpoint.ProcessAsync(ctx);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(response);
|
||||
var statusCode = Assert.IsType<StatusCodeResult>(response);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, statusCode.StatusCode);
|
||||
}
|
||||
|
||||
private void SetupRequestServices(DefaultHttpContext ctx)
|
||||
{
|
||||
var collection = new ServiceCollection();
|
||||
var authService = new Mock<IAuthenticationService>();
|
||||
authService.Setup(service => service.SignOutAsync(It.IsAny<HttpContext>(), It.IsAny<string>(), It.IsAny<AuthenticationProperties>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
collection.AddSingleton(authService.Object);
|
||||
ctx.RequestServices = collection.BuildServiceProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
// 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 IdentityServer4.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class RelativeRedirectUriValidatorTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task IsRedirectUriValidAsync_ConvertsRelativeUrisIntoAbsoluteUris_ForLocalSPAsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRelativeUri = "/authenticate";
|
||||
var providedFullUrl = "https://localhost:5001/authenticate";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
RedirectUris = { expectedRelativeUri },
|
||||
Properties = new Dictionary<string, string>
|
||||
{
|
||||
[ApplicationProfilesPropertyNames.Profile] = ApplicationProfiles.IdentityServerSPA,
|
||||
}
|
||||
};
|
||||
var factory = new TestUrlFactory(expectedRelativeUri, providedFullUrl);
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.True(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IsRedirectUriValidAsync_RejectsIfTheRelativeUriIsNotRegistered_ForLocalSPAsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRelativeUri = "/authenticate";
|
||||
var providedFullUrl = "https://localhost:5001/notregistered";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
RedirectUris = { expectedRelativeUri },
|
||||
Properties = new Dictionary<string, string>
|
||||
{
|
||||
[ApplicationProfilesPropertyNames.Profile] = ApplicationProfiles.IdentityServerSPA,
|
||||
}
|
||||
};
|
||||
var factory = new TestUrlFactory();
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.False(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IsRedirectUriValidAsync_CallsBaseAndSucceeds_ForValidRedirectUrisOnRegularClients()
|
||||
{
|
||||
// Arrange
|
||||
var providedFullUrl = "https://localhost:5001/authenticate";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
RedirectUris = { "https://localhost:5001/authenticate" },
|
||||
};
|
||||
|
||||
var factory = new TestUrlFactory();
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.True(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IsRedirectUriValidAsync_CallsBaseAndFails_ForInvalidRedirectUrisOnRegularClients()
|
||||
{
|
||||
// Arrange
|
||||
var providedFullUrl = "https://localhost:5001/notregistered";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
RedirectUris = { "https://localhost:5001/authenticate" },
|
||||
};
|
||||
|
||||
var factory = new TestUrlFactory();
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.False(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IsPostLogoutRedirectUriValidAsync_ConvertsRelativeUrisIntoAbsoluteUris_ForLocalSPAsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRelativeUri = "/logout";
|
||||
var providedFullUrl = "https://localhost:5001/logout";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
PostLogoutRedirectUris = { expectedRelativeUri },
|
||||
Properties = new Dictionary<string, string>
|
||||
{
|
||||
[ApplicationProfilesPropertyNames.Profile] = ApplicationProfiles.IdentityServerSPA,
|
||||
}
|
||||
};
|
||||
var factory = new TestUrlFactory(expectedRelativeUri,providedFullUrl);
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsPostLogoutRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.True(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IsPostLogoutRedirectUriValidAsync_RejectsIfTheRelativeUriIsNotRegistered_ForLocalSPAsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var expectedRelativeUri = "/logout";
|
||||
var providedFullUrl = "https://localhost:5001/notregistered";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
PostLogoutRedirectUris = { expectedRelativeUri },
|
||||
Properties = new Dictionary<string, string>
|
||||
{
|
||||
[ApplicationProfilesPropertyNames.Profile] = ApplicationProfiles.IdentityServerSPA,
|
||||
}
|
||||
};
|
||||
var factory = new TestUrlFactory();
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsPostLogoutRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.False(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IsPostLogoutRedirectUriValidAsync_CallsBaseAndSucceeds_ForValidPostLogoutRedirectUrisOnRegularClients()
|
||||
{
|
||||
// Arrange
|
||||
var providedFullUrl = "https://localhost:5001/logout";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
PostLogoutRedirectUris = { "https://localhost:5001/logout" },
|
||||
};
|
||||
|
||||
var factory = new TestUrlFactory();
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsPostLogoutRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.True(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IsPostLogoutRedirectUriValidAsync_CallsBaseAndFails_ForInvalidPostLogoutRedirectUrisOnRegularClients()
|
||||
{
|
||||
// Arrange
|
||||
var providedFullUrl = "https://localhost:5001/notregistered";
|
||||
var expectedClient = new Client
|
||||
{
|
||||
PostLogoutRedirectUris = { "https://localhost:5001/logout" },
|
||||
};
|
||||
|
||||
var factory = new TestUrlFactory();
|
||||
var redirectUriValidator = new RelativeRedirectUriValidator(factory);
|
||||
|
||||
// Act
|
||||
var validator = await redirectUriValidator.IsPostLogoutRedirectUriValidAsync(providedFullUrl, expectedClient);
|
||||
|
||||
// Assert
|
||||
Assert.False(validator);
|
||||
}
|
||||
|
||||
private class TestUrlFactory : IAbsoluteUrlFactory
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly string _result;
|
||||
|
||||
public TestUrlFactory()
|
||||
{
|
||||
}
|
||||
|
||||
public TestUrlFactory(string path, string result)
|
||||
{
|
||||
_path = path;
|
||||
_result = result;
|
||||
}
|
||||
|
||||
public string GetAbsoluteUrl(string path)
|
||||
{
|
||||
if (_path == null || _result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_path == path)
|
||||
{
|
||||
return _result;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public string GetAbsoluteUrl(HttpContext context, string path)
|
||||
{
|
||||
return GetAbsoluteUrl(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\ApiAuth.IS\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="current.pfx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="expired.pfx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="future.pfx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="test.pfx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
public class ClientParametersTagHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void ProcessThrows_WhenClientIdNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var clientRequestParametersProvider = new Mock<IClientRequestParametersProvider>();
|
||||
clientRequestParametersProvider.Setup(c => c.GetClientParameters(It.IsAny<HttpContext>(), It.IsAny<string>())).Returns<IDictionary<string, string>>(null);
|
||||
var tagHelperContext = new TagHelperContext(new TagHelperAttributeList(), new Dictionary<object, object>(), "id");
|
||||
var tagHelperOutput = new TagHelperOutput("meta", new TagHelperAttributeList(), (something, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
var tagHelper = new ClientParametersTagHelper(clientRequestParametersProvider.Object);
|
||||
tagHelper.ClientId = "id";
|
||||
tagHelper.ViewContext = new ViewContext() { HttpContext = new DefaultHttpContext() };
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => tagHelper.Process(tagHelperContext, tagHelperOutput));
|
||||
Assert.Equal("Parameters for client 'id' not found.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessAddsAttributesToTag_WhenClientIdFound()
|
||||
{
|
||||
// Arrange
|
||||
var clientRequestParametersProvider = new Mock<IClientRequestParametersProvider>();
|
||||
clientRequestParametersProvider.Setup(c => c.GetClientParameters(It.IsAny<HttpContext>(), It.IsAny<string>()))
|
||||
.Returns(new Dictionary<string, string>()
|
||||
{
|
||||
["client_id"] = "SampleApp",
|
||||
["scope"] = "SampleAPI openid",
|
||||
["redirect_uri"] = "https://www.example.com/auth-callback",
|
||||
["response_type"] = "id_token code"
|
||||
});
|
||||
|
||||
var tagHelperContext = new TagHelperContext(new TagHelperAttributeList(), new Dictionary<object, object>(), "id");
|
||||
var tagHelperOutput = new TagHelperOutput("meta", new TagHelperAttributeList(), (something, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
var tagHelper = new ClientParametersTagHelper(clientRequestParametersProvider.Object);
|
||||
tagHelper.ViewContext = new ViewContext() { HttpContext = new DefaultHttpContext() };
|
||||
|
||||
// Act
|
||||
tagHelper.Process(tagHelperContext, tagHelperOutput);
|
||||
|
||||
// Assert
|
||||
Assert.Contains(tagHelperOutput.Attributes, th => th.Name == "data-client_id" && th.Value is string value && value == "SampleApp");
|
||||
Assert.Contains(tagHelperOutput.Attributes, th => th.Name == "data-scope" && th.Value is string value && value == "SampleAPI openid");
|
||||
Assert.Contains(tagHelperOutput.Attributes, th => th.Name == "data-redirect_uri" && th.Value is string value && value == "https://www.example.com/auth-callback");
|
||||
Assert.Contains(tagHelperOutput.Attributes, th => th.Name == "data-response_type" && th.Value is string value && value == "id_token code");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 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.Logging;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
|
||||
{
|
||||
internal class TestLogger<TCategory> : ILogger<TCategory>, IDisposable
|
||||
{
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue