I have a .NET6 Blazor wasm application that uses IdentityServer4 for authentication. I want to create a login event in database whenever a user logs in. To do that, I am using the IdentityServer
ClientAuthenticationSuccess
event as follows. The issue is that
user
is always null that I am getting from
HttpContextAccessor
:
public class CustomEventSink : IEventSink
{
private readonly ApplicationDbContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly UserManager<ApplicationUser> _userManager;
public CustomEventSink(ApplicationDbContext context, UserManager<ApplicationUser> user, IHttpContextAccessor contextAccessor)
{
_context = context;
_httpContextAccessor = contextAccessor;
_userManager = user;
}
public async Task PersistAsync(Event evt)
{
//log event here....
EventLog el = new();
if (@evt.Id.Equals(EventIds.ClientAuthenticationSuccess))
{
try
{
var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);
if (user != null)
{
// do stuff
el.EventName = "Login";
el.EventDescription = $"{evt.Name} ({evt.Id}), Details:{evt}";
el.Created = DateTime.Now;
el.UserName = user.UserName;
_context.EventLog.Add(el);
_context.SaveChanges();
}
}
catch (Exception ex)
{
// handle exception
}
}
if (@evt.Id.Equals(EventIds.ClientAuthenticationFailure))
{
try
{
// do stuff
el.EventName = "Login Failure";
el.EventDescription = $"{evt.Name} ({evt.Id}), Details:{evt}";
el.Created = DateTime.Now;
_context.EventLog.Add(el);
_context.SaveChanges();
}
catch (Exception ex)
{
// handle exception
}
}
}
}
The problem is that
user
is always null.
Startup.cs:
namespace P.Server
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
//services.AddDbContext<ApplicationDbContext>(options =>
// options.UseSqlServer(
// Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSyncfusionBlazor();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), new MySqlServerVersion(new Version(8, 0, 26))));
services.AddOptions();
services.AddControllers();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer(options =>
{
options.Events.RaiseSuccessEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseErrorEvents = true;
})
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddAuthorization();
services.AddHttpContextAccessor();
services.AddSession();
//services.AddSingleton<IUserIdProvider, NameUserIdProvider>(); //this is for signalr
services.AddControllersWithViews();
services.AddRazorPages();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = false;
options.Password.RequiredLength = 4;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
});
services.AddServerSideBlazor();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
services.AddMvc().AddMvcOptions(options =>
{
options.EnableEndpointRouting = false;
});
services.AddMvcCore(options => options.OutputFormatters.Add(new XmlSerializerOutputFormatter()));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddMvc().AddXmlDataContractSerializerFormatters();
services.AddSignalR();
services.AddTransient<IEventSink, CustomEventSink>();
//services.AddCors(options =>
//{
// options.AddPolicy("EnableCORS", builder =>
// {
// builder.AllowAnyOrigin().AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials().Build();
// });
//});
//services.AddMvc(option => option.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// 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.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapHub<Broadcaster>("/broadcaster");
endpoints.MapHub<LoginCountHub>("/LoginCountHub");
endpoints.MapFallbackToFile("index.html");
});
CreateRoles(serviceProvider).Wait();
}
...
}
}