Add support for exposing identity as a service

This commit is contained in:
Javier Calvarro Nelson 2017-05-10 13:26:19 -07:00
parent ba64cd0fb0
commit c7567f9f74
218 changed files with 16477 additions and 1 deletions

View File

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26123.0
VisualStudioVersion = 15.0.26507.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}"
EndProject
@ -26,6 +26,34 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.Identity.A
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Specification.Tests", "src\Microsoft.AspNetCore.Identity.Specification.Tests\Microsoft.AspNetCore.Identity.Specification.Tests.csproj", "{5608E828-DD54-4E2A-B73C-FC22268BE797}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Diagnostics.Identity.Service", "src\Microsoft.AspNetCore.Diagnostics.Identity.Service\Microsoft.AspNetCore.Diagnostics.Identity.Service.csproj", "{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service", "src\Microsoft.AspNetCore.Identity.Service\Microsoft.AspNetCore.Identity.Service.csproj", "{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.Abstractions", "src\Microsoft.AspNetCore.Identity.Service.Abstractions\Microsoft.AspNetCore.Identity.Service.Abstractions.csproj", "{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.Core", "src\Microsoft.AspNetCore.Identity.Service.Core\Microsoft.AspNetCore.Identity.Service.Core.csproj", "{590697C1-EA60-4412-8A21-4EF35142381F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore", "src\Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore\Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.csproj", "{CD360545-3395-4C44-AD27-C32EECDD9572}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.IntegratedWebClient", "src\Microsoft.AspNetCore.Identity.Service.IntegratedWebClient\Microsoft.AspNetCore.Identity.Service.IntegratedWebClient.csproj", "{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.Mvc", "src\Microsoft.AspNetCore.Identity.Service.Mvc\Microsoft.AspNetCore.Identity.Service.Mvc.csproj", "{B3AE446B-859B-4C2C-98FD-A084C854941E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.Abstractions.Test", "test\Microsoft.AspNetCore.Identity.Service.Abstractions.Test\Microsoft.AspNetCore.Identity.Service.Abstractions.Test.csproj", "{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.Core.Test", "test\Microsoft.AspNetCore.Identity.Service.Core.Test\Microsoft.AspNetCore.Identity.Service.Core.Test.csproj", "{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.Test", "test\Microsoft.AspNetCore.Identity.Service.Test\Microsoft.AspNetCore.Identity.Service.Test.csproj", "{204163F9-E9BB-4940-9659-77F617C00D97}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.Specification.Tests", "src\Microsoft.AspNetCore.Identity.Service.Specification.Tests\Microsoft.AspNetCore.Identity.Service.Specification.Tests.csproj", "{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.InMemory.Test", "test\Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.InMemory.Test\Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.InMemory.Test.csproj", "{7423EB30-FFE9-4707-A44B-571E89A7CA15}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.Test", "test\Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.Test\Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.Test.csproj", "{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Service.InMemory.Test", "test\Microsoft.AspNetCore.Identity.Service.InMemory.Test\Microsoft.AspNetCore.Identity.Service.InMemory.Test.csproj", "{94EC586A-2AE6-4AF2-894A-B0973C65BD68}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -130,6 +158,174 @@ Global
{5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.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
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Debug|x86.ActiveCfg = Debug|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Debug|x86.Build.0 = Debug|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Release|Any CPU.Build.0 = Release|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Release|x86.ActiveCfg = Release|Any CPU
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11}.Release|x86.Build.0 = Release|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Debug|x86.ActiveCfg = Debug|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Debug|x86.Build.0 = Debug|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Release|Any CPU.Build.0 = Release|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Release|x86.ActiveCfg = Release|Any CPU
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C}.Release|x86.Build.0 = Release|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Debug|x86.ActiveCfg = Debug|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Debug|x86.Build.0 = Debug|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Release|Any CPU.Build.0 = Release|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Release|x86.ActiveCfg = Release|Any CPU
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81}.Release|x86.Build.0 = Release|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Debug|x86.ActiveCfg = Debug|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Debug|x86.Build.0 = Debug|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Release|Any CPU.Build.0 = Release|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Release|x86.ActiveCfg = Release|Any CPU
{590697C1-EA60-4412-8A21-4EF35142381F}.Release|x86.Build.0 = Release|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Debug|x86.ActiveCfg = Debug|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Debug|x86.Build.0 = Debug|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Release|Any CPU.Build.0 = Release|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Release|x86.ActiveCfg = Release|Any CPU
{CD360545-3395-4C44-AD27-C32EECDD9572}.Release|x86.Build.0 = Release|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Debug|x86.ActiveCfg = Debug|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Debug|x86.Build.0 = Debug|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Release|Any CPU.Build.0 = Release|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Release|x86.ActiveCfg = Release|Any CPU
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB}.Release|x86.Build.0 = Release|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Debug|x86.ActiveCfg = Debug|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Debug|x86.Build.0 = Debug|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Release|Any CPU.Build.0 = Release|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Release|x86.ActiveCfg = Release|Any CPU
{B3AE446B-859B-4C2C-98FD-A084C854941E}.Release|x86.Build.0 = Release|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Debug|x86.ActiveCfg = Debug|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Debug|x86.Build.0 = Debug|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Release|Any CPU.Build.0 = Release|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Release|x86.ActiveCfg = Release|Any CPU
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B}.Release|x86.Build.0 = Release|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Debug|x86.ActiveCfg = Debug|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Debug|x86.Build.0 = Debug|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Release|Any CPU.Build.0 = Release|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Release|x86.ActiveCfg = Release|Any CPU
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E}.Release|x86.Build.0 = Release|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Debug|Any CPU.Build.0 = Debug|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Debug|x86.ActiveCfg = Debug|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Debug|x86.Build.0 = Debug|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Release|Any CPU.ActiveCfg = Release|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Release|Any CPU.Build.0 = Release|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Release|x86.ActiveCfg = Release|Any CPU
{204163F9-E9BB-4940-9659-77F617C00D97}.Release|x86.Build.0 = Release|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Debug|x86.ActiveCfg = Debug|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Debug|x86.Build.0 = Debug|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Release|Any CPU.Build.0 = Release|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Release|x86.ActiveCfg = Release|Any CPU
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36}.Release|x86.Build.0 = Release|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Debug|x86.ActiveCfg = Debug|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Debug|x86.Build.0 = Debug|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Release|Any CPU.Build.0 = Release|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Release|x86.ActiveCfg = Release|Any CPU
{7423EB30-FFE9-4707-A44B-571E89A7CA15}.Release|x86.Build.0 = Release|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Debug|x86.ActiveCfg = Debug|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Debug|x86.Build.0 = Debug|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Release|Any CPU.Build.0 = Release|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Release|x86.ActiveCfg = Release|Any CPU
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66}.Release|x86.Build.0 = Release|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Debug|x86.ActiveCfg = Debug|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Debug|x86.Build.0 = Debug|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Release|Any CPU.Build.0 = Release|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Release|x86.ActiveCfg = Release|Any CPU
{94EC586A-2AE6-4AF2-894A-B0973C65BD68}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -144,5 +340,19 @@ Global
{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}
{CD787C9A-58B7-4CBC-B8E3-66698EE58C11} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{B44C2A7F-EA9E-4A9F-9698-1C9F9BB40E0C} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{F34C3ED8-D4A9-47CE-BE0F-1F234A33AC81} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{590697C1-EA60-4412-8A21-4EF35142381F} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{CD360545-3395-4C44-AD27-C32EECDD9572} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{CA19785B-CE2F-480D-BB57-93A43A2DFDAB} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{B3AE446B-859B-4C2C-98FD-A084C854941E} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{27D28F0E-08F6-4EEA-8705-E0B559C87F3B} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
{444F07E7-CF65-4717-BEF3-BA29F60DDE6E} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
{204163F9-E9BB-4940-9659-77F617C00D97} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
{C05D641C-A3EE-4A56-9A39-F20F3B9C4D36} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
{7423EB30-FFE9-4707-A44B-571E89A7CA15} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
{4F5D777E-3CFA-4EDF-BA89-4FE04BBF7A66} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
{94EC586A-2AE6-4AF2-894A-B0973C65BD68} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
EndGlobalSection
EndGlobal

View File

@ -1,6 +1,8 @@
<Project>
<PropertyGroup>
<AspNetCoreVersion>2.0.0-*</AspNetCoreVersion>
<IdentityModelOpenIdVersion>2.1.3</IdentityModelOpenIdVersion>
<CoreFxVersion>4.3.0</CoreFxVersion>
<IdentityEFCompatVersion>2.2.1</IdentityEFCompatVersion>
<InternalAspNetCoreSdkVersion>2.1.0-*</InternalAspNetCoreSdkVersion>
<MoqVersion>4.7.1</MoqVersion>

View File

@ -0,0 +1,137 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity.Service;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Diagnostics.Identity.Service
{
public class DeveloperCertificateMiddleware
{
private readonly RequestDelegate _next;
private readonly IHostingEnvironment _environment;
private readonly IOptions<DeveloperCertificateOptions> _options;
private readonly ITimeStampManager _timeStampManager;
private readonly IConfiguration _configuration;
private readonly IOptionsCache<IdentityServiceOptions> _identityServiceOptionsCache;
public DeveloperCertificateMiddleware(
RequestDelegate next,
IOptions<DeveloperCertificateOptions> options,
IOptionsCache<IdentityServiceOptions> identityServiceOptions,
ITimeStampManager timeStampManager,
IHostingEnvironment environment,
IConfiguration configuration)
{
_next = next;
_options = options;
_identityServiceOptionsCache = identityServiceOptions;
_environment = environment;
_timeStampManager = timeStampManager;
_configuration = configuration;
}
public async Task InvokeAsync(HttpContext context)
{
var credentialsProvider = context.RequestServices.GetRequiredService<ISigningCredentialsPolicyProvider>();
var openIdOptionsCache = context.RequestServices.GetRequiredService<IOptionsCache<OpenIdConnectOptions>>();
if (_environment.IsDevelopment() &&
context.Request.Path.Equals(_options.Value.ListeningEndpoint))
{
if (context.Request.Method.Equals(HttpMethods.Get))
{
var credentials = await credentialsProvider.GetAllCredentialsAsync();
bool hasDevelopmentCertificate = await IsDevelopmentCertificateConfiguredAndValid();
var foundDeveloperCertificate = FoundDeveloperCertificate();
if (!foundDeveloperCertificate || !hasDevelopmentCertificate)
{
var page = new DeveloperCertificateErrorPage();
page.Model = new DeveloperCertificateViewModel()
{
CertificateExists = foundDeveloperCertificate,
CertificateIsInvalid = !hasDevelopmentCertificate,
Options = _options.Value
};
await page.ExecuteAsync(context);
return;
}
}
if (context.Request.Method.Equals(HttpMethods.Post))
{
CreateDevelopmentCertificate();
return;
}
}
await _next(context);
void CreateDevelopmentCertificate()
{
using (var rsa = RSA.Create(2048))
{
var signingRequest = new CertificateRequest(
new X500DistinguishedName("CN=IdentityService.Development"), rsa, HashAlgorithmName.SHA256);
var enhacedKeyUsage = new OidCollection();
enhacedKeyUsage.Add(new Oid("1.3.6.1.5.5.7.3.1", "Server Authentication"));
signingRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(enhacedKeyUsage, critical: true));
signingRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true));
var certificate = signingRequest.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1));
certificate.FriendlyName = "Identity Service developer certificate";
// We need to take this step so that the key gets persisted.
var export = certificate.Export(X509ContentType.Pkcs12, "");
var imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet);
Array.Clear(export, 0, export.Length);
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadWrite);
store.Add(imported);
store.Close();
_identityServiceOptionsCache.TryRemove(Options.DefaultName);
openIdOptionsCache.TryRemove(OpenIdConnectDefaults.AuthenticationScheme);
context.Response.StatusCode = StatusCodes.Status204NoContent;
};
}
}
bool FoundDeveloperCertificate()
{
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
var developmentCertificate = store.Certificates.Find(
X509FindType.FindBySubjectName,
"IdentityService.Development",
validOnly: false);
store.Close();
return developmentCertificate.OfType<X509Certificate2>().Any();
}
}
async Task<bool> IsDevelopmentCertificateConfiguredAndValid()
{
var certificates = await credentialsProvider.GetAllCredentialsAsync();
return certificates.Any(
c => _timeStampManager.IsValidPeriod(c.NotBefore, c.Expires) &&
c.Credentials.Key is X509SecurityKey key &&
key.Certificate.Subject.Equals("CN=IdentityService.Development"));
}
}
}
}

View File

@ -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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Diagnostics.Identity.Service
{
public class DeveloperCertificateOptions
{
public PathString ListeningEndpoint { get; set; } = "/tfp/IdentityService/signinsignup/oauth2/v2.0/authorize";
}
}

View File

@ -0,0 +1,19 @@
// 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.Builder;
using Microsoft.Extensions.Configuration;
namespace Microsoft.AspNetCore.Diagnostics.Identity.Service
{
public static class IdentityApplicationBuilderExtensions
{
public static IApplicationBuilder UseDevelopmentCertificateErrorPage(
this IApplicationBuilder builder,
IConfiguration configuration)
{
builder.UseMiddleware<DeveloperCertificateMiddleware>(configuration);
return builder;
}
}
}

View File

