From b9a39cde330994ea937bd015897f388e0aeae806 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:27:47 +0000 Subject: [PATCH 1/5] Upgrade AdHocReporting.BlazorWasm (Server/Client/Shared) to .NET 8 - Bump TargetFramework to net8.0 on all three projects - Replace Microsoft.AspNetCore.ApiAuthorization.IdentityServer (removed in .NET 8) with ASP.NET Core Identity cookie auth (AddDefaultIdentity + Identity UI Razor Pages) - Drop ApiAuthorizationDbContext for IdentityDbContext - Remove OidcConfigurationController (depended on removed package) - Remove RemoteAuthenticatorView/SignOutSessionStateManager usage on the client (obsolete in .NET 8); redirect /authentication/{action} to the Identity Razor Pages - Bump Microsoft.EntityFrameworkCore.* to 8.0.11 - Bump Microsoft.AspNetCore.Components.WebAssembly.* to 8.0.11 - Bump Microsoft.AspNetCore.Identity.* to 8.0.11 - Bump Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore to 8.0.11 - Add Microsoft.AspNetCore.Authentication.JwtBearer 8.0.11 - Bump Microsoft.Data.SqlClient to 5.2.2 - Bump Microsoft.IdentityModel.JsonWebTokens / System.IdentityModel.Tokens.Jwt to 7.6.0 - Remove Microsoft.VisualStudio.Web.CodeGeneration.Design, System.Data.SqlClient, System.Net.Http, System.Text.RegularExpressions, System.Drawing.Common (no longer needed; provided by .NET 8 BCL) Co-Authored-By: Toby Drinkall --- ...mo.BlazorWasm.AdhocReporting.Client.csproj | 14 ++++----- .../Client/Pages/Authentication.razor | 22 +++++++++++-- .../Client/Program.cs | 7 ++--- .../Client/Shared/LoginDisplay.razor | 20 ++++-------- .../Client/Shared/RedirectToLogin.razor | 2 +- .../OidcConfigurationController.cs | 25 --------------- .../Server/Data/AppDbContext.cs | 11 ++----- ...mo.BlazorWasm.AdhocReporting.Server.csproj | 31 ++++++++----------- .../Server/Program.cs | 16 ---------- .../Server/appsettings.json | 7 ----- ...mo.BlazorWasm.AdhocReporting.Shared.csproj | 2 +- 11 files changed, 53 insertions(+), 104 deletions(-) delete mode 100644 AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Controllers/OidcConfigurationController.cs diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/EqDemo.BlazorWasm.AdhocReporting.Client.csproj b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/EqDemo.BlazorWasm.AdhocReporting.Client.csproj index 757d9614..8ef8b788 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/EqDemo.BlazorWasm.AdhocReporting.Client.csproj +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/EqDemo.BlazorWasm.AdhocReporting.Client.csproj @@ -1,18 +1,18 @@ - + - net6.0 + net8.0 EqDemo.Client enable enable - - - - - + + + + + diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Authentication.razor b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Authentication.razor index 6c743567..eb3d9ffd 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Authentication.razor +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Authentication.razor @@ -1,7 +1,23 @@ @page "/authentication/{action}" -@using Microsoft.AspNetCore.Components.WebAssembly.Authentication - +@inject NavigationManager Navigation -@code{ +@code { [Parameter] public string? Action { get; set; } + + protected override void OnInitialized() + { + // The legacy OIDC client-side authentication flow has been replaced with + // server-side cookie auth via ASP.NET Core Identity Razor Pages. Redirect + // legacy /authentication/{action} URLs to the new endpoints. + var target = Action switch + { + "login" => "Identity/Account/Login", + "register" => "Identity/Account/Register", + "logout" => "Identity/Account/Logout", + "profile" => "Identity/Account/Manage", + _ => "/" + }; + + Navigation.NavigateTo(target, forceLoad: true); + } } diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Program.cs b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Program.cs index ba141b48..14f7ec16 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Program.cs +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Program.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using EqDemo.Client; @@ -8,12 +7,12 @@ builder.RootComponents.Add("#app"); builder.RootComponents.Add("head::after"); -builder.Services.AddHttpClient("EqDemo.BlazorWasm.AdhocReporting.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) - .AddHttpMessageHandler(); +builder.Services.AddHttpClient("EqDemo.BlazorWasm.AdhocReporting.ServerAPI", + client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); // Supply HttpClient instances that include access tokens when making requests to the server project builder.Services.AddScoped(sp => sp.GetRequiredService().CreateClient("EqDemo.BlazorWasm.AdhocReporting.ServerAPI")); -builder.Services.AddApiAuthorization(); +builder.Services.AddAuthorizationCore(); await builder.Build().RunAsync(); diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/LoginDisplay.razor b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/LoginDisplay.razor index 5e52e86f..1143db73 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/LoginDisplay.razor +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/LoginDisplay.razor @@ -1,24 +1,16 @@ @using Microsoft.AspNetCore.Components.Authorization -@using Microsoft.AspNetCore.Components.WebAssembly.Authentication @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager - Hello, @context.User.Identity?.Name! - + Hello, @context.User.Identity?.Name! +
+ +
- Register - Log in + Register + Log in
- -@code{ - private async Task BeginSignOut(MouseEventArgs args) - { - await SignOutManager.SetSignOutState(); - Navigation.NavigateTo("authentication/logout"); - } -} diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/RedirectToLogin.razor b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/RedirectToLogin.razor index 2db61cf6..f2aba03e 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/RedirectToLogin.razor +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Shared/RedirectToLogin.razor @@ -3,6 +3,6 @@ @code { protected override void OnInitialized() { - Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}"); + Navigation.NavigateTo($"Identity/Account/Login?ReturnUrl={Uri.EscapeDataString(Navigation.Uri)}", forceLoad: true); } } diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Controllers/OidcConfigurationController.cs b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Controllers/OidcConfigurationController.cs deleted file mode 100644 index 07da33dc..00000000 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Controllers/OidcConfigurationController.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.AspNetCore.ApiAuthorization.IdentityServer; -using Microsoft.AspNetCore.Mvc; - -namespace EqDemo.Controllers -{ - public class OidcConfigurationController : Controller - { - private readonly ILogger _logger; - - public OidcConfigurationController(IClientRequestParametersProvider clientRequestParametersProvider, ILogger logger) - { - ClientRequestParametersProvider = clientRequestParametersProvider; - _logger = logger; - } - - public IClientRequestParametersProvider ClientRequestParametersProvider { get; } - - [HttpGet("_configuration/{clientId}")] - public IActionResult GetClientRequestParameters([FromRoute] string clientId) - { - var parameters = ClientRequestParametersProvider.GetClientParameters(HttpContext, clientId); - return Ok(parameters); - } - } -} \ No newline at end of file diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Data/AppDbContext.cs b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Data/AppDbContext.cs index 6f103610..39df4c23 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Data/AppDbContext.cs +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Data/AppDbContext.cs @@ -1,18 +1,13 @@ -using Microsoft.AspNetCore.ApiAuthorization.IdentityServer; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; - -using Duende.IdentityServer.EntityFramework.Options; using EqDemo.Models; namespace EqDemo.Data { - public class AppDbContext : ApiAuthorizationDbContext + public class AppDbContext : IdentityDbContext { - public AppDbContext( - DbContextOptions options, - IOptions operationalStoreOptions) : base(options, operationalStoreOptions) + public AppDbContext(DbContextOptions options) : base(options) { } diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/EqDemo.BlazorWasm.AdhocReporting.Server.csproj b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/EqDemo.BlazorWasm.AdhocReporting.Server.csproj index 79054182..0e904fcb 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/EqDemo.BlazorWasm.AdhocReporting.Server.csproj +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/EqDemo.BlazorWasm.AdhocReporting.Server.csproj @@ -1,36 +1,31 @@ - net6.0 + net8.0 disable enable EqDemo.BlazorWasm.AdhocReporting.Server EqDemo.Server - - - + + + + - - - - - + - - - - - - - - + + + + + + @@ -48,4 +43,4 @@ - \ No newline at end of file +
diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Program.cs b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Program.cs index 4a2636a4..340a1f2d 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Program.cs +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/Program.cs @@ -1,5 +1,3 @@ -using System.IdentityModel.Tokens.Jwt; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; @@ -29,19 +27,6 @@ .AddRoles() .AddEntityFrameworkStores(); -builder.Services.AddIdentityServer() - .AddApiAuthorization(options => { - //the following 2 lines are necessary to support roles on the WebAssembly side - options.IdentityResources["openid"].UserClaims.Add("role"); - options.ApiResources.Single().UserClaims.Add("role"); - }); - -// We need to do this as it maps "role" to ClaimTypes.Role and causes issues -JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role"); - -builder.Services.AddAuthentication() - .AddIdentityServerJwt(); - //EasyQuery services builder.Services.AddEasyQuery() .UseSqlManager() @@ -79,7 +64,6 @@ app.UseRouting(); -app.UseIdentityServer(); app.UseAuthentication(); app.UseAuthorization(); diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/appsettings.json b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/appsettings.json index 4826929d..a1986a8b 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/appsettings.json +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Server/appsettings.json @@ -5,13 +5,6 @@ "Microsoft.AspNetCore": "Warning" } }, - "IdentityServer": { - "Clients": { - "EqDemo.BlazorWasm.AdhocReporting.Client": { - "Profile": "IdentityServerSPA" - } - } - }, "AllowedHosts": "*", "ConnectionStrings": { "EqDemoSqLite": "Data Source=eqdemo-sqlite.db", diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Shared/EqDemo.BlazorWasm.AdhocReporting.Shared.csproj b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Shared/EqDemo.BlazorWasm.AdhocReporting.Shared.csproj index a5b78ac4..9a938be4 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Shared/EqDemo.BlazorWasm.AdhocReporting.Shared.csproj +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Shared/EqDemo.BlazorWasm.AdhocReporting.Shared.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable From f47ef14b3281dfcc87a1ab1dd7c13659f62ab5f9 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:34:33 +0000 Subject: [PATCH 2/5] Address Devin Review: register custom AuthenticationStateProvider and decouple Reports.razor from IAccessTokenProviderAccessor - Add EqDemo.Client.HostAuthenticationStateProvider that resolves auth state by calling a small /_auth/me endpoint on the server (which is authenticated via the ASP.NET Core Identity cookie). This restores the AuthenticationStateProvider DI registration that was previously provided by AddApiAuthorization() so resolves at runtime. - Register HostAuthenticationStateProvider in Client/Program.cs alongside AddAuthorizationCore(). - Add /_auth/me minimal API endpoint in Server/Program.cs that returns the current cookie-authenticated user's claims. - Update Client/Pages/Reports.razor to drop the OIDC-only IAccessTokenProviderAccessor dependency. With cookie auth the auth cookie is sent automatically with same-origin requests, so no bearer token is needed for the EasyQuery JS bootstrap. Co-Authored-By: Toby Drinkall --- .../Client/HostAuthenticationStateProvider.cs | 61 +++++++++++++++++++ .../Client/Pages/Reports.razor | 14 ++--- .../Client/Program.cs | 5 +- .../Server/Program.cs | 13 ++++ 4 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/HostAuthenticationStateProvider.cs diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/HostAuthenticationStateProvider.cs b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/HostAuthenticationStateProvider.cs new file mode 100644 index 00000000..a79a412f --- /dev/null +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/HostAuthenticationStateProvider.cs @@ -0,0 +1,61 @@ +using System.Net; +using System.Net.Http.Json; +using System.Security.Claims; + +using Microsoft.AspNetCore.Components.Authorization; + +namespace EqDemo.Client; + +/// +/// Resolves the current user's authentication state by calling a small endpoint +/// on the server (which authenticates the request via the ASP.NET Core Identity +/// auth cookie). Replaces the OIDC-based remote authentication state provider +/// that came with Microsoft.AspNetCore.ApiAuthorization.IdentityServer. +/// +public sealed class HostAuthenticationStateProvider : AuthenticationStateProvider +{ + private const string AuthenticationType = "Cookies"; + + private static readonly AuthenticationState Anonymous = + new(new ClaimsPrincipal(new ClaimsIdentity())); + + private readonly HttpClient _http; + + public HostAuthenticationStateProvider(HttpClient http) + { + _http = http; + } + + public override async Task GetAuthenticationStateAsync() + { + try + { + var response = await _http.GetAsync("_auth/me"); + if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) + { + return Anonymous; + } + + response.EnsureSuccessStatusCode(); + + var info = await response.Content.ReadFromJsonAsync(); + if (info is null || !info.IsAuthenticated) + { + return Anonymous; + } + + var claims = (info.Claims ?? Array.Empty()) + .Select(c => new Claim(c.Type, c.Value)); + var identity = new ClaimsIdentity(claims, AuthenticationType, ClaimTypes.Name, ClaimTypes.Role); + return new AuthenticationState(new ClaimsPrincipal(identity)); + } + catch (HttpRequestException) + { + return Anonymous; + } + } + + private sealed record AuthInfo(bool IsAuthenticated, AuthClaim[]? Claims); + + private sealed record AuthClaim(string Type, string Value); +} diff --git a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Reports.razor b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Reports.razor index ac5ce0a1..b594c3d6 100644 --- a/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Reports.razor +++ b/AspNetCore/Blazor/AdHocReporting.BlazorWasm/Client/Pages/Reports.razor @@ -1,14 +1,11 @@ @page "/reports" @using Microsoft.AspNetCore.Authorization -@using Microsoft.AspNetCore.Components.WebAssembly.Authentication -@using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal @attribute [Authorize] @implements IAsyncDisposable @inject IJSRuntime JSRuntime @inject NavigationManager NavigationManager @inject AuthenticationStateProvider AuthenticationStateProvider -@inject IAccessTokenProviderAccessor AccessTokenProviderAccessor