From 6958989268895dfc0c5ae4cc8930fb19541d1138 Mon Sep 17 00:00:00 2001 From: Diana Barros Date: Mon, 2 Dec 2024 19:05:53 +0000 Subject: [PATCH] Replace IdentityServer4 with Duende.IdentityServer (#3008) --- .../OAuth2Integration/AuthServer/Config.cs | 21 +++++------ .../Controllers/AccountController.cs | 11 +++--- .../Controllers/ConsentController.cs | 36 ++++++++++++------- .../OAuth2Integration.csproj | 29 ++++++++------- .../Controllers/ProductsController.cs | 8 ++--- test/WebSites/OAuth2Integration/Startup.cs | 20 +++++++---- 6 files changed, 70 insertions(+), 55 deletions(-) diff --git a/test/WebSites/OAuth2Integration/AuthServer/Config.cs b/test/WebSites/OAuth2Integration/AuthServer/Config.cs index c51f6312d0..6a7c64a610 100644 --- a/test/WebSites/OAuth2Integration/AuthServer/Config.cs +++ b/test/WebSites/OAuth2Integration/AuthServer/Config.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; -using IdentityServer4.Models; -using IdentityServer4.Test; +using Duende.IdentityServer.Models; +using Duende.IdentityServer.Test; namespace OAuth2Integration.AuthServer { - public static class Config + internal static class Config { - internal static IEnumerable Clients() + public static IEnumerable Clients() { yield return new Client { @@ -31,15 +31,12 @@ internal static IEnumerable Clients() internal static IEnumerable ApiResources() { - yield return new ApiResource + return new List { - Name = "api", - DisplayName = "API", - Scopes = - [ - new Scope("readAccess", "Access read operations"), - new Scope("writeAccess", "Access write operations") - ] + new ApiResource("api", "API") + { + Scopes = { "readAccess", "writeAccess" } + } }; } diff --git a/test/WebSites/OAuth2Integration/AuthServer/Controllers/AccountController.cs b/test/WebSites/OAuth2Integration/AuthServer/Controllers/AccountController.cs index f276bbb410..ba4d65bf30 100644 --- a/test/WebSites/OAuth2Integration/AuthServer/Controllers/AccountController.cs +++ b/test/WebSites/OAuth2Integration/AuthServer/Controllers/AccountController.cs @@ -1,9 +1,9 @@ using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; +using Duende.IdentityServer; +using Duende.IdentityServer.Test; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; -using IdentityServer4; -using IdentityServer4.Test; +using Microsoft.AspNetCore.Mvc; namespace OAuth2Integration.AuthServer.Controllers { @@ -22,12 +22,11 @@ public AccountController() public IActionResult Login(string returnUrl) { var viewModel = new LoginViewModel { Username = "joebloggs", Password = "pass123", ReturnUrl = returnUrl }; - return View("/AuthServer/Views/Login.cshtml", viewModel); } [HttpPost("login")] - public async Task Login([FromForm]LoginViewModel viewModel) + public async Task Login([FromForm] LoginViewModel viewModel) { if (!_userStore.ValidateCredentials(viewModel.Username, viewModel.Password)) { @@ -36,9 +35,9 @@ public async Task Login([FromForm]LoginViewModel viewModel) return View("/AuthServer/Views/Login.cshtml", viewModel); } - // Use an IdentityServer-compatible ClaimsPrincipal var identityServerUser = new IdentityServerUser(viewModel.Username); identityServerUser.DisplayName = viewModel.Username; + await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, identityServerUser.CreatePrincipal()); return Redirect(viewModel.ReturnUrl); diff --git a/test/WebSites/OAuth2Integration/AuthServer/Controllers/ConsentController.cs b/test/WebSites/OAuth2Integration/AuthServer/Controllers/ConsentController.cs index 32e5652e3f..704d54839b 100644 --- a/test/WebSites/OAuth2Integration/AuthServer/Controllers/ConsentController.cs +++ b/test/WebSites/OAuth2Integration/AuthServer/Controllers/ConsentController.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Stores; +using Duende.IdentityServer.Models; +using Duende.IdentityServer.Services; +using Duende.IdentityServer.Stores; using Microsoft.AspNetCore.Mvc; namespace OAuth2Integration.AuthServer.Controllers @@ -29,29 +29,39 @@ public ConsentController( public async Task Consent(string returnUrl) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); - var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); - var resource = await _resourceStore.FindApiResourceAsync("api"); var viewModel = new ConsentViewModel { ReturnUrl = returnUrl, - ClientName = client.ClientName, - ScopesRequested = resource.Scopes.Where(s => request.ScopesRequested.Contains(s.Name)) + ClientName = request.Client.ClientName, + ScopesRequested = request.ValidatedResources?.Resources?.ApiScopes ?? new List() }; return View("/AuthServer/Views/Consent.cshtml", viewModel); } [HttpPost("consent")] - public async Task Consent([FromForm]ConsentViewModel viewModel) + public async Task Consent([FromForm] ConsentViewModel viewModel) { var request = await _interaction.GetAuthorizationContextAsync(viewModel.ReturnUrl); - // Communicate outcome of consent back to identityserver - var consentResponse = new ConsentResponse + ConsentResponse consentResponse; + if (viewModel.ScopesConsented != null && viewModel.ScopesConsented.Any()) { - ScopesConsented = viewModel.ScopesConsented - }; + consentResponse = new ConsentResponse + { + RememberConsent = true, + ScopesValuesConsented = viewModel.ScopesConsented.ToList() + }; + } + else + { + consentResponse = new ConsentResponse + { + Error = AuthorizationError.AccessDenied + }; + } + await _interaction.GrantConsentAsync(request, consentResponse); return Redirect(viewModel.ReturnUrl); @@ -62,7 +72,7 @@ public class ConsentViewModel { public string ReturnUrl { get; set; } public string ClientName { get; set; } - public IEnumerable ScopesRequested { get; set; } + public IEnumerable ScopesRequested { get; set; } public string[] ScopesConsented { get; set; } } } diff --git a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj index ac9116ab50..7bad4f8198 100644 --- a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj +++ b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj @@ -1,26 +1,29 @@ - + net9.0;net8.0;net6.0 - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/test/WebSites/OAuth2Integration/ResourceServer/Controllers/ProductsController.cs b/test/WebSites/OAuth2Integration/ResourceServer/Controllers/ProductsController.cs index 15df0e1978..0c9f93ab1c 100644 --- a/test/WebSites/OAuth2Integration/ResourceServer/Controllers/ProductsController.cs +++ b/test/WebSites/OAuth2Integration/ResourceServer/Controllers/ProductsController.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace OAuth2Integration.ResourceServer.Controllers { @@ -33,7 +33,7 @@ public Product GetProduct(int id) [HttpPost] [Authorize("writeAccess")] - public void CreateProduct([FromBody]Product product) + public void CreateProduct([FromBody] Product product) { } @@ -53,6 +53,6 @@ public class Product public enum ProductStatus { - InStock, ComingSoon + InStock, ComingSoon } -} \ No newline at end of file +} diff --git a/test/WebSites/OAuth2Integration/Startup.cs b/test/WebSites/OAuth2Integration/Startup.cs index 03d3cffd79..9c95576940 100644 --- a/test/WebSites/OAuth2Integration/Startup.cs +++ b/test/WebSites/OAuth2Integration/Startup.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -24,7 +23,7 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - // Register IdentityServer services to power OAuth2.0 flows + // Register Duende IdentityServer services to power OAuth2.0 flows services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryClients(AuthServer.Config.Clients()) @@ -36,11 +35,19 @@ public void ConfigureServices(IServiceCollection services) // See https://learn.microsoft.com/aspnet/core/security/authorization/limitingidentitybyscheme services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie() - .AddIdentityServerAuthentication(c => + .AddJwtBearer("Bearer", options => { - c.Authority = "https://localhost:5001/auth-server/"; - c.RequireHttpsMetadata = false; - c.ApiName = "api"; + options.Authority = "https://localhost:5001/auth-server/"; + options.RequireHttpsMetadata = false; + options.Audience = "api"; + options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidAudience = "api", + ValidIssuer = "https://localhost:5001/auth-server/", + }; }); // Configure named auth policies that map directly to OAuth2.0 scopes @@ -56,7 +63,6 @@ public void ConfigureServices(IServiceCollection services) { c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "Test API V1" }); - // Define the OAuth2.0 scheme that's in use (i.e. Implicit Flow) c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2,