I'm building my first app with Blazor, and it's actually my first project authenticating with RapidIdentity as well. I have my URIs and ClientId / Secrets already setup as well.
Anyway, I'm having some trouble trying to log in. When I attempt to go to the URI to login, I receive an error stating "AuthenticationFailureException: OpenIdConnectAuthenticationHandler: message.State is null or empty." and I'm not sure how to handle it. I'm sure it probably has to do with my Program.CS file, or another file I've added called Security.CS.
I'd appreciate any help I could get in resolving this issue.
My Program.CS file is pretty straight forward:
using InvestmentTrackingSystem;using InvestmentTrackingSystem.Components;using Microsoft.AspNetCore.Hosting;var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddRazorComponents() .AddInteractiveServerComponents();builder.Services.AddSingleton<OpenIdConnectCustomEvents>();builder.AddSecurity();var app = builder.Build();// Configure the HTTP request pipeline.if (!app.Environment.IsDevelopment()){ app.UseExceptionHandler("/Error", createScopeForErrors: true); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts();}app.UseHttpsRedirection();app.UseAntiforgery();app.MapStaticAssets();app.MapRazorComponents<App>() .AddInteractiveServerRenderMode();app.Run();And my Security.CS File:
using Microsoft.AspNetCore.Authentication.Cookies;using Microsoft.AspNetCore.Authentication;using System.Security.Claims;using IdentityModel;using Microsoft.AspNetCore.Authentication.OpenIdConnect;using Microsoft.IdentityModel.Protocols.OpenIdConnect;using Microsoft.IdentityModel.Tokens;namespace InvestmentTrackingSystem{ file static class SecurityExtras { public static AuthenticationBuilder AddOpenIdConnect<TEvent>(this AuthenticationBuilder builder, IConfigurationSection configSection) where TEvent : OpenIdConnectEvents => builder.AddOpenIdConnect(opt => { configSection.Bind(opt); opt.EventsType = typeof(TEvent); }); } public static class Security { public static void AddSecurity(this IHostApplicationBuilder builder) { builder.Services.AddSingleton<BlazorAuthEvents>(); var oidcConfig = builder.Configuration.GetSection("RapidAuth"); builder.Services.AddAuthentication(opts => { opts.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; opts.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(opts => { opts.ExpireTimeSpan = TimeSpan.FromMinutes(180); //options.AccessDeniedPath = StaticSettings.OpenIDConnectSettings.AccessDeniedPath; opts.SlidingExpiration = true; }) .AddOpenIdConnect(options => { options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.ResponseType = OidcConstants.ResponseTypes.Code; options.Authority = oidcConfig["Authority"].ToString(); options.ClientId = oidcConfig["RapidAD_ClientId"].ToString(); options.ClientSecret = oidcConfig["RapidAD_Secret"].ToString(); options.CallbackPath = oidcConfig["CallbackPath"].ToString(); //options.SignedOutRedirectUri = "https://localhost:44313/signin-oidc"; //options.SkipUnrecognizedRequests = true; options.SaveTokens = true; bool isLocal = false;#if DEBUG isLocal = true;#endif options.RequireHttpsMetadata = !isLocal; options.Prompt = "login"; options.Scope.Clear(); options.Scope.Add(OpenIdConnectScope.OpenIdProfile); options.TokenValidationParameters = new TokenValidationParameters() { AuthenticationType = OpenIdConnectDefaults.AuthenticationScheme, ValidateIssuer = false, }; options.EventsType = typeof(OpenIdConnectCustomEvents); } ); } } public class BlazorAuthEvents : OpenIdConnectEvents { public override async Task TokenValidated(TokenValidatedContext context) { var userEmail = context.Principal?.FindFirstValue(ClaimTypes.Upn); if (userEmail is null) return; var identity = new ClaimsIdentity(); context.Principal?.AddIdentity(identity); } } public class OpenIdConnectCustomEvents : OpenIdConnectEvents { private readonly IEnumerable<IClaimsHandler> _claimHandlers; public OpenIdConnectCustomEvents(IEnumerable<IClaimsHandler> claimHandlers) { _claimHandlers = claimHandlers; } public async /*override*/ Task TokenValidated(TokenValidatedContext ctx) { try { var claims = _claimHandlers? .AsParallel() .SelectMany(handler => handler.Invoke(ctx) .ConfigureAwait(false) .GetAwaiter() .GetResult()) .ToList(); if (claims?.Count > 0) { var claimsIdentity = new ClaimsIdentity( ctx.Principal.Identity, claims, ctx.Principal.Identity.AuthenticationType,"UserName", ClaimTypes.Role); ctx.Principal = new ClaimsPrincipal(claimsIdentity); } await Task.FromResult(0); } catch (Exception ex) { await Task.FromException(ex); } } public /*override*/ Task RemoteFailure(RemoteFailureContext ctx) { var path = ctx.Failure is UnauthorizedAccessException ? StaticSettings.OpenIDConnectSettings.AccessDeniedPath : StaticSettings.OpenIDConnectSettings.ErrorPath; ctx.Response.Redirect(path); ctx.HandleResponse(); return Task.CompletedTask; } } internal static class StaticSettings { internal static OpenIDConnectAuthSettings OpenIDConnectSettings { get; set; } = new OpenIDConnectAuthSettings(); } public interface IClaimsHandler { Task<IEnumerable<Claim>> Invoke(TokenValidatedContext ctx); } public class OpenIDConnectAuthSettings { public static readonly string OptionName = nameof(OpenIDConnectAuthSettings); public string ClientId { get; set; } public string ClientSecret { get; set; } public string TenantId { get; set; } public string Authority { get; set; } public string CallbackPath { get; set; } public string PostLogoutRedirectUri { get; set; } public int SessionCookieLifetimeMinutes { get; set; } public string AccessDeniedPath { get; set; } public string ErrorPath { get; set; } }}