From 2e264043dace7cdb3cc87fb99909d3b73a293c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 4 Jan 2018 15:43:44 +0300 Subject: [PATCH] #185 Made identity server auth working as POC. --- Volo.Abp.sln | 7 +++ .../AbpDesk.ConsoleClient.csproj | 12 ++++ src/AbpDesk/AbpDesk.ConsoleClient/Program.cs | 55 +++++++++++++++++++ .../AbpDesk.Web.Mvc/AbpDesk.Web.Mvc.csproj | 3 + .../AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs | 41 ++++++++++---- .../Controllers/IdentityTestController.cs | 18 ++++++ .../Properties/launchSettings.json | 2 +- src/AbpDesk/AbpDesk.Web.Mvc/tempkey.rsa | 1 + ...Factory.cs => IdentityDbContextFactory.cs} | 2 +- .../Volo.Abp.IdentityServer.Domain.csproj | 3 + .../AbpIdentityServerDomainModule.cs | 20 +++++++ .../AbpIdentityServerOptions.cs | 19 +++++++ .../AbpZeroIdentityServerBuilderExtensions.cs | 46 ++++++++++++++++ .../AspNetIdentity/AbpClaimsService.cs | 13 +++++ .../AspNetIdentity/AbpProfileService.cs | 34 ++++++++++++ .../AbpResourceOwnerPasswordValidator.cs | 33 +++++++++++ .../IdentityServer/Jwt/JwtTokenMiddleware.cs | 27 +++++++++ .../Temp/IdentityServerConfig.cs | 39 +++++++++++++ ....IdentityServer.EntityFrameworkCore.csproj | 5 ++ .../AbpIdentityServerModule.cs | 3 +- .../IdentityServerDbContextFactory.cs | 17 ++++++ 21 files changed, 385 insertions(+), 15 deletions(-) create mode 100644 src/AbpDesk/AbpDesk.ConsoleClient/AbpDesk.ConsoleClient.csproj create mode 100644 src/AbpDesk/AbpDesk.ConsoleClient/Program.cs create mode 100644 src/AbpDesk/AbpDesk.Web.Mvc/Controllers/IdentityTestController.cs create mode 100644 src/AbpDesk/AbpDesk.Web.Mvc/tempkey.rsa rename src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/{IdentityDefaultDbContextFactory.cs => IdentityDbContextFactory.cs} (84%) create mode 100644 src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerOptions.cs create mode 100644 src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpZeroIdentityServerBuilderExtensions.cs create mode 100644 src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpClaimsService.cs create mode 100644 src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs create mode 100644 src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs create mode 100644 src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Jwt/JwtTokenMiddleware.cs create mode 100644 src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Temp/IdentityServerConfig.cs create mode 100644 src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextFactory.cs diff --git a/Volo.Abp.sln b/Volo.Abp.sln index 382424ead8..d850e5dd2e 100644 --- a/Volo.Abp.sln +++ b/Volo.Abp.sln @@ -218,6 +218,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.IdentityServer.Dom EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.IdentityServer.Application", "src\Volo.Abp.IdentityServer.Application\Volo.Abp.IdentityServer.Application.csproj", "{2794C2A5-C633-41E3-8B47-0659E72D8C9B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbpDesk.ConsoleClient", "src\AbpDesk\AbpDesk.ConsoleClient\AbpDesk.ConsoleClient.csproj", "{93B574B5-2827-4E0A-86FB-B07EF40BB39F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -580,6 +582,10 @@ Global {2794C2A5-C633-41E3-8B47-0659E72D8C9B}.Debug|Any CPU.Build.0 = Debug|Any CPU {2794C2A5-C633-41E3-8B47-0659E72D8C9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2794C2A5-C633-41E3-8B47-0659E72D8C9B}.Release|Any CPU.Build.0 = Release|Any CPU + {93B574B5-2827-4E0A-86FB-B07EF40BB39F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93B574B5-2827-4E0A-86FB-B07EF40BB39F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93B574B5-2827-4E0A-86FB-B07EF40BB39F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93B574B5-2827-4E0A-86FB-B07EF40BB39F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -685,6 +691,7 @@ Global {253C20C0-1F46-410A-ACFE-2F375491E6D2} = {324B920F-5BBA-46D8-BDC6-E1FA5EFE3733} {C386A083-4190-4567-B4E3-95D1C800A298} = {324B920F-5BBA-46D8-BDC6-E1FA5EFE3733} {2794C2A5-C633-41E3-8B47-0659E72D8C9B} = {324B920F-5BBA-46D8-BDC6-E1FA5EFE3733} + {93B574B5-2827-4E0A-86FB-B07EF40BB39F} = {1187F469-0063-4065-9419-A1D956C80145} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/src/AbpDesk/AbpDesk.ConsoleClient/AbpDesk.ConsoleClient.csproj b/src/AbpDesk/AbpDesk.ConsoleClient/AbpDesk.ConsoleClient.csproj new file mode 100644 index 0000000000..e56f83c0b7 --- /dev/null +++ b/src/AbpDesk/AbpDesk.ConsoleClient/AbpDesk.ConsoleClient.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.0 + + + + + + + diff --git a/src/AbpDesk/AbpDesk.ConsoleClient/Program.cs b/src/AbpDesk/AbpDesk.ConsoleClient/Program.cs new file mode 100644 index 0000000000..802fd647ef --- /dev/null +++ b/src/AbpDesk/AbpDesk.ConsoleClient/Program.cs @@ -0,0 +1,55 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using IdentityModel.Client; +using Newtonsoft.Json.Linq; + +namespace AbpDesk.ConsoleClient +{ + class Program + { + static void Main(string[] args) + { + RunDemo().Wait(); + Console.ReadLine(); + } + + private static async Task RunDemo() + { + // discover endpoints from metadata + var disco = await DiscoveryClient.GetAsync("http://localhost:59980"); + if (disco.IsError) + { + Console.WriteLine(disco.Error); + return; + } + + // request token + var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret"); + var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1"); + + if (tokenResponse.IsError) + { + Console.WriteLine(tokenResponse.Error); + return; + } + + Console.WriteLine(tokenResponse.Json); + + // call api + var client = new HttpClient(); + client.SetBearerToken(tokenResponse.AccessToken); + + var response = await client.GetAsync("http://localhost:59980/identity-test"); + if (!response.IsSuccessStatusCode) + { + Console.WriteLine(response.StatusCode); + } + else + { + var content = await response.Content.ReadAsStringAsync(); + Console.WriteLine(content); + } + } + } +} diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDesk.Web.Mvc.csproj b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDesk.Web.Mvc.csproj index e90644cba6..538386ea40 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDesk.Web.Mvc.csproj +++ b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDesk.Web.Mvc.csproj @@ -24,6 +24,8 @@ + + @@ -35,6 +37,7 @@ + diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs index c4bb696b63..53c33fde49 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs +++ b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs @@ -20,9 +20,12 @@ using Volo.Abp.Autofac; using Volo.Abp.Identity; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Identity.Web; +using Volo.Abp.IdentityServer; +using Volo.Abp.IdentityServer.EntityFrameworkCore; using Volo.Abp.Modularity; using Volo.Abp.Ui.Navigation; using Volo.Abp.VirtualFileSystem; +using Volo.Abp.IdentityServer.Jwt; namespace AbpDesk.Web.Mvc { @@ -35,7 +38,9 @@ namespace AbpDesk.Web.Mvc typeof(AbpIdentityEntityFrameworkCoreModule), typeof(AbpIdentityWebModule), typeof(AbpAccountWebModule), - typeof(AbpAutofacModule) + typeof(AbpAutofacModule), + typeof(AbpIdentityServerDomainModule), + typeof(AbpIdentityServerEntityFrameworkCoreModule) )] public class AbpDeskWebMvcModule : AbpModule //TODO: Rename to AbpDeskWebModule, change default namespace to AbpDesk.Web { @@ -68,18 +73,26 @@ namespace AbpDesk.Web.Mvc var authentication = services.AddAuthentication(); - //Adding Facebook authentication (TODO: Move to Account module as much as possible) - if (bool.Parse(configuration["Authentication:Facebook:IsEnabled"])) + authentication.AddIdentityServerAuthentication("Bearer", options => { - authentication.AddFacebook(options => - { - options.AppId = configuration["Authentication:Facebook:AppId"]; - options.AppSecret = configuration["Authentication:Facebook:AppSecret"]; + options.Authority = "http://localhost:59980"; + options.RequireHttpsMetadata = false; - options.Scope.Add("email"); - options.Scope.Add("public_profile"); - }); - } + options.ApiName = "api1"; + }); + + ////Adding Facebook authentication (TODO: Move to Account module as much as possible) + //if (bool.Parse(configuration["Authentication:Facebook:IsEnabled"])) + //{ + // authentication.AddFacebook(options => + // { + // options.AppId = configuration["Authentication:Facebook:AppId"]; + // options.AppSecret = configuration["Authentication:Facebook:AppSecret"]; + + // options.Scope.Add("email"); + // options.Scope.Add("public_profile"); + // }); + //} services.AddAssemblyOf(); @@ -123,8 +136,12 @@ namespace AbpDesk.Web.Mvc app.UseStaticFiles(); app.UseVirtualFiles(); + app.UseIdentityServer(); + app.UseAuthentication(); + app.UseJwtTokenMiddleware("Bearer"); //TODO: It would be better without that, however it requires to use Bearer as default auth schema. + var cultures = new List { new CultureInfo("en"), @@ -160,6 +177,6 @@ namespace AbpDesk.Web.Mvc .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); return builder.Build(); - } + } } } diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/Controllers/IdentityTestController.cs b/src/AbpDesk/AbpDesk.Web.Mvc/Controllers/IdentityTestController.cs new file mode 100644 index 0000000000..cdd7f192e5 --- /dev/null +++ b/src/AbpDesk/AbpDesk.Web.Mvc/Controllers/IdentityTestController.cs @@ -0,0 +1,18 @@ +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace AbpDesk.Web.Mvc.Controllers +{ + [Route("identity-test")] + [Authorize] + public class IdentityTestController : AbpController + { + [HttpGet] + public IActionResult Get() + { + return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); + } + } +} diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/Properties/launchSettings.json b/src/AbpDesk/AbpDesk.Web.Mvc/Properties/launchSettings.json index aaa78fc0c1..ffcb2f4ec6 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/Properties/launchSettings.json +++ b/src/AbpDesk/AbpDesk.Web.Mvc/Properties/launchSettings.json @@ -18,7 +18,7 @@ "AbpDesk.Web.Mvc": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "http://localhost:5000", + "launchUrl": "http://localhost:59980", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/tempkey.rsa b/src/AbpDesk/AbpDesk.Web.Mvc/tempkey.rsa new file mode 100644 index 0000000000..5ad3458a9c --- /dev/null +++ b/src/AbpDesk/AbpDesk.Web.Mvc/tempkey.rsa @@ -0,0 +1 @@ +{"KeyId":"3b003a0fc7d8278f13f59d44f4620374","Parameters":{"D":"Qravv6bNhcfweciZna68hlflA8ygXVgoycYFC/oSG0Ulxr7iN1WtpJEau80OReBKVuRqqvxepzJcNv9CqMGvs9+bDrymk9DDdH46ybFKZB2MaufaBuwbuiJi5qCsGmaDvjzf1dwPyGS+7OEa28GDw7ibwwkf6sMJcIBWGQrAZ6O+/Ka8ouF8Zdn+6Igv4rK+RVtKQbjYeJ9CvhOZFXSuLl1XVAM01bGwvQSh8BwMC7i1g7gKypvTG31OSlG3ZzIoF7uuoXaC/WWbGvryvPUzFbgCLO8tQu4/Kdy9NsU3C3M/adUtRxLtVNvD29o8dDZT8NM2UTotfgGg3v72ktmnjQ==","DP":"cQ4XqOgPQAOO4dqg3yFH/xFoXQx/5/q4oGtFZYDTSOyrRL/LopZHrr53ys7Uble7+dDa6PPYBgY/C2/SwLBaUF9FpM27Kp7D+3yUKFbKtW8uke+UEM6MgcfjBdwEFVV4CqqHzgD8JvtZ1xhBvx6yUJuHcxxuWKlElXhsTVJKwwE=","DQ":"ktpqqgnwT2Rr9Eb0Kkyg5LZgsO75Pu+0u1q4WhZ7ZzOMJqquCf7hw7ucaWPLq8Ipzn5Hu5CO2gT+URjMGkJNQA+728tFnFkST9wFeqp1hQh5ZxgYsONiH9e/Nw6iauI07i2TYt7pFhIYUOg52/SvHrAzPyEYznCw3BZrR4rEjJk=","Exponent":"AQAB","InverseQ":"hkiDiH926FhARZnVhV5sDnbjxGTdQl8ErZ+qOdBE2vVP6IwNj14dkw+ON4XeIyM/CE3RYmhd8I68JCDNVd6J4hE4NIe4xr+ykmVHYLWQhZ/k4QippwabZ3SK9dkcosQF6BP3lNSgW0UxomdgMKQcsQqccroKEq52Ccr3dUVXICg=","Modulus":"7fgl4D1Emkq+KRAj+u7g7e8hrEhYT19ZRVu1LVdZpx8vDUmuBLVCrNdm6VPIeV5YvrRLELiAknujOMytnIwtY6D1beOtdunEE1z32QsFxizYa9lVaL1rwEURnLQ51sP2bJsxNZnHaJQFGCzOc1i8V551V8eG43asoSbQKFz8Aa6A5UdZaecWSF9WoIkWH/xdi2Ecun69HAS8LiS2S3yKgqyLJivZM72lMUgTWyklh8WMscMb2pRTHspV2oqlIRBo4bRbLgYRJ+6pOMvtge5CgWMupolpy0BTpQrRLPYK9oiZ9iP1/Hdrlu8U0G6XL1rz0kkfqOU10AtlYZe01b0zXQ==","P":"+pWvk2Q5+YN0IikHJkCdpeZMUM3Tkj08FEJETRNU3Rcrpg1wyFQJdqIib28XzWdua5vilBNnAwhbBw4dO978rn1riHq8VQD1JSFh1+vwlzhG76M89KnP7v/DuILbTT4tKU28VWjQsT4RVlI5Rqc3REaUoQDYAmvARCFDGOxCrYs=","Q":"8xyreLJWnAFQoVJjLhK/eNE+GdABidksHz47k1p9imeKAv4xpFx4jfAec4VSiXCvK/KW9+fbPA+S4hEa/5oICKvvALFZavL7A4htnQjflgnEc+QqlR7TjnVgMILyBB3mf4mlNjR7kfwcDwGLRBjMx8mxadj+NlREri4Jw6q4D7c="}} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDefaultDbContextFactory.cs b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextFactory.cs similarity index 84% rename from src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDefaultDbContextFactory.cs rename to src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextFactory.cs index a844e4c1dc..94d89c961b 100644 --- a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDefaultDbContextFactory.cs +++ b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextFactory.cs @@ -5,7 +5,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore { /* This class is needed for EF Core command line tooling */ - public class IdentityDefaultDbContextFactory : IDesignTimeDbContextFactory + public class IdentityDbContextFactory : IDesignTimeDbContextFactory { public IdentityDbContext CreateDbContext(string[] args) { diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj b/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj index bd54443a48..25bf68b68f 100644 --- a/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj +++ b/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj @@ -16,11 +16,14 @@ + + + diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs index 87cd39e4c8..92d31a478f 100644 --- a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs @@ -1,13 +1,18 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; +using Volo.Abp.Identity; using Volo.Abp.IdentityServer.Clients; +using Volo.Abp.IdentityServer.Temp; using Volo.Abp.Modularity; +using Volo.Abp.Security; namespace Volo.Abp.IdentityServer { [DependsOn(typeof(AbpIdentityServerDomainSharedModule))] [DependsOn(typeof(AbpDddModule))] [DependsOn(typeof(AbpAutoMapperModule))] + [DependsOn(typeof(AbpIdentityDomainModule))] + [DependsOn(typeof(AbpSecurityModule))] public class AbpIdentityServerDomainModule : AbpModule { public override void ConfigureServices(IServiceCollection services) @@ -18,6 +23,21 @@ namespace Volo.Abp.IdentityServer }); services.AddAssemblyOf(); + + AddIdentityServer(services); + } + + private static void AddIdentityServer(IServiceCollection services) + { + var identityServerBuilder = services.AddIdentityServer(); + + //TODO: Remove in-memory stores once EF Core stores are fully completed. + identityServerBuilder.AddDeveloperSigningCredential() + //.AddInMemoryClients(IdentityServerConfig.GetClients()) + //.AddInMemoryApiResources(IdentityServerConfig.GetApiResources()) + .AddAbpIdentityServer(); + + services.ExecutePreConfiguredActions(identityServerBuilder); } } } diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerOptions.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerOptions.cs new file mode 100644 index 0000000000..1eb6b14d35 --- /dev/null +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerOptions.cs @@ -0,0 +1,19 @@ +using System.IdentityModel.Tokens.Jwt; + +namespace Volo.Abp.IdentityServer +{ + public class AbpIdentityServerOptions + { + /// + /// Updates to be compatible with identity server claims. + /// Default: true. + /// + public bool UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap { get; set; } = true; + + /// + /// Updates to be compatible with identity server claims. + /// Default: true. + /// + public bool UpdateAbpClaimTypes { get; set; } = true; + } +} \ No newline at end of file diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpZeroIdentityServerBuilderExtensions.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpZeroIdentityServerBuilderExtensions.cs new file mode 100644 index 0000000000..f8b52260bd --- /dev/null +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpZeroIdentityServerBuilderExtensions.cs @@ -0,0 +1,46 @@ +using System; +using System.IdentityModel.Tokens.Jwt; +using IdentityModel; +using IdentityServer4.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.Identity; +using Volo.Abp.IdentityServer.AspNetIdentity; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.IdentityServer +{ + public static class AbpZeroIdentityServerBuilderExtensions + { + public static IIdentityServerBuilder AddAbpIdentityServer( + this IIdentityServerBuilder builder, + Action optionsAction = null) + { + var options = new AbpIdentityServerOptions(); + optionsAction?.Invoke(options); + + //TODO: AspNet Identity integration lines. Can be extracted to a extension method + builder.AddAspNetIdentity(); + builder.AddProfileService(); + builder.AddResourceOwnerValidator(); + + builder.Services.Replace(ServiceDescriptor.Transient()); + + if (options.UpdateAbpClaimTypes) + { + AbpClaimTypes.UserId = JwtClaimTypes.Subject; + AbpClaimTypes.UserName = JwtClaimTypes.Name; + AbpClaimTypes.Role = JwtClaimTypes.Role; + } + + if (options.UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap) + { + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap[AbpClaimTypes.UserId] = AbpClaimTypes.UserId; + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap[AbpClaimTypes.UserName] = AbpClaimTypes.UserName; + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap[AbpClaimTypes.Role] = AbpClaimTypes.Role; + } + + return builder; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpClaimsService.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpClaimsService.cs new file mode 100644 index 0000000000..c516f45e6a --- /dev/null +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpClaimsService.cs @@ -0,0 +1,13 @@ +using IdentityServer4.Services; +using Microsoft.Extensions.Logging; + +namespace Volo.Abp.IdentityServer.AspNetIdentity +{ + public class AbpClaimsService : DefaultClaimsService + { + public AbpClaimsService(IProfileService profile, ILogger logger) + : base(profile, logger) + { + } + } +} diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs new file mode 100644 index 0000000000..c2cc261bc5 --- /dev/null +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpProfileService.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using IdentityServer4.AspNetIdentity; +using IdentityServer4.Models; +using Microsoft.AspNetCore.Identity; +using Volo.Abp.Identity; +using Volo.Abp.Uow; + +namespace Volo.Abp.IdentityServer.AspNetIdentity +{ + //TODO: Implement multi-tenancy as like in old ABP + + public class AbpProfileService : ProfileService + { + public AbpProfileService( + IdentityUserManager userManager, + IUserClaimsPrincipalFactory claimsFactory + ) : base(userManager, claimsFactory) + { + + } + + [UnitOfWork] + public override async Task GetProfileDataAsync(ProfileDataRequestContext context) + { + await base.GetProfileDataAsync(context); + } + + [UnitOfWork] + public override async Task IsActiveAsync(IsActiveContext context) + { + await base.IsActiveAsync(context); + } + } +} diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs new file mode 100644 index 0000000000..778b3445bf --- /dev/null +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using IdentityServer4.AspNetIdentity; +using IdentityServer4.Services; +using IdentityServer4.Validation; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Volo.Abp.Identity; +using Volo.Abp.Uow; + +namespace Volo.Abp.IdentityServer.AspNetIdentity +{ + public class AbpResourceOwnerPasswordValidator : ResourceOwnerPasswordValidator + { + public AbpResourceOwnerPasswordValidator( + IdentityUserManager userManager, + SignInManager signInManager, + IEventService events, + ILogger> logger + ) : base( + userManager, + signInManager, + events, + logger) + { + } + + [UnitOfWork] + public override async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) + { + await base.ValidateAsync(context); + } + } +} diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Jwt/JwtTokenMiddleware.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Jwt/JwtTokenMiddleware.cs new file mode 100644 index 0000000000..8dd90d2d22 --- /dev/null +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Jwt/JwtTokenMiddleware.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; + +namespace Volo.Abp.IdentityServer.Jwt +{ + //TODO: Can we move this to another package..? + + public static class JwtTokenMiddleware + { + public static IApplicationBuilder UseJwtTokenMiddleware(this IApplicationBuilder app, string schema) + { + return app.Use(async (ctx, next) => + { + if (ctx.User.Identity?.IsAuthenticated != true) + { + var result = await ctx.AuthenticateAsync(schema); + if (result.Succeeded && result.Principal != null) + { + ctx.User = result.Principal; + } + } + + await next(); + }); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Temp/IdentityServerConfig.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Temp/IdentityServerConfig.cs new file mode 100644 index 0000000000..3421d6c950 --- /dev/null +++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Temp/IdentityServerConfig.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using IdentityServer4.Models; + +namespace Volo.Abp.IdentityServer.Temp +{ + internal static class IdentityServerConfig + { + public static IEnumerable GetApiResources() + { + return new List + { + new ApiResource("api1", "My API") + }; + } + + public static IEnumerable GetClients() + { + return new List + { + new Client + { + ClientId = "client", + + // no interactive user, use the clientid/secret for authentication + AllowedGrantTypes = GrantTypes.ClientCredentials, + + // secret for authentication + ClientSecrets = + { + new IdentityServer4.Models.Secret("secret".Sha256()) + }, + + // scopes that client has access to + AllowedScopes = { "api1" } + } + }; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo.Abp.IdentityServer.EntityFrameworkCore.csproj b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo.Abp.IdentityServer.EntityFrameworkCore.csproj index 19c847d9a1..6e94ade830 100644 --- a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo.Abp.IdentityServer.EntityFrameworkCore.csproj +++ b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo.Abp.IdentityServer.EntityFrameworkCore.csproj @@ -18,4 +18,9 @@ + + + + + diff --git a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerModule.cs b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerModule.cs index 2b38a9f56a..b00b76a162 100644 --- a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerModule.cs +++ b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/AbpIdentityServerModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.IdentityServer.Clients; using Volo.Abp.Modularity; namespace Volo.Abp.IdentityServer.EntityFrameworkCore @@ -13,7 +14,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore services.AddAbpDbContext(options => { options.WithDefaultRepositories(); - //options.WithCustomRepository(); + options.WithCustomRepository(); }); services.AddAssemblyOf(); diff --git a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextFactory.cs b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextFactory.cs new file mode 100644 index 0000000000..28c9a38cec --- /dev/null +++ b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextFactory.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace Volo.Abp.IdentityServer.EntityFrameworkCore +{ + /* This class is needed for EF Core command line tooling */ + + public class IdentityServerDbContextFactory : IDesignTimeDbContextFactory + { + public IdentityServerDbContext CreateDbContext(string[] args) + { + var builder = new DbContextOptionsBuilder(); + builder.UseSqlServer("Server=localhost;Database=AbpDesk;Trusted_Connection=True;"); + return new IdentityServerDbContext(builder.Options); + } + } +}