@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<Description>ASP.NET Core Identity Service Diagnostics Middleware.</Description>
<TargetFramework>netcoreapp2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore</PackageTags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.RazorViews.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Microsoft.AspNetCore.Identity.Service/Microsoft.AspNetCore.Identity.Service.csproj" />
<ProjectReference Include="../Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore/Microsoft.AspNetCore.Identity.Service.EntityFrameworkCore.csproj" />
<ProjectReference Include="../Microsoft.AspNetCore.Identity.Service.Mvc/Microsoft.AspNetCore.Identity.Service.Mvc.csproj" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="RazorPageGenerator" Version="$(AspNetCoreVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Update="Strings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -0,0 +1,146 @@
//------------------------------------------------------------------------------
// <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 Microsoft.AspNetCore.Diagnostics.Identity.Service {
using System;
/// <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 Strings {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Strings() {
}
/// <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 (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNetCore.Diagnostics.Identity.Service.Strings", typeof(Strings).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;
}
}
/// <summary>
/// Looks up a localized string similar to Developer certificate.
/// </summary>
internal static string CertificateErrorPage_Title {
get {
return ResourceManager.GetString("CertificateErrorPage_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create certificate.
/// </summary>
internal static string CreateCertificate {
get {
return ResourceManager.GetString("CreateCertificate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Done.
/// </summary>
internal static string CreateCertificateDone {
get {
return ResourceManager.GetString("CreateCertificateDone", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Certificate creation failed..
/// </summary>
internal static string CreateCertificateFailed {
get {
return ResourceManager.GetString("CreateCertificateFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Certificate creation succeeded. Try refreshing the page..
/// </summary>
internal static string CreateCertificateRefresh {
get {
return ResourceManager.GetString("CreateCertificateRefresh", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Creating developer certificate....
/// </summary>
internal static string CreateCertificateRunning {
get {
return ResourceManager.GetString("CreateCertificateRunning", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Identity requires a certificate to sign tokens. You can create a developer certificate by clicking the Create Certificate button to generate a developer certificate for you automatically. This will create a self-signed certificate with subject IdentityService.Development and will add it to your current user personal store.
/// Alternatively, you can create this certificate manually with the instructions given in the following link:
/// .
/// </summary>
internal static string ManualCertificateGenerationInfo {
get {
return ResourceManager.GetString("ManualCertificateGenerationInfo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to https://go.microsoft.com/fwlink/?linkid=848037.
/// </summary>
internal static string ManualCertificateGenerationInfoLink {
get {
return ResourceManager.GetString("ManualCertificateGenerationInfoLink", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The developer certificate is missing or invalid.
/// </summary>
internal static string MissingOrInvalidCertificate {
get {
return ResourceManager.GetString("MissingOrInvalidCertificate", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,149 @@
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
<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" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</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" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CertificateErrorPage_Title" xml:space="preserve">
<value>Developer certificate</value>
</data>
<data name="CreateCertificate" xml:space="preserve">
<value>Create certificate</value>
</data>
<data name="CreateCertificateDone" xml:space="preserve">
<value>Done</value>
</data>
<data name="CreateCertificateFailed" xml:space="preserve">
<value>Certificate creation failed.</value>
</data>
<data name="CreateCertificateRefresh" xml:space="preserve">
<value>Certificate creation succeeded. Try refreshing the page.</value>
</data>
<data name="CreateCertificateRunning" xml:space="preserve">
<value>Creating developer certificate...</value>
</data>
<data name="ManualCertificateGenerationInfo" xml:space="preserve">
<value>Identity requires a certificate to sign tokens. You can create a developer certificate by clicking the Create Certificate button to generate a developer certificate for you automatically. This will create a self-signed certificate with subject IdentityService.Development and will add it to your current user personal store.
Alternatively, you can create this certificate manually with the instructions given in the following link:
</value>
</data>
<data name="ManualCertificateGenerationInfoLink" xml:space="preserve">
<value>https://go.microsoft.com/fwlink/?linkid=848037</value>
</data>
<data name="MissingOrInvalidCertificate" xml:space="preserve">
<value>The developer certificate is missing or invalid</value>
</data>
</root>

View File

@ -0,0 +1,247 @@
namespace Microsoft.AspNetCore.Diagnostics.Identity.Service
{
#line hidden
#line 1 "DeveloperCertificateErrorPage.cshtml"
using System;
#line default
#line hidden
using System.Threading.Tasks;
internal class DeveloperCertificateErrorPage : Microsoft.Extensions.RazorViews.BaseView
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
#line 2 "DeveloperCertificateErrorPage.cshtml"
Response.StatusCode = 500;
Response.ContentType = "text/html; charset=utf-8";
Response.ContentLength = null; // Clear any prior Content-Length
#line default
#line hidden
WriteLiteral(@"<!DOCTYPE html>
<html lang=""en"" xmlns=""http://www.w3.org/1999/xhtml"">
<head>
<meta charset=""utf-8"" />
<title>Internal Server Error</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
font-size: .813em;
line-height: 1.4em;
color: #222;
}
h1, h2, h3, h4, h5 {
font-weight: 100;
}
h1 {
color: #44525e;
margin: 15px 0 15px 0;
}
h2 {
margin: 10px 5px 0 0;
}
h3 {
color: #363636;
margin: 5px 5px 0 0;
}
code {
font-family: Consolas, ""Courier New"", courier, monospace;
}
a {
color: #1ba1e2;
text-decoration: none;
}
a:hover {
color: #13709e;
text-decoration: underline;
}
hr {
border: 1px #ddd solid;
}
body .titleerror {
padding: 3px;
}
#createCertificate {
font-size: 14px;
background: #44c5f2;
color: #ffffff;
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-weight: normal;");
WriteLiteral(@"
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
}
#createCertificate:disabled {
background-color: #a9e4f9;
border-color: #44c5f2;
}
.error {
color: red;
}
.expanded {
display: block;
}
.collapsed {
display: none;
}
</style>
</head>
<body>
<h1>");
#line 110 "DeveloperCertificateErrorPage.cshtml"
Write(Strings.CertificateErrorPage_Title);
#line default
#line hidden
WriteLiteral("</h1>\r\n <p>\r\n </p>\r\n <hr />\r\n\r\n");
#line 115 "DeveloperCertificateErrorPage.cshtml"
if (!Model.CertificateExists || Model.CertificateIsInvalid)
{
#line default
#line hidden
WriteLiteral(" <h2>");
#line 117 "DeveloperCertificateErrorPage.cshtml"
Write(Strings.MissingOrInvalidCertificate);
#line default
#line hidden
WriteLiteral("</h2>\r\n <p>");
#line 118 "DeveloperCertificateErrorPage.cshtml"
Write(Strings.ManualCertificateGenerationInfo);
#line default
#line hidden
WriteLiteral("<a");
BeginWriteAttribute("href", " href=\"", 2170, "\"", 2221, 1);
#line 118 "DeveloperCertificateErrorPage.cshtml"
WriteAttributeValue("", 2177, Strings.ManualCertificateGenerationInfoLink, 2177, 44, false);
#line default
#line hidden
EndWriteAttribute();
WriteLiteral(">");
#line 118 "DeveloperCertificateErrorPage.cshtml"
Write(Strings.ManualCertificateGenerationInfoLink);
#line default
#line hidden
WriteLiteral("</a></p>\r\n <br />\r\n <hr />\r\n <div>\r\n <p>\r\n <button id=\"createCertificate\" onclick=\"CreateCertificate()\">");
#line 123 "DeveloperCertificateErrorPage.cshtml"
Write(Strings.CreateCertificate);
#line default
#line hidden
WriteLiteral(@"</button>
<span id=""createCertificateError"" class=""error""></span>
<span id=""createCertificateSuccess""></span>
</p>
<script>
function CreateCertificate() {
createCertificate.disabled = true;
createCertificateError.innerHTML = """";
createCertificate.innerHTML = """);
#line 131 "DeveloperCertificateErrorPage.cshtml"
Write(JavaScriptEncode(Strings.CreateCertificateRunning));
#line default
#line hidden
WriteLiteral("\";\r\n\r\n var req = new XMLHttpRequest();\r\n\r\n req.onload = function (e) {\r\n if (req.status === 204) {\r\n createCertificate.innerHTML = \"");
#line 137 "DeveloperCertificateErrorPage.cshtml"
Write(JavaScriptEncode(Strings.CreateCertificateDone));
#line default
#line hidden
WriteLiteral("\";\r\n createCertificateSuccess.innerHTML = \"");
#line 138 "DeveloperCertificateErrorPage.cshtml"
Write(JavaScriptEncode(Strings.CreateCertificateRefresh));
#line default
#line hidden
WriteLiteral(@""";
} else {
ErrorCreatingCertificate();
}
};
req.onerror = function (e) {
ErrorCreatingCertificate();
};
var formBody = """";
req.open(""POST"", """);
#line 149 "DeveloperCertificateErrorPage.cshtml"
Write(JavaScriptEncode(Model.Options.ListeningEndpoint.Value));
#line default
#line hidden
WriteLiteral(@""", true);
req.setRequestHeader(""Content-type"", ""application/x-www-form-urlencoded"");
req.setRequestHeader(""Content-length"", formBody.length);
req.setRequestHeader(""Connection"", ""close"");
req.send(formBody);
}
function ErrorCreatingCertificate() {
createCertificate.innerHTML = """);
#line 157 "DeveloperCertificateErrorPage.cshtml"
Write(JavaScriptEncode(Strings.CreateCertificate));
#line default
#line hidden
WriteLiteral("\";\r\n createCertificateError.innerHTML = \"");
#line 158 "DeveloperCertificateErrorPage.cshtml"
Write(JavaScriptEncode(Strings.CreateCertificateFailed));
#line default
#line hidden
WriteLiteral("\";\r\n createCertificate.disabled = false;\r\n }\r\n </script>\r\n </div>\r\n");
#line 163 "DeveloperCertificateErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral("</body>\r\n</html>");
}
#pragma warning restore 1998
#line 8 "DeveloperCertificateErrorPage.cshtml"
public DeveloperCertificateViewModel Model { get; set; }
public string UrlEncode(string content)
{
return UrlEncoder.Encode(content);
}
public string JavaScriptEncode(string content)
{
return JavaScriptEncoder.Encode(content);
}
#line default
#line hidden
}
}

View File

@ -0,0 +1,87 @@
@using System
@{
Response.StatusCode = 500;
Response.ContentType = "text/html; charset=utf-8";
Response.ContentLength = null; // Clear any prior Content-Length
}
@functions
{
public DeveloperCertificateViewModel Model { get; set; }
public string UrlEncode(string content)
{
return UrlEncoder.Encode(content);
}
public string JavaScriptEncode(string content)
{
return JavaScriptEncoder.Encode(content);
}
}
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Internal Server Error</title>
<style>
<%$ include: ErrorPage.css %>
</style>
</head>
<body>
<h1>@Strings.CertificateErrorPage_Title</h1>
<p>
</p>
<hr />
@if (!Model.CertificateExists || Model.CertificateIsInvalid)
{
<h2>@Strings.MissingOrInvalidCertificate</h2>
<p>@Strings.ManualCertificateGenerationInfo<a href="@Strings.ManualCertificateGenerationInfoLink">@Strings.ManualCertificateGenerationInfoLink</a></p>
<br />
<hr />
<div>
<p>
<button id="createCertificate" onclick="CreateCertificate()">@Strings.CreateCertificate</button>
<span id="createCertificateError" class="error"></span>
<span id="createCertificateSuccess"></span>
</p>
<script>
function CreateCertificate() {
createCertificate.disabled = true;
createCertificateError.innerHTML = "";
createCertificate.innerHTML = "@JavaScriptEncode(Strings.CreateCertificateRunning)";
var req = new XMLHttpRequest();
req.onload = function (e) {
if (req.status === 204) {
createCertificate.innerHTML = "@JavaScriptEncode(Strings.CreateCertificateDone)";
createCertificateSuccess.innerHTML = "@JavaScriptEncode(Strings.CreateCertificateRefresh)";
} else {
ErrorCreatingCertificate();
}
};
req.onerror = function (e) {
ErrorCreatingCertificate();
};
var formBody = "";
req.open("POST", "@JavaScriptEncode(Model.Options.ListeningEndpoint.Value)", true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.setRequestHeader("Content-length", formBody.length);
req.setRequestHeader("Connection", "close");
req.send(formBody);
}
function ErrorCreatingCertificate() {
createCertificate.innerHTML = "@JavaScriptEncode(Strings.CreateCertificate)";
createCertificateError.innerHTML = "@JavaScriptEncode(Strings.CreateCertificateFailed)";
createCertificate.disabled = false;
}
</script>
</div>
}
</body>
</html>

View File

@ -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.Diagnostics.Identity.Service
{
public class DeveloperCertificateViewModel
{
public DeveloperCertificateOptions Options { get; set; }
public bool CertificateExists { get; set; }
public bool CertificateIsInvalid { get; set; }
public bool CertificateIsFoundInConfiguration { get; set; }
}
}

View File

@ -0,0 +1,78 @@
body {
font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
font-size: .813em;
line-height: 1.4em;
color: #222;
}
h1, h2, h3, h4, h5 {
font-weight: 100;
}
h1 {
color: #44525e;
margin: 15px 0 15px 0;
}
h2 {
margin: 10px 5px 0 0;
}
h3 {
color: #363636;
margin: 5px 5px 0 0;
}
code {
font-family: Consolas, "Courier New", courier, monospace;
}
a {
color: #1ba1e2;
text-decoration: none;
}
a:hover {
color: #13709e;
text-decoration: underline;
}
hr {
border: 1px #ddd solid;
}
body .titleerror {
padding: 3px;
}
#createCertificate {
font-size: 14px;
background: #44c5f2;
color: #ffffff;
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-weight: normal;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
}
#createCertificate:disabled {
background-color: #a9e4f9;
border-color: #44c5f2;
}
.error {
color: red;
}
.expanded {
display: block;
}
.collapsed {
display: none;
}

View File

@ -0,0 +1 @@
dotnet razorpagegenerator Microsoft.AspNetCore.Diagnostics.Identity.Service

View File

@ -0,0 +1,49 @@
// 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.Diagnostics;
namespace Microsoft.AspNetCore.Identity.Service
{
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
public class ApplicationScope : IEquatable<ApplicationScope>
{
public static readonly ApplicationScope OpenId = new ApplicationScope("openid");
public static readonly ApplicationScope Profile = new ApplicationScope("profile");
public static readonly ApplicationScope Email = new ApplicationScope("email");
public static readonly ApplicationScope OfflineAccess = new ApplicationScope("offline_access");
public static readonly IReadOnlyDictionary<string, ApplicationScope> CanonicalScopes = new Dictionary<string, ApplicationScope>(StringComparer.Ordinal)
{
[OpenId.Scope] = OpenId,
[Profile.Scope] = Profile,
[Email.Scope] = Email,
[OfflineAccess.Scope] = OfflineAccess
};
private ApplicationScope(string scope)
{
Scope = scope;
}
public ApplicationScope(string clientId, string scope)
{
ClientId = clientId;
Scope = scope;
}
public string ClientId { get; }
public string Scope { get; }
public bool Equals(ApplicationScope other) => string.Equals(ClientId, other?.ClientId, StringComparison.Ordinal) &&
string.Equals(Scope, other?.Scope, StringComparison.Ordinal);
public override bool Equals(object obj) => Equals(obj as ApplicationScope);
public override int GetHashCode() => ClientId == null ? Scope.GetHashCode() : ClientId.GetHashCode() ^ Scope.GetHashCode();
private string DebuggerDisplay() => ClientId != null ? $"{ClientId},{Scope}" : Scope;
}
}

View File

@ -0,0 +1,57 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AuthorizationGrant
{
private AuthorizationGrant(OpenIdConnectMessage error)
{
IsValid = false;
Error = error;
}
private AuthorizationGrant(
string userId,
string clientId,
IEnumerable<string> grantedTokens,
IEnumerable<ApplicationScope> grantedScopes,
Token token)
{
IsValid = true;
UserId = userId;
ClientId = clientId;
GrantedTokens = grantedTokens;
GrantedScopes = grantedScopes;
Token = token;
}
public bool IsValid { get; }
public OpenIdConnectMessage Error { get; }
public Token Token { get; }
public string UserId { get; }
public string ClientId { get; }
public IEnumerable<string> GrantedTokens { get; }
public IEnumerable<ApplicationScope> GrantedScopes { get; }
public string Resource { get; }
public static AuthorizationGrant Invalid(OpenIdConnectMessage error)
{
return new AuthorizationGrant(error);
}
public static AuthorizationGrant Valid(
string userId,
string clientId,
IEnumerable<string> grantedTokens,
IEnumerable<ApplicationScope> grantedScopes,
Token token)
{
return new AuthorizationGrant(userId, clientId, grantedTokens, grantedScopes, token);
}
}
}

View File

@ -0,0 +1,50 @@
// 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.Security.Claims;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AuthorizationRequest
{
private AuthorizationRequest(AuthorizationRequestError error)
{
IsValid = false;
Error = error;
}
private AuthorizationRequest(OpenIdConnectMessage request, RequestGrants requestGrants)
{
IsValid = true;
Message = request;
RequestGrants = requestGrants;
}
public bool IsValid { get; }
public AuthorizationRequestError Error { get; }
public OpenIdConnectMessage Message { get; }
public RequestGrants RequestGrants { get; }
public static AuthorizationRequest Invalid(AuthorizationRequestError authorizationRequestError)
{
return new AuthorizationRequest(authorizationRequestError);
}
public static AuthorizationRequest Valid(
OpenIdConnectMessage request,
RequestGrants requestGrants)
{
return new AuthorizationRequest(request, requestGrants);
}
public TokenGeneratingContext CreateTokenGeneratingContext(ClaimsPrincipal user, ClaimsPrincipal application)
{
return new TokenGeneratingContext(
user,
application,
Message,
RequestGrants);
}
}
}

View File

@ -0,0 +1,14 @@
// 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.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AuthorizationResponse
{
public OpenIdConnectMessage Message { get; set; }
public string RedirectUri { get; set; }
public string ResponseMode { get; set; }
}
}

View File

@ -0,0 +1,16 @@
// 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.Claims;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public class ClaimsGenerationContext
{
public string TokenType { get; set; }
public TokenGeneratingContext GenerationContext { get; set; }
public IEnumerable<TokenResult> GeneratedTokens { get; set; }
public IList<Claim> Claims { get; set; }
}
}

View File

@ -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.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ITokenClaimsManager
{
Task CreateClaimsAsync(TokenGeneratingContext context);
}
}

View File

@ -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 System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public interface ITokenClaimsProvider
{
int Order { get; }
Task OnGeneratingClaims(TokenGeneratingContext context);
}
}

View File

@ -0,0 +1,19 @@
// 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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Identity.Service
{
public class ConfigurationContext
{
public string Id { get; set; }
public HttpContext HttpContext { get; set; }
public string AuthorizationEndpoint { get; set; }
public string TokenEndpoint { get; set; }
public string JwksUriEndpoint { get; set; }
public string EndSessionEndpoint { get; set; }
public NameValueCollection AdditionalValues { get; set; }
}
}

View File

@ -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 System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IAuthorizationRequestFactory
{
Task<AuthorizationRequest> CreateAuthorizationRequestAsync(IDictionary<string, string[]> requestParameters);
}
}

View File

@ -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.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IAuthorizationResponseFactory
{
Task<AuthorizationResponse> CreateAuthorizationResponseAsync(TokenGeneratingContext context);
}
}

View File

@ -0,0 +1,14 @@
// 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;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IAuthorizationResponseParameterProvider
{
int Order { get; }
Task AddParameters(TokenGeneratingContext context, AuthorizationResponse response);
}
}

View File

@ -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 System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IConfigurationManager
{
Task<OpenIdConnectConfiguration> GetConfigurationAsync(ConfigurationContext context);
}
}

View File

@ -0,0 +1,15 @@
// 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 Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IConfigurationMetadataProvider
{
int Order { get; }
Task ConfigureMetadataAsync(OpenIdConnectConfiguration configuration, ConfigurationContext context);
}
}

View File

@ -0,0 +1,15 @@
// 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.DependencyInjection;
using System;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IIdentityServiceBuilder
{
IServiceCollection Services { get; }
Type ApplicationType { get; }
Type UserType { get; }
}
}

View File

@ -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.IdentityModel.Tokens;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IKeySetMetadataProvider
{
Task<JsonWebKeySet> GetKeysAsync();
}
}

View File

@ -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 System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ILogoutRequestFactory
{
Task<LogoutRequest> CreateLogoutRequestAsync(IDictionary<string, string[]> requestParameters);
}
}

View File

@ -0,0 +1,14 @@
// 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.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ISigningCredentialsPolicyProvider
{
Task<IEnumerable<SigningCredentialsDescriptor>> GetAllCredentialsAsync();
Task<SigningCredentialsDescriptor> GetSigningCredentialsAsync();
}
}

View File

@ -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 System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ISigningCredentialsSource
{
Task<IEnumerable<SigningCredentialsDescriptor>> GetCredentials();
}
}

View File

@ -0,0 +1,14 @@
// 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 Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ITokenManager
{
Task IssueTokensAsync(TokenGeneratingContext context);
Task<AuthorizationGrant> ExchangeTokenAsync(OpenIdConnectMessage message);
}
}

View File

@ -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 System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ITokenRequestFactory
{
Task<TokenRequest> CreateTokenRequestAsync(IDictionary<string, string[]> requestParameters);
}
}

View File

@ -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.IdentityModel.Protocols.OpenIdConnect;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ITokenResponseFactory
{
Task<OpenIdConnectMessage> CreateTokenResponseAsync(TokenGeneratingContext context);
}
}

View File

@ -0,0 +1,15 @@
// 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 Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ITokenResponseParameterProvider
{
int Order { get; }
Task AddParameters(TokenGeneratingContext context, OpenIdConnectMessage response);
}
}

View File

@ -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.
namespace Microsoft.AspNetCore.Identity.Service
{
public class IdentityServiceClaimTypes
{
public const string TokenUniqueId = "tuid";
public const string ObjectId = "oid";
public const string JwtId = "jti";
public const string Issuer = "iss";
public const string Subject = "sub";
public const string Audience = "aud";
public const string AuthorizedParty = "azp";
public const string ClientId = "client_id";
public const string RedirectUri = "r_uri";
public const string LogoutRedirectUri = "lo_uri";
public const string IssuedAt = "iat";
public const string Expires = "exp";
public const string NotBefore = "nbf";
public const string Scope = "scp";
public const string Nonce = "nonce";
public const string CodeHash = "c_hash";
public const string AccessTokenHash = "at_hash";
public const string AuthenticationTime = "auth_time";
public const string UserId = "user_id";
public const string Version = "ver";
public const string Name = "name";
public const string GrantedToken = "g_token";
public const string TenantId = "tid";
public const string Resource = "rid";
}
}

View File

@ -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.Identity.Service
{
public class IdentityServiceErrorCodes
{
public const string InvalidRequest = "invalid_request";
}
}

View File

@ -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.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IAccessTokenIssuer
{
Task IssueAccessTokenAsync(TokenGeneratingContext context);
}
}

View File

@ -0,0 +1,14 @@
// 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 Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IAuthorizationCodeIssuer
{
Task CreateAuthorizationCodeAsync(TokenGeneratingContext context);
Task<AuthorizationGrant> ExchangeAuthorizationCodeAsync(OpenIdConnectMessage message);
}
}

View File

@ -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.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IIdTokenIssuer
{
Task IssueIdTokenAsync(TokenGeneratingContext context);
}
}

View File

@ -0,0 +1,14 @@
// 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 Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IRefreshTokenIssuer
{
Task IssueRefreshTokenAsync(TokenGeneratingContext context);
Task<AuthorizationGrant> ExchangeRefreshTokenAsync(OpenIdConnectMessage message);
}
}

View File

@ -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.Identity.Service
{
public interface ITokenHasher
{
string HashToken(string token, string algorithm);
}
}

View File

@ -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.
namespace Microsoft.AspNetCore.Identity.Service
{
public class TokenResult
{
public TokenResult(Token token, string serializedValue)
{
Token = token;
SerializedValue = serializedValue;
}
public TokenResult(Token token, string serializedValue, string tokenType)
: this(token, serializedValue)
{
Token = token;
TokenType = tokenType;
SerializedValue = serializedValue;
}
public Token Token { get; }
public string TokenType { get; }
public string SerializedValue { get; }
}
}

View File

@ -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.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class LogoutRequest
{
private LogoutRequest(OpenIdConnectMessage message)
{
Message = message;
IsValid = false;
}
private LogoutRequest(OpenIdConnectMessage message, string logoutRedirectUri)
{
Message = message;
LogoutRedirectUri = logoutRedirectUri;
IsValid = true;
}
public OpenIdConnectMessage Message { get; }
public string LogoutRedirectUri { get; set; }
public bool IsValid { get; }
public static LogoutRequest Valid(OpenIdConnectMessage message, string logoutRedirectUri) => new LogoutRequest(message, logoutRedirectUri);
public static LogoutRequest Invalid(OpenIdConnectMessage error) => new LogoutRequest(error);
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<Description>ASP.NET Core common types defining the main abstractions for Identity Service.</Description>
<TargetFramework>netcoreapp2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore</PackageTags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="$(IdentityModelOpenIdVersion)" />
</ItemGroup>
</Project>

View File

@ -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.Identity.Service
{
public static class PromptValues
{
public const string None = "none";
public const string Login = "login";
public const string Consent = "consent";
public const string SelectAccount = "select_account";
}
}

View File

@ -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 Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AuthorizationRequestError
{
public AuthorizationRequestError(OpenIdConnectMessage error, string redirectUri, string responseMode)
{
Message = error;
RedirectUri = redirectUri;
ResponseMode = responseMode;
}
public OpenIdConnectMessage Message { get; set; }
public string RedirectUri { get; set; }
public string ResponseMode { get; set; }
}
}

View File

@ -0,0 +1,36 @@
// 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.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class RedirectUriResolutionResult
{
private RedirectUriResolutionResult(string uri)
{
IsValid = true;
Uri = uri;
}
private RedirectUriResolutionResult(OpenIdConnectMessage error)
{
IsValid = false;
Error = error;
}
public bool IsValid { get; }
public string Uri { get; }
public OpenIdConnectMessage Error { get; }
public static RedirectUriResolutionResult Valid(string uri)
{
return new RedirectUriResolutionResult(uri);
}
public static RedirectUriResolutionResult Invalid(OpenIdConnectMessage error)
{
return new RedirectUriResolutionResult(error);
}
}
}

View File

@ -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.
using System.Collections.Generic;
using System.Security.Claims;
namespace Microsoft.AspNetCore.Identity.Service
{
public class RequestGrants
{
public string RedirectUri { get; set; }
public string ResponseMode { get; set; }
public IList<string> Tokens { get; set; } = new List<string>();
public IList<ApplicationScope> Scopes { get; set; } = new List<ApplicationScope>();
public IList<Claim> Claims { get; set; } = new List<Claim>();
}
}

View File

@ -0,0 +1,37 @@
// 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.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class ScopeResolutionResult
{
public ScopeResolutionResult(IEnumerable<ApplicationScope> scopes)
{
IsValid = true;
Scopes = scopes;
}
public ScopeResolutionResult(OpenIdConnectMessage error)
{
IsValid = false;
Error = error;
}
public IEnumerable<ApplicationScope> Scopes { get; }
public OpenIdConnectMessage Error { get; }
public bool IsValid { get; }
public static ScopeResolutionResult Valid(IEnumerable<ApplicationScope> scopes)
{
return new ScopeResolutionResult(scopes);
}
public static ScopeResolutionResult Invalid(OpenIdConnectMessage error)
{
return new ScopeResolutionResult(error);
}
}
}

View File

@ -0,0 +1,51 @@
// 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.Diagnostics;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service
{
[DebuggerDisplay("{GetDebugDisplay(),nq}")]
public class SigningCredentialsDescriptor
{
public SigningCredentialsDescriptor(
SigningCredentials credentials,
string algorithm,
DateTimeOffset notBefore,
DateTimeOffset expires,
IDictionary<string, string> metadata)
{
Credentials = credentials;
Algorithm = algorithm;
NotBefore = notBefore;
Expires = expires;
Metadata = metadata;
}
public string Id => Credentials.Kid;
public string Algorithm { get; set; }
public DateTimeOffset NotBefore { get; set; }
public DateTimeOffset Expires { get; set; }
public SigningCredentials Credentials { get; set; }
public IDictionary<string, string> Metadata { get; set; }
private string GetDebugDisplay()
{
var builder = new StringBuilder();
builder.Append($"Id = {Id}, ");
builder.Append($"Alg = {Algorithm}, ");
builder.Append($"Nbf = {NotBefore}, ");
builder.Append($"Exp = {Expires}, ");
foreach (var kvp in Metadata)
{
builder.Append($"{kvp.Key} = {kvp.Value}, ");
}
return builder.ToString();
}
}
}

View File

@ -0,0 +1,134 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class TokenGeneratingContext
{
private readonly IList<TokenResult> _issuedTokens = new List<TokenResult>();
public TokenGeneratingContext(
ClaimsPrincipal user,
ClaimsPrincipal application,
OpenIdConnectMessage requestParameters,
RequestGrants requestGrants)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (requestParameters == null)
{
throw new ArgumentNullException(nameof(requestParameters));
}
if (requestGrants == null)
{
throw new ArgumentNullException(nameof(requestGrants));
}
User = user;
Application = application;
RequestParameters = requestParameters;
RequestGrants = requestGrants;
}
public ClaimsPrincipal User { get; }
public ClaimsPrincipal Application { get; }
public OpenIdConnectMessage RequestParameters { get; }
public RequestGrants RequestGrants { get; }
public IList<Claim> AmbientClaims { get; } = new List<Claim>();
public string CurrentToken { get; private set; }
public IList<Claim> CurrentClaims { get; private set; }
public IEnumerable<TokenResult> IssuedTokens { get => _issuedTokens; }
public TokenResult AuthorizationCode =>
IssuedTokens.SingleOrDefault(it => it.Token.IsOfKind(TokenTypes.AuthorizationCode));
public TokenResult AccessToken =>
IssuedTokens.SingleOrDefault(it => it.Token.IsOfKind(TokenTypes.AccessToken));
public TokenResult IdToken =>
IssuedTokens.SingleOrDefault(it => it.Token.IsOfKind(TokenTypes.IdToken));
public TokenResult RefreshToken =>
IssuedTokens.SingleOrDefault(it => it.Token.IsOfKind(TokenTypes.RefreshToken));
public void InitializeForToken(string tokenType)
{
if (tokenType == null)
{
throw new ArgumentNullException(nameof(tokenType));
}
if (CurrentToken != null)
{
throw new InvalidOperationException($"Currently issuing a token for {CurrentToken}");
}
if (IssuedTokens.Any(it => it.Token.IsOfKind(tokenType)))
{
throw new InvalidOperationException($"A token of type '{tokenType}' has already been emitted.");
}
CurrentToken = tokenType;
CurrentClaims = new List<Claim>();
}
public bool IsContextForTokenTypes(params string [] tokenTypes)
{
if (CurrentToken == null)
{
return false;
}
foreach (var token in tokenTypes)
{
if (CurrentToken.Equals(token, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
public void AddClaimToCurrentToken(Claim claim)
{
if (CurrentToken == null)
{
throw new InvalidOperationException();
}
CurrentClaims.Add(claim);
}
public void AddClaimToCurrentToken(string type, string value) => AddClaimToCurrentToken(new Claim(type, value));
public void AddToken(TokenResult result)
{
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
if (!result.Token.IsOfKind(CurrentToken))
{
throw new InvalidOperationException(
$"Can't add a result of token type '{result.Token.Kind}' to a context of '{CurrentToken ?? "(null)"}'");
}
_issuedTokens.Add(result);
CurrentToken = null;
CurrentClaims = null;
}
}
}

View File

@ -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.Identity.Service
{
public class TokenKinds
{
public static readonly string Bearer = "Bearer";
}
}

View File

@ -0,0 +1,56 @@
// 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.Security.Claims;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class TokenRequest
{
private TokenRequest(OpenIdConnectMessage error)
{
IsValid = false;
Error = error;
}
private TokenRequest(
OpenIdConnectMessage request,
string userId,
string clientId,
RequestGrants grants)
{
IsValid = true;
Request = request;
UserId = userId;
ClientId = clientId;
RequestGrants = grants;
}
public bool IsValid { get; }
public OpenIdConnectMessage Request { get; }
public string UserId { get; }
public string ClientId { get; }
public OpenIdConnectMessage Error { get; }
public RequestGrants RequestGrants { get; }
public static TokenRequest Invalid(OpenIdConnectMessage error)
{
return new TokenRequest(error);
}
public static TokenRequest Valid(
OpenIdConnectMessage request,
string userId,
string clientId,
RequestGrants grants)
{
return new TokenRequest(request, userId, clientId, grants);
}
public TokenGeneratingContext CreateTokenGeneratingContext(ClaimsPrincipal user, ClaimsPrincipal application)
{
return new TokenGeneratingContext(user, application, Request, RequestGrants);
}
}
}

View File

@ -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.Identity.Service
{
public class TokenTypes
{
public const string AuthorizationCode = "code";
public const string AccessToken = "access_token";
public const string RefreshToken = "refresh_token";
public const string IdToken = "id_token";
}
}

View File

@ -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.Collections.Generic;
using System.Security.Claims;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AccessToken : Token
{
public AccessToken(IEnumerable<Claim> claims)
: base(ValidateClaims(claims))
{
}
private static IEnumerable<Claim> ValidateClaims(IEnumerable<Claim> claims)
{
EnsureUniqueClaim(IdentityServiceClaimTypes.Issuer, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Subject, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Audience, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Scope, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.AuthorizedParty, claims);
return claims;
}
public override string Kind => TokenTypes.AccessToken;
public string Issuer => GetClaimValue(IdentityServiceClaimTypes.Issuer);
public string Subject => GetClaimValue(IdentityServiceClaimTypes.Subject);
public string Audience => GetClaimValue(IdentityServiceClaimTypes.Audience);
public string AuthorizedParty => GetClaimValue(IdentityServiceClaimTypes.AuthorizedParty);
public IEnumerable<string> Scopes => GetClaimValuesOrEmpty(IdentityServiceClaimTypes.Scope);
}
}

View File

@ -0,0 +1,36 @@
// 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.Claims;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AuthorizationCode : Token
{
public AuthorizationCode(IEnumerable<Claim> claims)
: base(ValidateClaims(claims))
{
}
private static IEnumerable<Claim> ValidateClaims(IEnumerable<Claim> claims)
{
EnsureUniqueClaim(IdentityServiceClaimTypes.UserId, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.ClientId, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.RedirectUri, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Scope, claims);
EnsureRequiredClaim(IdentityServiceClaimTypes.GrantedToken, claims);
return claims;
}
public override string Kind => TokenTypes.AuthorizationCode;
public string UserId => GetClaimValue(IdentityServiceClaimTypes.UserId);
public string ClientId => GetClaimValue(IdentityServiceClaimTypes.ClientId);
public string Resource => GetClaimValue(IdentityServiceClaimTypes.Resource);
public string RedirectUri => GetClaimValue(IdentityServiceClaimTypes.RedirectUri);
public IEnumerable<string> Scopes => GetClaimValuesOrEmpty(IdentityServiceClaimTypes.Scope);
public IEnumerable<string> GrantedTokens => GetClaimValuesOrEmpty(IdentityServiceClaimTypes.GrantedToken);
public string Nonce => GetClaimValue(IdentityServiceClaimTypes.Nonce);
}
}

View File

@ -0,0 +1,35 @@
// 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.Claims;
namespace Microsoft.AspNetCore.Identity.Service
{
public class IdToken : Token
{
public IdToken(IEnumerable<Claim> claims)
: base(ValidateClaims(claims))
{
}
private static IEnumerable<Claim> ValidateClaims(IEnumerable<Claim> claims)
{
EnsureUniqueClaim(IdentityServiceClaimTypes.Issuer, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Subject, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Audience, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Nonce, claims, required: false);
EnsureUniqueClaim(IdentityServiceClaimTypes.CodeHash, claims, required: false);
EnsureUniqueClaim(IdentityServiceClaimTypes.AccessTokenHash, claims, required: false);
return claims;
}
public override string Kind => TokenTypes.IdToken;
public string Issuer => GetClaimValue(IdentityServiceClaimTypes.Issuer);
public string Subject => GetClaimValue(IdentityServiceClaimTypes.Subject);
public string Audience => GetClaimValue(IdentityServiceClaimTypes.Audience);
public string Nonce => GetClaimValue(IdentityServiceClaimTypes.Nonce);
public string CodeHash => GetClaimValue(IdentityServiceClaimTypes.CodeHash);
public string AccessTokenHash => GetClaimValue(IdentityServiceClaimTypes.AccessTokenHash);
}
}

View File

@ -0,0 +1,34 @@
// 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.Claims;
namespace Microsoft.AspNetCore.Identity.Service
{
public class RefreshToken : Token
{
public RefreshToken(IEnumerable<Claim> claims)
: base(ValidateClaims(claims))
{
}
private static IEnumerable<Claim> ValidateClaims(IEnumerable<Claim> claims)
{
EnsureUniqueClaim(IdentityServiceClaimTypes.UserId, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.ClientId, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Scope, claims);
EnsureRequiredClaim(IdentityServiceClaimTypes.GrantedToken, claims);
return claims;
}
public override string Kind => TokenTypes.RefreshToken;
public string UserId => GetClaimValue(IdentityServiceClaimTypes.UserId);
public string ClientId => GetClaimValue(IdentityServiceClaimTypes.ClientId);
public string Resource => GetClaimValue(IdentityServiceClaimTypes.Resource);
public string Issuer => GetClaimValue(IdentityServiceClaimTypes.Issuer);
public IEnumerable<string> GrantedTokens => GetClaimValuesOrEmpty(IdentityServiceClaimTypes.GrantedToken);
public IEnumerable<string> Scopes => GetClaimValuesOrEmpty(IdentityServiceClaimTypes.Scope);
}
}

View File

@ -0,0 +1,114 @@
// 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;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service
{
public abstract class Token : IEnumerable<Claim>
{
private readonly IList<Claim> _claims = new List<Claim>();
protected Token(IEnumerable<Claim> claims)
{
EnsureUniqueClaim(IdentityServiceClaimTypes.TokenUniqueId, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.IssuedAt, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.Expires, claims);
EnsureUniqueClaim(IdentityServiceClaimTypes.NotBefore, claims);
_claims = new List<Claim>(claims);
}
internal static void EnsureUniqueClaim(string claimType, IEnumerable<Claim> claims, bool required = true)
{
var count = 0;
foreach (var claim in claims)
{
if (string.Equals(claimType, claim.Type, StringComparison.Ordinal))
{
count++;
}
if (count > 1)
{
break;
}
}
if (count == 0 && required)
{
throw new InvalidOperationException($"'{claimType}' is required.");
}
if (count > 1)
{
throw new InvalidOperationException($"'{claimType}' must be unique.");
}
}
internal static void EnsureRequiredClaim(string claimType, IEnumerable<Claim> claims)
{
foreach (var claim in claims)
{
if (string.Equals(claimType, claim.Type, StringComparison.Ordinal))
{
return;
}
}
throw new InvalidOperationException($"'{claimType}' not found.");
}
public abstract string Kind { get; }
public virtual string Id => GetClaimValue(IdentityServiceClaimTypes.TokenUniqueId);
public virtual DateTimeOffset IssuedAt => GetClaimValueOrNull(IdentityServiceClaimTypes.IssuedAt, v => EpochTime.DateTime(long.Parse(v)));
public virtual DateTimeOffset Expires => GetClaimValueOrNull(IdentityServiceClaimTypes.Expires, v => EpochTime.DateTime(long.Parse(v)));
public virtual DateTimeOffset NotBefore => GetClaimValueOrNull(IdentityServiceClaimTypes.NotBefore, v => EpochTime.DateTime(long.Parse(v)));
public bool IsOfKind(string tokenType) => Kind.Equals(tokenType, StringComparison.Ordinal);
public virtual string GetClaimValue(string claimType)
{
foreach (var claim in _claims)
{
if (string.Equals(claimType, claim.Type, StringComparison.Ordinal))
{
return claim.Value;
}
}
return null;
}
protected virtual T GetClaimValueOrNull<T>(string claimType, Func<string, T> valueFactory)
{
foreach (var claim in _claims)
{
if (string.Equals(claimType, claim.Type, StringComparison.Ordinal))
{
return valueFactory(claim.Value);
}
}
return default(T);
}
protected virtual IEnumerable<string> GetClaimValuesOrEmpty(string claimType)
{
foreach (var claim in _claims)
{
if (string.Equals(claimType, claim.Type, StringComparison.Ordinal))
{
yield return claim.Value;
}
}
}
public IEnumerator<Claim> GetEnumerator() => _claims.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _claims.GetEnumerator();
}
}

View File

@ -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.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IAuthorizationRequestValidator
{
Task<AuthorizationRequest> ValidateRequestAsync(AuthorizationRequest request);
}
}

View File

@ -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 System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IClientIdValidator
{
Task<bool> ValidateClientIdAsync(string clientId);
Task<bool> ValidateClientCredentialsAsync(string clientId, string clientSecret);
}
}

View File

@ -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 System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IRedirectUriResolver
{
Task<RedirectUriResolutionResult> ResolveRedirectUriAsync(string clientId, string redirectUrl);
Task<RedirectUriResolutionResult> ResolveLogoutUriAsync(string clientId, string logoutUrl);
}
}

View File

@ -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 System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface IScopeResolver
{
Task<ScopeResolutionResult> ResolveScopesAsync(string clientId, IEnumerable<string> scopes);
}
}

View File

@ -0,0 +1,20 @@
// 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;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ITimeStampManager
{
DateTimeOffset GetCurrentTimeStampUtc();
DateTimeOffset GetTimeStampUtc(TimeSpan validityPeriod);
DateTime GetCurrentTimeStampUtcAsDateTime();
DateTime GetTimeStampUtcAsDateTime(TimeSpan validityPeriod);
string GetTimeStampInEpochTime(TimeSpan validityPeriod);
string GetCurrentTimeStampInEpochTime();
DateTimeOffset GetTimeStampFromEpochTime(string epochTime);
long GetDurationInSeconds(DateTimeOffset end, DateTimeOffset beginning);
bool IsValidPeriod(DateTimeOffset start, DateTimeOffset end);
}
}

View File

@ -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.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public interface ITokenRequestValidator
{
Task<TokenRequest> ValidateRequestAsync(TokenRequest request);
}
}

View File

@ -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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AuthorizationCodeIssuer : IAuthorizationCodeIssuer
{
private readonly ISecureDataFormat<AuthorizationCode> _dataFormat;
private readonly ITokenClaimsManager _claimsManager;
private readonly ProtocolErrorProvider _errorProvider;
public AuthorizationCodeIssuer(
ITokenClaimsManager claimsManager,
ISecureDataFormat<AuthorizationCode> dataFormat,
ProtocolErrorProvider errorProvider)
{
_claimsManager = claimsManager;
_dataFormat = dataFormat;
_errorProvider = errorProvider;
}
public async Task CreateAuthorizationCodeAsync(TokenGeneratingContext context)
{
await _claimsManager.CreateClaimsAsync(context);
var claims = context.CurrentClaims;
var code = new AuthorizationCode(claims);
var tokenResult = new TokenResult(code, _dataFormat.Protect(code));
context.AddToken(tokenResult);
}
public Task<AuthorizationGrant> ExchangeAuthorizationCodeAsync(OpenIdConnectMessage message)
{
var code = _dataFormat.Unprotect(message.Code);
if (code == null)
{
return Task.FromResult(AuthorizationGrant.Invalid(_errorProvider.InvalidAuthorizationCode()));
}
var userId = code.UserId;
var clientId = code.ClientId;
var scopes = code.Scopes;
var resource = code.Resource;
var nonce = code.Nonce;
var tokenTypes = code.GrantedTokens;
var grantedScopes = scopes.SelectMany(s => s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
.Select(s => ApplicationScope.CanonicalScopes.TryGetValue(s, out var canonicalScope) ? canonicalScope : new ApplicationScope(resource, s))
.ToList();
return Task.FromResult(AuthorizationGrant.Valid(userId, clientId, tokenTypes, grantedScopes, code));
}
}
}

View File

@ -0,0 +1,398 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class AuthorizationRequestFactory : IAuthorizationRequestFactory
{
private static readonly string[] ValidResponseTypes = new string[] {
OpenIdConnectResponseType.None,
OpenIdConnectResponseType.Token,
OpenIdConnectResponseType.IdToken,
OpenIdConnectResponseType.Code
};
private static readonly string[] ValidResponseModes = new string[] {
OpenIdConnectResponseMode.Query,
OpenIdConnectResponseMode.Fragment,
OpenIdConnectResponseMode.FormPost
};
private readonly IClientIdValidator _clientIdValidator;
private readonly IRedirectUriResolver _redirectUrlValidator;
private readonly IScopeResolver _scopeValidator;
private readonly IEnumerable<IAuthorizationRequestValidator> _validators;
private readonly ProtocolErrorProvider _errorProvider;
public AuthorizationRequestFactory(
IClientIdValidator clientIdValidator,
IRedirectUriResolver redirectUriValidator,
IScopeResolver scopeValidator,
IEnumerable<IAuthorizationRequestValidator> validators,
ProtocolErrorProvider errorProvider)
{
_clientIdValidator = clientIdValidator;
_redirectUrlValidator = redirectUriValidator;
_scopeValidator = scopeValidator;
_validators = validators;
_errorProvider = errorProvider;
}
public async Task<AuthorizationRequest> CreateAuthorizationRequestAsync(IDictionary<string, string[]> requestParameters)
{
// Parameters sent without a value MUST be treated as if they were
// omitted from the request.The authorization server MUST ignore
// unrecognized request parameters.Request and response parameters
// MUST NOT be included more than once.
// Validate that we only got send one state property as it needs to be included in all responses (including error ones)
var (state, stateError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.State, _errorProvider);
// Start by validating the client_id and redirect_uri as any of them being invalid indicates that we need to
// return a 400 response instead of a 302 response with the error. This is signaled by the result not containing
// a url to redirect to.
var (clientId, redirectUri, clientError) = await ValidateClientIdAndRedirectUri(requestParameters, state);
if (clientError != null)
{
// Send first the state error if there was one.
return AuthorizationRequest.Invalid(new AuthorizationRequestError(
stateError ?? clientError,
redirectUri: null,
responseMode: null));
}
// We need to determine what response mode to use to send the errors in case there are any.
// In case the response type and response modes are valid, we should use those values when
// notifying clients of the errors.
// In case there is an issue with the response type or the response mode we need to determine
// how to notify the relying party of the errors.
// We can divide this in two situations:
// The response mode is invalid:
// * We ignore the response mode and base our response based on the response type specified.
// If a token was requested we send the error response on the fragment of the redirect uri.
// If no token was requested we send the error response on the query of the redirect uri.
// The response type is invalid:
// * We try to determine if this is a hybrid or implicit flow:
// If the invalid response type contained a request for an id_token or an access_token, or
// contained more than one space separated value, we send the response on the fragment,
// unless the response mode is specified and form_post.
// If the invalid response type only contained one value and we can not determine is an
// implicit request flow, we return the error on the query string unless the response mode
// is specified and form_post or fragment.
var (responseType, parsedResponseType, tokenRequested, responseTypeError) = ValidateResponseType(requestParameters);
var (responseMode, responseModeError) = ValidateResponseMode(requestParameters);
var invalidCombinationError = ValidateResponseModeTypeCombination(responseType, tokenRequested, responseMode);
if (responseModeError != null || responseMode == null)
{
responseMode = GetResponseMode(parsedResponseType, tokenRequested);
}
if (responseTypeError != null)
{
responseTypeError.State = state;
return AuthorizationRequest.Invalid(
new AuthorizationRequestError(stateError ?? responseTypeError, redirectUri, responseMode));
}
if (responseModeError != null)
{
responseModeError.State = state;
return AuthorizationRequest.Invalid(
new AuthorizationRequestError(stateError ?? responseModeError, redirectUri, responseMode));
}
if (invalidCombinationError != null)
{
invalidCombinationError.State = state;
return AuthorizationRequest.Invalid(
new AuthorizationRequestError(stateError ?? invalidCombinationError, redirectUri, responseMode));
}
var (nonce, nonceError) = tokenRequested ?
RequestParametersHelper.ValidateParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Nonce, _errorProvider) :
RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Nonce, _errorProvider);
if (nonceError != null)
{
nonceError.State = state;
return AuthorizationRequest.Invalid(new AuthorizationRequestError(nonceError, redirectUri, responseMode));
}
var (scope, scopeError) = RequestParametersHelper.ValidateParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Scope, _errorProvider);
if (scopeError != null)
{
scopeError.State = state;
return AuthorizationRequest.Invalid(new AuthorizationRequestError(scopeError, redirectUri, responseMode));
}
var parsedScope = scope.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var allWhiteSpace = true;
for (int i = 0; i < parsedScope.Length; i++)
{
allWhiteSpace = string.IsNullOrWhiteSpace(parsedScope[i]);
if (!allWhiteSpace)
{
break;
}
}
if (allWhiteSpace)
{
scopeError = _errorProvider.MissingRequiredParameter(OpenIdConnectParameterNames.Scope);
scopeError.State = state;
return AuthorizationRequest.Invalid(new AuthorizationRequestError(
scopeError,
redirectUri,
responseMode));
}
if (parsedResponseType.Contains(OpenIdConnectResponseType.IdToken) && !parsedScope.Contains(OpenIdConnectScope.OpenId))
{
scopeError = _errorProvider.MissingOpenIdScope();
scopeError.State = state;
return AuthorizationRequest.Invalid(new AuthorizationRequestError(
scopeError,
redirectUri,
responseMode));
}
var resolvedScopes = await _scopeValidator.ResolveScopesAsync(clientId, parsedScope);
if (!resolvedScopes.IsValid)
{
resolvedScopes.Error.State = state;
return AuthorizationRequest.Invalid(new AuthorizationRequestError(resolvedScopes.Error, redirectUri, responseMode));
}
var (prompt, promptError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.Prompt, _errorProvider);
if (promptError != null)
{
promptError.State = state;
return AuthorizationRequest.Invalid(new AuthorizationRequestError(promptError, redirectUri, responseMode));
}
if (prompt != null)
{
var parsedPrompt = prompt.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
promptError = ValidatePrompt(parsedPrompt);
if (promptError != null)
{
promptError.State = state;
return AuthorizationRequest.Invalid(new AuthorizationRequestError(promptError, redirectUri, responseMode));
}
}
var result = new OpenIdConnectMessage(requestParameters);
result.RequestType = OpenIdConnectRequestType.Authentication;
var requestGrants = new RequestGrants
{
Tokens = GetRequestedTokens(parsedResponseType, resolvedScopes.Scopes),
Scopes = resolvedScopes.Scopes.ToList(),
ResponseMode = responseMode,
RedirectUri = redirectUri
};
return await ValidateRequestAsync(AuthorizationRequest.Valid(result, requestGrants));
}
private IList<string> GetRequestedTokens(IEnumerable<string> parsedResponseType, IEnumerable<ApplicationScope> scopes)
{
var tokens = new List<string>();
foreach (var response in parsedResponseType)
{
switch (response)
{
case OpenIdConnectResponseType.Code:
tokens.Add(TokenTypes.AuthorizationCode);
break;
case OpenIdConnectResponseType.Token when HasCustomScope():
tokens.Add(TokenTypes.AccessToken);
break;
case OpenIdConnectResponseType.IdToken when HasOpenIdScope():
tokens.Add(TokenTypes.IdToken);
break;
default:
break;
}
}
return tokens;
bool HasCustomScope() => scopes.Any(s => s.ClientId != null);
bool HasOpenIdScope() => scopes.Contains(ApplicationScope.OpenId);
}
private async Task<AuthorizationRequest> ValidateRequestAsync(AuthorizationRequest authorizationRequest)
{
foreach (var validator in _validators)
{
var newRequest = await validator.ValidateRequestAsync(authorizationRequest);
if (!newRequest.IsValid)
{
return newRequest;
}
}
return authorizationRequest;
}
private OpenIdConnectMessage ValidatePrompt(string[] parsedPrompt)
{
for (int i = 0; i < parsedPrompt.Length; i++)
{
var prompt = parsedPrompt[i];
if (string.Equals(prompt, PromptValues.None, StringComparison.Ordinal))
{
if (parsedPrompt.Length > 1)
{
return _errorProvider.PromptNoneMustBeTheOnlyValue(string.Join(" ", parsedPrompt));
}
continue;
}
if (string.Equals(prompt, PromptValues.Login, StringComparison.Ordinal) ||
string.Equals(prompt, PromptValues.Consent, StringComparison.Ordinal) ||
string.Equals(prompt, PromptValues.SelectAccount, StringComparison.Ordinal))
{
continue;
}
return _errorProvider.InvalidPromptValue(prompt);
}
return null;
}
private static string GetResponseMode(string[] parsedResponseType, bool tokenRequested)
{
return tokenRequested || parsedResponseType != null && parsedResponseType.Length > 1
? OpenIdConnectResponseMode.Fragment : OpenIdConnectResponseMode.Query;
}
private OpenIdConnectMessage ValidateResponseModeTypeCombination(string responseType, bool tokenRequested, string responseMode)
{
return tokenRequested && responseMode != null && responseMode.Equals(OpenIdConnectResponseMode.Query) ?
_errorProvider.InvalidResponseTypeModeCombination(responseType, responseMode) :
null;
}
private (string responseMode, OpenIdConnectMessage responseModeError) ValidateResponseMode(IDictionary<string, string[]> parameters)
{
var (responseMode, responseModeParameterError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(parameters, OpenIdConnectParameterNames.ResponseMode, _errorProvider);
var responseModeValidationError = responseMode != null && !ValidResponseModes.Contains(responseMode) ?
_errorProvider.InvalidParameterValue(responseMode, OpenIdConnectParameterNames.ResponseMode) :
null;
var isResponseModeInvalid = responseModeParameterError != null || responseModeValidationError != null;
return (isResponseModeInvalid ? null : responseMode, responseModeParameterError ?? responseModeValidationError);
}
private (string responseType, string[] parsedResponseType, bool tokenRequested, OpenIdConnectMessage error) ValidateResponseType(IDictionary<string, string[]> parameters)
{
var (responseType, responseTypeParameterError) = RequestParametersHelper.ValidateParameterIsUnique(parameters, OpenIdConnectParameterNames.ResponseType, _errorProvider);
var parsedResponseType = responseType?.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var (tokenRequested, responseTypeValidationError) = parsedResponseType != null ? IsValidResponseTypeCombination(parsedResponseType) : (false, null);
return (responseType, parsedResponseType, tokenRequested, responseTypeParameterError ?? responseTypeValidationError);
}
private (bool tokenRequested, OpenIdConnectMessage error) IsValidResponseTypeCombination(string[] parsedResponseType)
{
var containsNone = false;
var tokenRequested = false;
for (var i = 0; i < parsedResponseType.Length; i++)
{
containsNone = containsNone || string.Equals(OpenIdConnectResponseType.None, parsedResponseType[i], StringComparison.Ordinal);
tokenRequested = tokenRequested ||
string.Equals(OpenIdConnectResponseType.Token, parsedResponseType[i], StringComparison.Ordinal) ||
string.Equals(OpenIdConnectResponseType.IdToken, parsedResponseType[i], StringComparison.Ordinal);
}
if (containsNone && parsedResponseType.Length > 1)
{
return (tokenRequested, _errorProvider.ResponseTypeNoneNotAllowed());
}
for (var i = 0; i < parsedResponseType.Length; i++)
{
if (!ValidResponseTypes.Contains(parsedResponseType[i]))
{
var error = _errorProvider.InvalidParameterValue(
parsedResponseType[i],
OpenIdConnectParameterNames.ResponseType);
return (tokenRequested, error);
}
}
return (tokenRequested, null);
}
private async Task<(string clientId, string redirectUri, OpenIdConnectMessage error)> ValidateClientIdAndRedirectUri(
IDictionary<string, string[]> requestParameters, string state)
{
var (clientId, clientIdError) = RequestParametersHelper.ValidateParameterIsUnique(requestParameters, OpenIdConnectParameterNames.ClientId, _errorProvider);
if (clientIdError != null)
{
clientIdError.State = state;
return (null, null, clientIdError);
}
if (!await _clientIdValidator.ValidateClientIdAsync(clientId))
{
clientIdError = _errorProvider.InvalidClientId(clientId);
clientIdError.State = state;
return (null, null, clientIdError);
}
var (redirectUri, redirectUriError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.RedirectUri, _errorProvider);
if (redirectUriError != null)
{
redirectUriError.State = state;
return (null, null, redirectUriError);
}
if (redirectUri != null)
{
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
{
redirectUriError = _errorProvider.InvalidUriFormat(redirectUri);
redirectUriError.State = state;
return (null, null, redirectUriError);
}
var parsedUri = new Uri(redirectUri, UriKind.Absolute);
if (!string.IsNullOrEmpty(parsedUri.Fragment))
{
redirectUriError = _errorProvider.InvalidUriFormat(redirectUri);
redirectUriError.State = state;
return (null, null, redirectUriError);
}
}
var resolvedUriResult = await _redirectUrlValidator.ResolveRedirectUriAsync(clientId, redirectUri);
if (!resolvedUriResult.IsValid)
{
resolvedUriResult.Error.State = state;
return (null, null, resolvedUriResult.Error);
}
return (clientId, resolvedUriResult.Uri, null);
}
}
}

View File

@ -0,0 +1,28 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.Service.Claims;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DefaultTokenClaimsManager : ITokenClaimsManager
{
private readonly ITokenClaimsProvider[] _providers;
public DefaultTokenClaimsManager(IEnumerable<ITokenClaimsProvider> providers)
{
_providers = providers.OrderBy(p => p.Order).ToArray();
}
public async Task CreateClaimsAsync(TokenGeneratingContext context)
{
foreach (var provider in _providers)
{
await provider.OnGeneratingClaims(context);
}
}
}
}

View File

@ -0,0 +1,145 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public class DefaultTokenClaimsProvider : ITokenClaimsProvider
{
private readonly IOptions<IdentityServiceOptions> _options;
public DefaultTokenClaimsProvider(IOptions<IdentityServiceOptions> options)
{
_options = options;
}
public int Order => 100;
public Task OnGeneratingClaims(TokenGeneratingContext context)
{
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.TokenUniqueId, Guid.NewGuid().ToString());
var userMapping = GetUserPrincipalTokenMapping(context.CurrentToken);
var applicationMapping = GetApplicationPrincipalTokenMapping(context.CurrentToken);
var ambientMapping = GetAmbientClaimsTokenMapping(context.CurrentToken);
MapFromPrincipal(context, context.User, userMapping);
MapFromPrincipal(context, context.Application, applicationMapping);
MapFromContext(context, context.AmbientClaims, ambientMapping);
if (context.IsContextForTokenTypes(TokenTypes.AccessToken, TokenTypes.IdToken))
{
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.Issuer, _options.Value.Issuer);
}
if (context.IsContextForTokenTypes(TokenTypes.AuthorizationCode) && context.RequestParameters.RedirectUri != null)
{
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.RedirectUri, context.RequestParameters.RedirectUri);
}
return Task.CompletedTask;
}
private TokenMapping GetAmbientClaimsTokenMapping(string tokenType)
{
switch (tokenType)
{
case TokenTypes.AuthorizationCode:
return _options.Value.AuthorizationCodeOptions.ContextClaims;
case TokenTypes.AccessToken:
return _options.Value.AccessTokenOptions.ContextClaims;
case TokenTypes.IdToken:
return _options.Value.IdTokenOptions.ContextClaims;
case TokenTypes.RefreshToken:
return _options.Value.RefreshTokenOptions.ContextClaims;
default:
throw new InvalidOperationException();
}
}
private TokenMapping GetApplicationPrincipalTokenMapping(string tokenType)
{
switch (tokenType)
{
case TokenTypes.AuthorizationCode:
return _options.Value.AuthorizationCodeOptions.ApplicationClaims;
case TokenTypes.AccessToken:
return _options.Value.AccessTokenOptions.ApplicationClaims;
case TokenTypes.IdToken:
return _options.Value.IdTokenOptions.ApplicationClaims;
case TokenTypes.RefreshToken:
return _options.Value.RefreshTokenOptions.ApplicationClaims;
default:
throw new InvalidOperationException();
}
}
private TokenMapping GetUserPrincipalTokenMapping(string tokenType)
{
switch (tokenType)
{
case TokenTypes.AuthorizationCode:
return _options.Value.AuthorizationCodeOptions.UserClaims;
case TokenTypes.AccessToken:
return _options.Value.AccessTokenOptions.UserClaims;
case TokenTypes.IdToken:
return _options.Value.IdTokenOptions.UserClaims;
case TokenTypes.RefreshToken:
return _options.Value.RefreshTokenOptions.UserClaims;
default:
throw new InvalidOperationException();
}
}
private static void MapFromPrincipal(
TokenGeneratingContext context,
ClaimsPrincipal user,
TokenMapping claimsDefinition)
{
foreach (var mapping in claimsDefinition)
{
var foundClaims = user.FindAll(mapping.Alias);
ValidateCardinality(mapping, foundClaims, claimsDefinition.Source);
foreach (var userClaim in foundClaims)
{
context.AddClaimToCurrentToken(mapping.Name, userClaim.Value);
}
}
}
private static void MapFromContext(
TokenGeneratingContext context,
IList<Claim> ambientClaims,
TokenMapping claimsDefinition)
{
foreach (var mapping in claimsDefinition)
{
var ctxValues = ambientClaims.Where(c => c.Type == mapping.Alias);
ValidateCardinality(mapping, ctxValues, claimsDefinition.Source);
foreach (var ctxValue in ctxValues)
{
context.AddClaimToCurrentToken(mapping.Name, ctxValue.Value);
}
}
}
private static void ValidateCardinality<T>(TokenValueDescriptor mapping, IEnumerable<T> foundClaims, string source)
{
if (mapping.Cardinality != TokenValueCardinality.Zero && !foundClaims.Any())
{
throw new InvalidOperationException($"Missing '{mapping.Alias}' claim from the {source}.");
}
if (mapping.Cardinality != TokenValueCardinality.Many && foundClaims.Skip(1).Any())
{
throw new InvalidOperationException($"Multiple claims found for '{mapping.Alias}' claim from the {source}.");
}
}
}
}

View File

@ -0,0 +1,53 @@
// 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.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public class GrantedTokensTokenClaimsProvider : ITokenClaimsProvider
{
public int Order => 100;
public Task OnGeneratingClaims(TokenGeneratingContext context)
{
if (context.IsContextForTokenTypes(TokenTypes.AuthorizationCode))
{
foreach (var grantedToken in GetGrantedTokensForAuthorizationCode(context))
{
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.GrantedToken, grantedToken);
}
}
if (context.IsContextForTokenTypes(TokenTypes.RefreshToken))
{
foreach (var grantedToken in context.RequestGrants.Tokens)
{
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.GrantedToken, grantedToken);
}
}
return Task.CompletedTask;
}
private IEnumerable<string> GetGrantedTokensForAuthorizationCode(TokenGeneratingContext context)
{
if (context.RequestGrants.Scopes.Any(s => s.ClientId != null))
{
yield return TokenTypes.AccessToken;
}
if (context.RequestGrants.Scopes.Contains(ApplicationScope.OpenId))
{
yield return TokenTypes.IdToken;
}
if (context.RequestGrants.Scopes.Contains(ApplicationScope.OfflineAccess))
{
yield return TokenTypes.RefreshToken;
}
}
}
}

View File

@ -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.Linq;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public class NonceTokenClaimsProvider : ITokenClaimsProvider
{
public int Order => 100;
public Task OnGeneratingClaims(TokenGeneratingContext context)
{
var nonce = GetNonce(context);
if (context.IsContextForTokenTypes(
TokenTypes.IdToken,
TokenTypes.AccessToken,
TokenTypes.AuthorizationCode) && nonce != null)
{
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.Nonce, nonce);
}
return Task.CompletedTask;
}
private string GetNonce(TokenGeneratingContext context) =>
context.RequestParameters.RequestType == OpenIdConnectRequestType.Authentication ?
context.RequestParameters.Nonce :
context.RequestGrants.Claims.SingleOrDefault(c => c.Type.Equals(IdentityServiceClaimTypes.Nonce))?.Value;
}
}

View File

@ -0,0 +1,86 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public class ScopesTokenClaimsProvider : ITokenClaimsProvider
{
public int Order => 100;
public Task OnGeneratingClaims(TokenGeneratingContext context)
{
var resource = context.RequestGrants.Scopes.FirstOrDefault(rg => rg.ClientId != null)?.ClientId;
if (context.IsContextForTokenTypes(TokenTypes.AccessToken) && resource != null)
{
// For access tokens we use the scopes from the set of granted scopes, this takes into account the
// fact that a token request can ask for a subset of the scopes granted during authorization, either
// on a code exchange or on a refresh token grant flow.
AddClaimsForAccessToken(context, resource);
return Task.CompletedTask;
}
if (context.IsContextForTokenTypes(TokenTypes.AuthorizationCode))
{
context.AddClaimToCurrentToken(
IdentityServiceClaimTypes.Scope,
GetScopeValue(context.RequestGrants.Scopes, excludeCanonical: false));
if (resource != null)
{
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.Resource, resource);
}
return Task.CompletedTask;
}
if (context.IsContextForTokenTypes(TokenTypes.RefreshToken))
{
// For refresh tokens the scope claim never changes as the set of scopes granted for a refresh token
// should not change no matter what scopes are sent on a token request.
var scopeClaim = context
.RequestGrants
.Claims
.Single(c => c.Type.Equals(IdentityServiceClaimTypes.Scope, StringComparison.Ordinal));
var resourceClaim = context
.RequestGrants
.Claims
.SingleOrDefault(c => c.Type.Equals(IdentityServiceClaimTypes.Resource, StringComparison.Ordinal));
context.AddClaimToCurrentToken(scopeClaim);
if (resourceClaim != null)
{
context.AddClaimToCurrentToken(resourceClaim);
}
}
return Task.CompletedTask;
}
private void AddClaimsForAccessToken(TokenGeneratingContext context, string resource)
{
var scopes = context.RequestGrants.Scopes;
var accessTokenScopes = GetAccessTokenScopes(scopes);
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.Scope, GetScopeValue(scopes, excludeCanonical: true));
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.Audience, resource);
context.AddClaimToCurrentToken(IdentityServiceClaimTypes.AuthorizedParty, context.RequestParameters.ClientId);
}
private IEnumerable<ApplicationScope> GetAccessTokenScopes(IEnumerable<ApplicationScope> applicationScopes) =>
applicationScopes.Where(s => s.ClientId != null);
private string GetScopeValue(IEnumerable<ApplicationScope> scopes, bool excludeCanonical) =>
!excludeCanonical ?
string.Join(" ", scopes.Select(s => s.Scope)) :
string.Join(" ", scopes.Where(s => s.ClientId != null).Select(s => s.Scope));
}
}

View File

@ -0,0 +1,66 @@
// 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.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public class TimestampsTokenClaimsProvider : ITokenClaimsProvider
{
private readonly ITimeStampManager _timeStampManager;
private readonly IOptions<IdentityServiceOptions> _options;
public TimestampsTokenClaimsProvider(
ITimeStampManager timestampManager,
IOptions<IdentityServiceOptions> options)
{
_timeStampManager = timestampManager;
_options = options;
}
public int Order => 100;
public Task OnGeneratingClaims(TokenGeneratingContext context)
{
var options = GetOptions(context.CurrentToken);
context.CurrentClaims.Add(new Claim(
IdentityServiceClaimTypes.NotBefore,
_timeStampManager.GetTimeStampInEpochTime(options.NotValidBefore)));
context.CurrentClaims.Add(new Claim(
IdentityServiceClaimTypes.IssuedAt,
_timeStampManager.GetCurrentTimeStampInEpochTime()));
context.CurrentClaims.Add(new Claim(
IdentityServiceClaimTypes.Expires,
_timeStampManager.GetTimeStampInEpochTime(options.NotValidAfter)));
return Task.CompletedTask;
}
private TokenOptions GetOptions(string tokenType)
{
switch (tokenType)
{
case TokenTypes.AccessToken:
return _options.Value.AccessTokenOptions;
case TokenTypes.AuthorizationCode:
return _options.Value.AuthorizationCodeOptions;
case TokenTypes.IdToken:
return _options.Value.IdTokenOptions;
case TokenTypes.RefreshToken:
return _options.Value.RefreshTokenOptions;
default:
throw new InvalidOperationException();
}
}
public Task OnValidatingClaims(TokenGeneratingContext context)
{
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,53 @@
// 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 System.Security.Claims;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service.Claims
{
public class TokenHashTokenClaimsProvider : ITokenClaimsProvider
{
private readonly ITokenHasher _tokenHasher;
public TokenHashTokenClaimsProvider(ITokenHasher tokenHasher)
{
_tokenHasher = tokenHasher;
}
public int Order => 100;
public Task OnGeneratingClaims(TokenGeneratingContext context)
{
if (context.IsContextForTokenTypes(TokenTypes.IdToken))
{
var accessToken = context
.IssuedTokens.SingleOrDefault(t => t.Token.Kind == TokenTypes.AccessToken);
var authorizationCode = context
.IssuedTokens.SingleOrDefault(t => t.Token.Kind == TokenTypes.AuthorizationCode);
if (accessToken != null)
{
context.CurrentClaims.Add(new Claim(
IdentityServiceClaimTypes.AccessTokenHash,
GetTokenHash(accessToken.SerializedValue)));
}
if (authorizationCode != null)
{
context.CurrentClaims.Add(new Claim(
IdentityServiceClaimTypes.CodeHash,
GetTokenHash(authorizationCode.SerializedValue)));
}
}
return Task.CompletedTask;
}
private string GetTokenHash(string token)
{
return _tokenHasher.HashToken(token, "RS256");
}
}
}

View File

@ -0,0 +1,99 @@
// 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.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service
{
internal static class CryptographyHelpers
{
public static string FindAlgorithm(X509Certificate2 certificate)
{
var rsapk = certificate.GetRSAPublicKey();
if (rsapk != null)
{
return "RS256";
}
else
{
throw new InvalidOperationException("Algorithm not supported.");
}
}
public static void ValidateRsaKeyLength(X509Certificate2 certificate)
{
var rsa = certificate.GetRSAPublicKey();
if (rsa == null)
{
throw new InvalidOperationException("Algorithm not supported.");
}
ValidateRsaKeyLength(rsa);
}
public static void ValidateRsaKeyLength(RSA rsa)
{
if (rsa.KeySize < 2048)
{
throw new InvalidOperationException("The RSA key must be at least 2048 bits long.");
}
return;
}
public static RSA CreateRsaAlgorithm() => RSA.Create(2048);
public static SHA256 CreateSHA256() => SHA256.Create();
public static RSAParameters GetRSAParameters(SigningCredentials credentials)
{
RSA algorithm = null;
if (credentials.Key is X509SecurityKey x509SecurityKey)
{
algorithm = x509SecurityKey.PublicKey as RSA;
}
if (credentials.Key is RsaSecurityKey rsaSecurityKey)
{
algorithm = rsaSecurityKey.Rsa;
if (algorithm == null)
{
var rsa = RSA.Create();
rsa.ImportParameters(rsaSecurityKey.Parameters);
algorithm = rsa;
}
}
var parameters = algorithm.ExportParameters(includePrivateParameters: false);
return parameters;
}
public static string GetAlgorithm(SigningCredentials credentials)
{
RSA algorithm = null;
if (credentials.Key is X509SecurityKey x509SecurityKey && x509SecurityKey.PublicKey is RSA)
{
return JsonWebAlgorithmsKeyTypes.RSA;
}
var rsaSecurityKey = credentials.Key as RsaSecurityKey;
// Check that the key has either an Asymetric Algorithm assigned or that at least
// one of the RSA parameters are initialized to consider the key "valid".
if (rsaSecurityKey != null &&
(rsaSecurityKey.Rsa != null || rsaSecurityKey.Parameters.Modulus != null))
{
return JsonWebAlgorithmsKeyTypes.RSA;
}
if (algorithm != null)
{
return JsonWebAlgorithmsKeyTypes.RSA;
}
throw new NotSupportedException();
}
}
}

View File

@ -0,0 +1,35 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DefaultAuthorizationResponseFactory : IAuthorizationResponseFactory
{
private readonly IAuthorizationResponseParameterProvider[] _providers;
public DefaultAuthorizationResponseFactory(IEnumerable<IAuthorizationResponseParameterProvider> providers)
{
_providers = providers.OrderBy(p => p.Order).ToArray();
}
public async Task<AuthorizationResponse> CreateAuthorizationResponseAsync(TokenGeneratingContext context)
{
var result = new AuthorizationResponse();
result.Message = new OpenIdConnectMessage();
result.ResponseMode = context.RequestGrants.ResponseMode;
result.RedirectUri = context.RequestGrants.RedirectUri;
foreach (var provider in _providers)
{
await provider.AddParameters(context, result);
}
return result;
}
}
}

View File

@ -0,0 +1,56 @@
// 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.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DefaultAuthorizationResponseParameterProvider : IAuthorizationResponseParameterProvider
{
private readonly ITimeStampManager _manager;
public int Order => 100;
public DefaultAuthorizationResponseParameterProvider(ITimeStampManager manager)
{
_manager = manager;
}
public Task AddParameters(TokenGeneratingContext context, AuthorizationResponse response)
{
if (context.AuthorizationCode != null)
{
response.Message.Code = context.AuthorizationCode.SerializedValue;
}
if (context.AccessToken != null)
{
response.Message.AccessToken = context.AccessToken.SerializedValue;
response.Message.TokenType = "Bearer";
response.Message.ExpiresIn = GetExpirationTime(context.AccessToken.Token);
response.Message.Scope = string.Join(" ", context.RequestGrants.Scopes.Select(s => s.Scope));
}
if (context.IdToken != null)
{
response.Message.IdToken = context.IdToken.SerializedValue;
}
response.Message.State = context.RequestParameters.State;
return Task.CompletedTask;
}
private string GetExpirationTime(Token token)
{
if (token.Expires < token.IssuedAt)
{
throw new InvalidOperationException("Can't expire before issuance.");
}
return _manager.GetDurationInSeconds(token.Expires, token.IssuedAt).ToString(CultureInfo.InvariantCulture);
}
}
}

View File

@ -0,0 +1,43 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DefaultConfigurationManager : IConfigurationManager
{
private readonly IConfigurationMetadataProvider[] _providers;
private readonly ConcurrentDictionary<string, Lazy<Task<OpenIdConnectConfiguration>>> _configurations =
new ConcurrentDictionary<string, Lazy<Task<OpenIdConnectConfiguration>>>();
public DefaultConfigurationManager(
IEnumerable<IConfigurationMetadataProvider> providers)
{
_providers = providers.OrderBy(p => p.Order).ToArray();
}
public async Task<OpenIdConnectConfiguration> GetConfigurationAsync(ConfigurationContext context)
{
return await _configurations.GetOrAdd(
context.Id,
new Lazy<Task<OpenIdConnectConfiguration>>(CreateConfiguration)).Value;
async Task<OpenIdConnectConfiguration> CreateConfiguration()
{
var configuration = new OpenIdConnectConfiguration();
foreach (var provider in _providers)
{
await provider.ConfigureMetadataAsync(configuration, context);
}
return configuration;
}
}
}
}

View File

@ -0,0 +1,88 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DefaultSigningCredentialsPolicyProvider : ISigningCredentialsPolicyProvider
{
private readonly IEnumerable<ISigningCredentialsSource> _sources;
private readonly IHostingEnvironment _environment;
private SigningCredentialsDescriptor[] _credentials;
private readonly ITimeStampManager _timeStampManager;
public DefaultSigningCredentialsPolicyProvider(
IEnumerable<ISigningCredentialsSource> sources,
ITimeStampManager timeStampManager,
IHostingEnvironment environment)
{
_sources = sources;
_timeStampManager = timeStampManager;
_environment = environment;
}
public async Task<IEnumerable<SigningCredentialsDescriptor>> GetAllCredentialsAsync()
{
if (_credentials == null || CredentialsExpired())
{
// This has the potential to spin up multiple calls to RetrieveCredentials
// we might consider an alternative pattern in which we hold a task in this
// instance and upon expired credentials we lock, make the call to retrieve
// credentials, swap the task on the instance, release the lock and then await.
_credentials = await RetrieveCredentials();
}
return _credentials;
async Task<SigningCredentialsDescriptor[]> RetrieveCredentials()
{
var credentialsFromSources = await Task.WhenAll(_sources.Select(s => s.GetCredentials()));
var finalList = new List<SigningCredentialsDescriptor>();
foreach (var credential in credentialsFromSources.SelectMany(c => c))
{
if (!_environment.IsDevelopment() && credential.Id.StartsWith("IdentityService.Development"))
{
continue;
}
finalList.Add(credential);
}
return finalList.OrderBy(o => o.NotBefore).ThenBy(d => d.Expires).ToArray();
}
}
private bool CredentialsExpired()
{
foreach (var credential in _credentials)
{
if (_timeStampManager.IsValidPeriod(credential.NotBefore, credential.Expires))
{
return false;
}
}
return true;
}
public async Task<SigningCredentialsDescriptor> GetSigningCredentialsAsync()
{
var credentials = await GetAllCredentialsAsync();
foreach (var credential in credentials)
{
if (_timeStampManager.IsValidPeriod(credential.NotBefore, credential.Expires))
{
return credential;
}
}
throw new InvalidOperationException("Could not find valid credentials to use for signing.");
}
}
}

View File

@ -0,0 +1,88 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service.Core
{
public class DefaultSigningCredentialsSource : ISigningCredentialsSource
{
private readonly IOptionsSnapshot<IdentityServiceOptions> _options;
private readonly ITimeStampManager _timeStampManager;
public DefaultSigningCredentialsSource(
IOptionsSnapshot<IdentityServiceOptions> options,
ITimeStampManager timeStampManager)
{
_options = options;
_timeStampManager = timeStampManager;
}
public Task<IEnumerable<SigningCredentialsDescriptor>> GetCredentials()
{
var descriptors = GetDescriptors(_options.Get(Options.DefaultName));
return Task.FromResult(descriptors);
}
private IEnumerable<SigningCredentialsDescriptor> GetDescriptors(IdentityServiceOptions options)
{
return options.SigningKeys.Select(sk =>
{
var validity = GetValidity(sk);
return new SigningCredentialsDescriptor(
sk,
CryptographyHelpers.GetAlgorithm(sk),
validity.NotBefore,
validity.Expires,
GetMetadata(sk));
});
}
private Validity GetValidity(SigningCredentials credentials)
{
var x509SecurityKey = credentials.Key as X509SecurityKey;
if (x509SecurityKey != null)
{
var certificate = x509SecurityKey.Certificate;
return new Validity
{
NotBefore = certificate.NotBefore.ToUniversalTime(),
Expires = certificate.NotAfter.ToUniversalTime()
};
}
var rsaSecurityKey = credentials.Key as RsaSecurityKey;
if (rsaSecurityKey != null)
{
return new Validity
{
NotBefore = _timeStampManager.GetCurrentTimeStampUtcAsDateTime(),
Expires = _timeStampManager.GetTimeStampUtcAsDateTime(TimeSpan.FromDays(1))
};
}
throw new NotSupportedException();
}
private struct Validity
{
public DateTimeOffset NotBefore;
public DateTimeOffset Expires;
}
private IDictionary<string, string> GetMetadata(SigningCredentials credentials)
{
var rsaParameters = CryptographyHelpers.GetRSAParameters(credentials);
return new Dictionary<string, string>
{
[JsonWebKeyParameterNames.E] = Base64UrlEncoder.Encode(rsaParameters.Exponent),
[JsonWebKeyParameterNames.N] = Base64UrlEncoder.Encode(rsaParameters.Modulus),
};
}
}
}

View File

@ -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 System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DefaultTokenResponseFactory : ITokenResponseFactory
{
private readonly ITokenResponseParameterProvider[] _providers;
public DefaultTokenResponseFactory(IEnumerable<ITokenResponseParameterProvider> providers)
{
_providers = providers.OrderBy(o => o.Order).ToArray();
}
public async Task<OpenIdConnectMessage> CreateTokenResponseAsync(TokenGeneratingContext context)
{
var response = new OpenIdConnectMessage();
foreach (var provider in _providers)
{
await provider.AddParameters(context, response);
}
return response;
}
}
}

View File

@ -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;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class DefaultTokenResponseParameterProvider : ITokenResponseParameterProvider
{
private readonly ITimeStampManager _manager;
public int Order => 100;
public DefaultTokenResponseParameterProvider(ITimeStampManager manager)
{
_manager = manager;
}
public Task AddParameters(TokenGeneratingContext context, OpenIdConnectMessage response)
{
if (context.IdToken != null)
{
response.IdToken = context.IdToken.SerializedValue;
var expiresIn = _manager.GetDurationInSeconds(
context.IdToken.Token.Expires,
context.IdToken.Token.IssuedAt);
response.Parameters.Add(
"id_token_expires_in",
expiresIn.ToString(CultureInfo.InvariantCulture));
}
if (context.AccessToken != null)
{
response.AccessToken = context.AccessToken.SerializedValue;
response.ExpiresIn = GetExpirationTime(context.AccessToken.Token);
response.Parameters["expires_on"] = context.AccessToken.Token.GetClaimValue(IdentityServiceClaimTypes.Expires);
response.Parameters["not_before"] = context.AccessToken.Token.GetClaimValue(IdentityServiceClaimTypes.NotBefore);
response.Resource = context.RequestGrants.Scopes.First(s => s.ClientId != null).ClientId;
}
if (context.RefreshToken != null)
{
response.RefreshToken = context.RefreshToken.SerializedValue;
var expiresIn = _manager.GetDurationInSeconds(
context.RefreshToken.Token.Expires,
context.RefreshToken.Token.IssuedAt);
response.Parameters.Add(
"refresh_token_expires_in",
expiresIn.ToString(CultureInfo.InvariantCulture));
}
response.TokenType = "Bearer";
return Task.CompletedTask;
}
private static string GetExpirationTime(Token token)
{
if (token.Expires < token.IssuedAt)
{
throw new InvalidOperationException("Can't expire before issuance.");
}
var expirationTimeInSeconds = Math.Truncate((token.Expires - token.IssuedAt).TotalSeconds);
checked
{
return ((long)expirationTimeInSeconds).ToString(CultureInfo.InvariantCulture);
}
}
}
}

View File

@ -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.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Identity.Service
{
public class FormPostResponseGenerator
{
private const string FormPostHeaderFormat = @"<!doctype html>
<html>
<head>
<title>Please wait while you're being redirected to the identity provider</title>
</head>
<body>
<form name=""form"" method=""post"" action=""{0}"">";
private const string FormPostParameterFormat = @" <input type=""hidden"" name=""{0}"" value=""{1}"" />";
private const string FormPostFooterFormat =
@" <noscript>Click here to finish the process: <input type=""submit"" /></noscript>
</form>
<script>document.form.submit();</script>
</body>
</html>";
private readonly HtmlEncoder _encoder;
public FormPostResponseGenerator(HtmlEncoder encoder)
{
_encoder = encoder;
}
public async Task GenerateResponseAsync(
HttpContext context,
string redirectUri,
IEnumerable<KeyValuePair<string, string>> parameters)
{
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 1024, leaveOpen: true))
{
writer.WriteLine(FormPostHeaderFormat, redirectUri);
foreach (var parameter in parameters)
{
writer.WriteLine(FormPostParameterFormat, _encoder.Encode(parameter.Key), _encoder.Encode(parameter.Value));
}
writer.Write(FormPostFooterFormat);
};
stream.Seek(0, SeekOrigin.Begin);
context.Response.ContentType = "text/html; charset=utf-8";
context.Response.ContentLength = stream.Length;
await stream.CopyToAsync(context.Response.Body);
}
}
}
}

View File

@ -0,0 +1,62 @@
// 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.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class FragmentResponseGenerator
{
private readonly UrlEncoder _urlEncoder;
public FragmentResponseGenerator(UrlEncoder urlEncoder)
{
_urlEncoder = urlEncoder;
}
public void GenerateResponse(
HttpContext context,
string redirectUri,
IEnumerable<KeyValuePair<string, string>> parameters)
{
var builder = new StringBuilder();
builder.Append(redirectUri);
builder.Append('#');
var enumerator = parameters.GetEnumerator();
while (enumerator.MoveNext())
{
if (!ShouldSkipKey(enumerator.Current.Key))
{
builder.Append(_urlEncoder.Encode(enumerator.Current.Key));
builder.Append('=');
builder.Append(_urlEncoder.Encode(enumerator.Current.Value));
break;
}
}
while (enumerator.MoveNext())
{
if (!ShouldSkipKey(enumerator.Current.Key))
{
builder.Append('&');
builder.Append(_urlEncoder.Encode(enumerator.Current.Key));
builder.Append('=');
builder.Append(_urlEncoder.Encode(enumerator.Current.Value));
}
}
context.Response.Redirect(builder.ToString());
}
private bool ShouldSkipKey(string key)
{
return string.Equals(key, OpenIdConnectParameterNames.RedirectUri, StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@ -0,0 +1,72 @@
// 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.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service
{
public static class IdentityServiceBuilderExtensions
{
public static IIdentityServiceBuilder AddSigningCertificate(
this IIdentityServiceBuilder builder,
X509Certificate2 certificate)
{
CryptographyHelpers.ValidateRsaKeyLength(certificate);
var key = new X509SecurityKey(certificate);
builder.Services.Configure<IdentityServiceOptions>(
options =>
{
var algorithm = CryptographyHelpers.FindAlgorithm(certificate);
options.SigningKeys.Add(new SigningCredentials(key, algorithm));
});
return builder;
}
public static IIdentityServiceBuilder AddSigningCertificates(
this IIdentityServiceBuilder builder,
IEnumerable<X509Certificate2> certificates)
{
foreach (var certificate in certificates)
{
builder.AddSigningCertificate(certificate);
}
return builder;
}
public static IIdentityServiceBuilder AddSigningCertificates(
this IIdentityServiceBuilder builder,
Func<IEnumerable<X509Certificate2>> certificatesLoader)
{
builder.Services.Configure<IdentityServiceOptions>(o =>
{
var certificates = certificatesLoader();
foreach (var certificate in certificates)
{
var algorithm = CryptographyHelpers.FindAlgorithm(certificate);
o.SigningKeys.Add(new SigningCredentials(new X509SecurityKey(certificate), algorithm));
}
});
return builder;
}
public static IIdentityServiceBuilder AddSigningCertificate(this IIdentityServiceBuilder builder, Func<X509Certificate2> func)
{
var cert = func();
if (cert == null)
{
return builder;
}
else
{
return builder.AddSigningCertificate(cert);
}
}
}
}

View File

@ -0,0 +1,90 @@
// 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.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service
{
public class JwtAccessTokenIssuer : IAccessTokenIssuer
{
private static readonly string[] ClaimsToFilter = new string[]
{
IdentityServiceClaimTypes.TokenUniqueId,
IdentityServiceClaimTypes.ObjectId,
IdentityServiceClaimTypes.Issuer,
IdentityServiceClaimTypes.Audience,
IdentityServiceClaimTypes.IssuedAt,
IdentityServiceClaimTypes.Expires,
IdentityServiceClaimTypes.NotBefore,
};
private readonly JwtSecurityTokenHandler _handler;
private readonly IdentityServiceOptions _options;
private readonly ITokenClaimsManager _claimsManager;
private readonly ISigningCredentialsPolicyProvider _credentialsProvider;
public JwtAccessTokenIssuer(
ITokenClaimsManager claimsManager,
ISigningCredentialsPolicyProvider credentialsProvider,
JwtSecurityTokenHandler handler,
IOptions<IdentityServiceOptions> options)
{
_claimsManager = claimsManager;
_credentialsProvider = credentialsProvider;
_handler = handler;
_options = options.Value;
}
public async Task IssueAccessTokenAsync(TokenGeneratingContext context)
{
var accessToken = await CreateAccessTokenAsync(context);
var subjectIdentity = CreateSubject(accessToken);
var descriptor = new SecurityTokenDescriptor();
descriptor.Issuer = accessToken.Issuer;
descriptor.Audience = accessToken.Audience;
descriptor.Subject = subjectIdentity;
descriptor.Expires = accessToken.Expires.UtcDateTime;
descriptor.NotBefore = accessToken.NotBefore.UtcDateTime;
var credentialsDescriptor = await _credentialsProvider.GetSigningCredentialsAsync();
descriptor.SigningCredentials = credentialsDescriptor.Credentials;
var token = _handler.CreateJwtSecurityToken(descriptor);
token.Payload.Remove(IdentityServiceClaimTypes.JwtId);
token.Payload.Remove(IdentityServiceClaimTypes.IssuedAt);
//token.Payload.Add(IdentityServiceClaimTypes.JwtId, accessToken.Id);
context.AddToken(new TokenResult(accessToken, _handler.WriteToken(token), TokenKinds.Bearer));
}
private ClaimsIdentity CreateSubject(AccessToken accessToken) => new ClaimsIdentity(GetFilteredClaims(accessToken));
private IEnumerable<Claim> GetFilteredClaims(AccessToken token)
{
foreach (var claim in token)
{
if (!ClaimsToFilter.Contains(claim.Type))
{
yield return claim;
}
}
}
private async Task<AccessToken> CreateAccessTokenAsync(TokenGeneratingContext context)
{
await _claimsManager.CreateClaimsAsync(context);
var claims = context.CurrentClaims;
return new AccessToken(claims);
}
}
}

View File

@ -0,0 +1,109 @@
// 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.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service
{
public class JwtIdTokenIssuer : IIdTokenIssuer
{
private static readonly string[] ClaimsToFilter = new string[]
{
IdentityServiceClaimTypes.TokenUniqueId,
IdentityServiceClaimTypes.Issuer,
IdentityServiceClaimTypes.Audience,
IdentityServiceClaimTypes.IssuedAt,
IdentityServiceClaimTypes.Expires,
IdentityServiceClaimTypes.NotBefore,
IdentityServiceClaimTypes.Nonce,
IdentityServiceClaimTypes.CodeHash,
IdentityServiceClaimTypes.AccessTokenHash,
};
private readonly ITokenClaimsManager _claimsManager;
private readonly JwtSecurityTokenHandler _handler;
private readonly IdentityServiceOptions _options;
private readonly ISigningCredentialsPolicyProvider _credentialsProvider;
public JwtIdTokenIssuer(
ITokenClaimsManager claimsManager,
ISigningCredentialsPolicyProvider credentialsProvider,
JwtSecurityTokenHandler handler,
IOptions<IdentityServiceOptions> options)
{
_claimsManager = claimsManager;
_credentialsProvider = credentialsProvider;
_handler = handler;
_options = options.Value;
}
public async Task IssueIdTokenAsync(TokenGeneratingContext context)
{
var idToken = await CreateIdTokenAsync(context);
var subjectIdentity = CreateSubject(idToken);
var descriptor = new SecurityTokenDescriptor();
descriptor.Issuer = idToken.Issuer;
descriptor.Audience = idToken.Audience;
descriptor.Subject = subjectIdentity;
descriptor.IssuedAt = idToken.IssuedAt.UtcDateTime;
descriptor.Expires = idToken.Expires.UtcDateTime;
descriptor.NotBefore = idToken.NotBefore.UtcDateTime;
var credentialsDescriptor = await _credentialsProvider.GetSigningCredentialsAsync();
descriptor.SigningCredentials = credentialsDescriptor.Credentials;
var token = _handler.CreateJwtSecurityToken(descriptor);
token.Payload.Remove(IdentityServiceClaimTypes.JwtId);
//token.Payload.Add(IdentityServiceClaimTypes.JwtId, idToken.Id);
if (idToken.Nonce != null)
{
token.Payload.AddClaim(new Claim(IdentityServiceClaimTypes.Nonce, idToken.Nonce));
}
if (idToken.CodeHash != null)
{
token.Payload.AddClaim(new Claim(IdentityServiceClaimTypes.CodeHash, idToken.CodeHash));
}
if (idToken.AccessTokenHash != null)
{
token.Payload.AddClaim(new Claim(IdentityServiceClaimTypes.AccessTokenHash, idToken.AccessTokenHash));
}
context.AddToken(new TokenResult(idToken, _handler.WriteToken(token)));
}
private ClaimsIdentity CreateSubject(IdToken idToken) =>
new ClaimsIdentity(GetFilteredClaims(idToken));
private IEnumerable<Claim> GetFilteredClaims(IdToken token)
{
foreach (var claim in token)
{
if (!ClaimsToFilter.Contains(claim.Type))
{
yield return claim;
}
}
}
private async Task<IdToken> CreateIdTokenAsync(TokenGeneratingContext context)
{
await _claimsManager.CreateClaimsAsync(context);
var claims = context.CurrentClaims;
return new IdToken(claims);
}
}
}

View File

@ -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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service
{
public class LogoutRequestFactory : ILogoutRequestFactory
{
private readonly IRedirectUriResolver _redirectUriValidator;
private readonly ProtocolErrorProvider _errorProvider;
public LogoutRequestFactory(
IRedirectUriResolver redirectUriValidator,
ProtocolErrorProvider errorProvider)
{
_redirectUriValidator = redirectUriValidator;
_errorProvider = errorProvider;
}
public async Task<LogoutRequest> CreateLogoutRequestAsync(IDictionary<string, string[]> requestParameters)
{
var (state, stateError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.State, _errorProvider);
if (stateError != null)
{
return LogoutRequest.Invalid(stateError);
}
var (logoutRedirectUri, redirectUriError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.PostLogoutRedirectUri, _errorProvider);
if (redirectUriError != null)
{
return LogoutRequest.Invalid(redirectUriError);
}
var (idTokenHint,idTokenHintError) = RequestParametersHelper.ValidateOptionalParameterIsUnique(requestParameters, OpenIdConnectParameterNames.IdTokenHint, _errorProvider);
if (idTokenHintError != null)
{
return LogoutRequest.Invalid(idTokenHintError);
}
var redirectUriValidationResult = await _redirectUriValidator.ResolveLogoutUriAsync(null, logoutRedirectUri);
if (!redirectUriValidationResult.IsValid)
{
return LogoutRequest.Invalid(redirectUriValidationResult.Error);
}
return LogoutRequest.Valid(new OpenIdConnectMessage(requestParameters),redirectUriValidationResult.Uri);
}
}
}

View File

@ -0,0 +1,45 @@
// 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 Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Identity.Service.Metadata
{
public class DefaultConfigurationMetadataProvider : IConfigurationMetadataProvider
{
private readonly IOptions<IdentityServiceOptions> _options;
public DefaultConfigurationMetadataProvider(IOptions<IdentityServiceOptions> options)
{
_options = options;
}
public int Order { get; } = 1000;
public Task ConfigureMetadataAsync(OpenIdConnectConfiguration configuration, ConfigurationContext context)
{
configuration.Issuer = _options.Value.Issuer;
configuration.AuthorizationEndpoint = context.AuthorizationEndpoint;
configuration.TokenEndpoint = context.TokenEndpoint;
configuration.JwksUri = context.JwksUriEndpoint;
configuration.EndSessionEndpoint = context.EndSessionEndpoint;
configuration.ResponseModesSupported.Add(OpenIdConnectResponseMode.Query);
configuration.ResponseModesSupported.Add(OpenIdConnectResponseMode.Fragment);
configuration.ResponseModesSupported.Add(OpenIdConnectResponseMode.FormPost);
configuration.ResponseTypesSupported.Add(OpenIdConnectResponseType.Code);
configuration.ResponseTypesSupported.Add(OpenIdConnectResponseType.IdToken);
configuration.ResponseTypesSupported.Add(OpenIdConnectResponseType.CodeIdToken);
configuration.ScopesSupported.Add("openid");
configuration.SubjectTypesSupported.Add("pairwise");
configuration.IdTokenSigningAlgValuesSupported.Add("RS256");
configuration.TokenEndpointAuthMethodsSupported.Add("client_secret_post");
configuration.ClaimsSupported.Add("oid");
configuration.ClaimsSupported.Add("sub");
configuration.ClaimsSupported.Add("name");
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,85 @@
// 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.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Identity.Service.Metadata
{
public class DefaultKeySetMetadataProvider : IKeySetMetadataProvider
{
private readonly ISigningCredentialsPolicyProvider _provider;
public DefaultKeySetMetadataProvider(ISigningCredentialsPolicyProvider provider)
{
_provider = provider;
}
public async Task<JsonWebKeySet> GetKeysAsync()
{
var keySet = new JsonWebKeySet();
var credentials = await _provider.GetAllCredentialsAsync();
foreach (var key in credentials)
{
keySet.Keys.Add(CreateJsonWebKey(key));
}
return keySet;
}
private JsonWebKey CreateJsonWebKey(SigningCredentialsDescriptor descriptor)
{
var jsonWebKey = new JsonWebKey
{
Kid = descriptor.Id,
Use = JsonWebKeyUseNames.Sig,
Kty = descriptor.Algorithm
};
if (!descriptor.Algorithm.Equals(JsonWebAlgorithmsKeyTypes.RSA))
{
throw new NotSupportedException();
}
if (!descriptor.Metadata.TryGetValue(JsonWebKeyParameterNames.E, out var exponent))
{
throw new InvalidOperationException($"Missing '{JsonWebKeyParameterNames.E}' from metadata");
}
if (!descriptor.Metadata.TryGetValue(JsonWebKeyParameterNames.N, out var modulus))
{
throw new InvalidOperationException($"Missing '{JsonWebKeyParameterNames.N}' from metadata");
}
jsonWebKey.E = exponent;
jsonWebKey.N = modulus;
return jsonWebKey;
}
private static RSAParameters GetRSAParameters(SigningCredentials credentials)
{
RSA algorithm = null;
var x509SecurityKey = credentials.Key as X509SecurityKey;
if (x509SecurityKey != null)
{
algorithm = x509SecurityKey.PublicKey as RSA;
}
var rsaSecurityKey = credentials.Key as RsaSecurityKey;
if (rsaSecurityKey != null)
{
algorithm = rsaSecurityKey.Rsa;
if (algorithm == null)
{
var rsa = RSA.Create();
rsa.ImportParameters(rsaSecurityKey.Parameters);
algorithm = rsa;
}
}
var parameters = algorithm.ExportParameters(includePrivateParameters: false);
return parameters;
}
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<Description>ASP.NET Core common types implementing the main abstractions for Identity Service.</Description>
<TargetFramework>netcoreapp2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Identity.Service.Abstractions\Microsoft.AspNetCore.Identity.Service.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="System.ValueTuple" Version="$(CoreFxVersion)" />
</ItemGroup>
</Project>

View File

@ -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 Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Identity.Service
{
public class IdentityServiceAuthorizationOptionsSetup : IConfigureOptions<AuthorizationOptions>
{
private readonly IOptions<IdentityServiceOptions> _identityServiceOptions;
public IdentityServiceAuthorizationOptionsSetup(IOptions<IdentityServiceOptions> identityServiceOptions)
{
_identityServiceOptions = identityServiceOptions;
}
public void Configure(AuthorizationOptions options)
{
options.AddPolicy(IdentityServiceOptions.LoginPolicyName, _identityServiceOptions.Value.LoginPolicy);
}
}
}

View File

@ -0,0 +1,35 @@
// 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.Authorization;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Identity.Service
{
public class IdentityServiceOptions
{
public const string LoginPolicyName = "Microsoft.AspNetCore.Identity.Service.Login";
public const string SessionPolicyName = "Microsoft.AspNetCore.Identity.Service.Session";
public const string CookieAuthenticationScheme = "Microsoft.AspNetCore.Identity.Service.Session.Cookies";
public const string AuthenticationCookieName = "Microsoft.AspNetCore.Identity.Service";
public string Issuer { get; set; }
public AuthorizationPolicy LoginPolicy { get; set; }
public AuthorizationPolicy SessionPolicy { get; set; }
public IList<SigningCredentials> SigningKeys { get; set; } = new List<SigningCredentials>();
public TokenOptions AuthorizationCodeOptions { get; set; } = new TokenOptions();
public TokenOptions AccessTokenOptions { get; set; } = new TokenOptions();
public TokenOptions RefreshTokenOptions { get; set; } = new TokenOptions();
public TokenOptions IdTokenOptions { get; set; } = new TokenOptions();
public JsonSerializerSettings SerializationSettings { get; set; }
}
}

View File

@ -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.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity.Service.Serialization;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Identity.Service
{
public class IdentityServiceOptionsDefaultSetup : IConfigureOptions<IdentityServiceOptions>
{
public void Configure(IdentityServiceOptions options)
{
options.LoginPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.SessionPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(IdentityServiceOptions.CookieAuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
options.SerializationSettings = CreateDefault();
options.SerializationSettings.Converters.Insert(0, new AuthorizationCodeConverter());
options.SerializationSettings.Converters.Insert(0, new RefreshTokenConverter());
options.AuthorizationCodeOptions = CreateAuthorizationCodeOptions(TimeSpan.FromMinutes(5), TimeSpan.Zero);
options.AccessTokenOptions = CreateAccessTokenOptions(TimeSpan.FromHours(2), TimeSpan.Zero);
options.RefreshTokenOptions = CreateRefreshTokenOptions(TimeSpan.FromDays(30), TimeSpan.Zero);
options.IdTokenOptions = CreateIdTokenOptions(TimeSpan.FromHours(2), TimeSpan.Zero);
}
private static TokenOptions CreateAuthorizationCodeOptions(TimeSpan notValidAfter, TimeSpan notValidBefore)
{
var userClaims = new TokenMapping("user");
userClaims.AddSingle(IdentityServiceClaimTypes.UserId, ClaimTypes.NameIdentifier);
var applicationClaims = new TokenMapping("application");
applicationClaims.AddSingle(IdentityServiceClaimTypes.ClientId);
return new TokenOptions()
{
UserClaims = userClaims,
ApplicationClaims = applicationClaims,
NotValidAfter = notValidAfter,
NotValidBefore = notValidBefore
};
}
private static TokenOptions CreateAccessTokenOptions(TimeSpan notValidAfter, TimeSpan notValidBefore)
{
var userClaims = new TokenMapping("user");
userClaims.AddSingle(IdentityServiceClaimTypes.Subject, ClaimTypes.NameIdentifier);
var applicationClaims = new TokenMapping("application");
return new TokenOptions()
{
UserClaims = userClaims,
ApplicationClaims = applicationClaims,
NotValidAfter = notValidAfter,
NotValidBefore = notValidBefore
};
}
private static TokenOptions CreateRefreshTokenOptions(TimeSpan notValidAfter, TimeSpan notValidBefore)
{
var userClaims = new TokenMapping("user");
userClaims.AddSingle(IdentityServiceClaimTypes.UserId, ClaimTypes.NameIdentifier);
var applicationClaims = new TokenMapping("application");
applicationClaims.AddSingle(IdentityServiceClaimTypes.ClientId, IdentityServiceClaimTypes.ClientId);
return new TokenOptions()
{
UserClaims = userClaims,
ApplicationClaims = applicationClaims,
NotValidAfter = notValidAfter,
NotValidBefore = notValidBefore
};
}
private static TokenOptions CreateIdTokenOptions(TimeSpan notValidAfter, TimeSpan notValidBefore)
{
var userClaims = new TokenMapping("user");
var applicationClaims = new TokenMapping("application");
applicationClaims.AddSingle(IdentityServiceClaimTypes.Audience, IdentityServiceClaimTypes.ClientId);
return new TokenOptions()
{
UserClaims = userClaims,
ApplicationClaims = applicationClaims,
NotValidAfter = notValidAfter,
NotValidBefore = notValidBefore
};
}
private static JsonSerializerSettings CreateDefault() =>
new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
// Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions
// from deserialization errors that might occur from deeply nested objects.
MaxDepth = 32,
// Do not change this setting
// Setting this to None prevents Json.NET from loading malicious, unsafe, or security-sensitive types
TypeNameHandling = TypeNameHandling.None,
};
}
}

View File

@ -0,0 +1,27 @@
// 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.ObjectModel;
namespace Microsoft.AspNetCore.Identity.Service
{
public class TokenMapping : Collection<TokenValueDescriptor>
{
public TokenMapping(string source)
{
Source = source;
}
public string Source { get; }
public void AddSingle(string claimType, string contextKey)
{
Add(new TokenValueDescriptor(claimType, contextKey, TokenValueCardinality.One));
}
public void AddSingle(string name)
{
Add(new TokenValueDescriptor(name, TokenValueCardinality.One));
}
}
}

View File

@ -0,0 +1,16 @@
// 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;
namespace Microsoft.AspNetCore.Identity.Service
{
public class TokenOptions
{
public TokenMapping UserClaims { get; set; } = new TokenMapping("user");
public TokenMapping ApplicationClaims { get; set; } = new TokenMapping("application");
public TokenMapping ContextClaims { get; set; } = new TokenMapping("context");
public TimeSpan NotValidBefore { get; set; }
public TimeSpan NotValidAfter { get; set; }
}
}

View File

@ -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.Identity.Service
{
public enum TokenValueCardinality
{
Zero,
One,
Many
}
}

View File

@ -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.
namespace Microsoft.AspNetCore.Identity.Service
{
public class TokenValueDescriptor
{
public TokenValueDescriptor(string name, TokenValueCardinality cardinality)
: this(name, name, cardinality)
{
}
public TokenValueDescriptor(string name, string alias, TokenValueCardinality cardinality)
{
Name = name;
Alias = alias;
Cardinality = cardinality;
}
public string Name { get; }
public string Alias { get; }
public TokenValueCardinality Cardinality { get; }
}
}

View File

@ -0,0 +1,18 @@
// 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 System.Security.Claims
{
internal static class PrincipalExtensions
{
public static string FindFirstValue(this ClaimsPrincipal principal, string claimType)
{
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
var claim = principal.FindFirst(claimType);
return claim != null ? claim.Value : null;
}
}
}

Some files were not shown because too many files have changed in this diff Show More