From 15bb7f6593c56bc149ea73b8f35e8588fe72ecf5 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 20 Jun 2025 19:26:21 +0200 Subject: [PATCH] Add recent auth log attempts to user details page (#948) --- .../Users/View/Components/AuthLogTable.razor | 84 +++++++++++++++++++ .../View/Components/EmailClaimTable.razor | 4 +- .../Main/Pages/Users/View/Index.razor | 20 +++++ .../AliasVault.Admin/wwwroot/css/tailwind.css | 9 ++ 4 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/AuthLogTable.razor diff --git a/apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/AuthLogTable.razor b/apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/AuthLogTable.razor new file mode 100644 index 000000000..78d24ec75 --- /dev/null +++ b/apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/AuthLogTable.razor @@ -0,0 +1,84 @@ +@using AliasVault.RazorComponents.Tables +@using AliasVault.Shared.Models.Enums + +
+ +
+ +@if (AuthLogList.Any()) +{ + + @foreach (var log in SortedAuthLogList) + { + + @log.Id + @log.Timestamp.ToString("yyyy-MM-dd HH:mm") + @log.EventType + + @log.IpAddress + @log.Client + + } + +} +else +{ +
+
+
+ +
+

No authentication logs

+

This user has no recent authentication attempts.

+
+
+} + +@code { + /// + /// Gets or sets the username for linking to full auth logs. + /// + [Parameter] + public string Username { get; set; } = string.Empty; + + /// + /// Gets or sets the list of auth logs to display (should be limited to recent logs). + /// + [Parameter] + public List AuthLogList { get; set; } = []; + + private string SortColumn { get; set; } = "Timestamp"; + private SortDirection SortDirection { get; set; } = SortDirection.Descending; + + private readonly List _authLogTableColumns = [ + new TableColumn { Title = "ID", PropertyName = "Id" }, + new TableColumn { Title = "Time", PropertyName = "Timestamp" }, + new TableColumn { Title = "Event", PropertyName = "EventType" }, + new TableColumn { Title = "Success", PropertyName = "IsSuccess" }, + new TableColumn { Title = "IP", PropertyName = "IpAddress" }, + new TableColumn { Title = "Client", PropertyName = "Client" }, + ]; + + private IEnumerable SortedAuthLogList => SortList(AuthLogList, SortColumn, SortDirection); + + private void HandleSortChanged((string column, SortDirection direction) sort) + { + SortColumn = sort.column; + SortDirection = sort.direction; + StateHasChanged(); + } + + private static IEnumerable SortList(List authLogs, string sortColumn, SortDirection sortDirection) + { + return sortColumn switch + { + "Id" => SortableTable.SortListByProperty(authLogs, a => a.Id, sortDirection), + "Timestamp" => SortableTable.SortListByProperty(authLogs, a => a.Timestamp, sortDirection), + "EventType" => SortableTable.SortListByProperty(authLogs, a => a.EventType, sortDirection), + "IsSuccess" => SortableTable.SortListByProperty(authLogs, a => a.IsSuccess, sortDirection), + "IpAddress" => SortableTable.SortListByProperty(authLogs, a => a.IpAddress, sortDirection), + "Client" => SortableTable.SortListByProperty(authLogs, a => a.Client, sortDirection), + _ => authLogs + }; + } +} \ No newline at end of file diff --git a/apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/EmailClaimTable.razor b/apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/EmailClaimTable.razor index 70a4704ab..5242eafb1 100644 --- a/apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/EmailClaimTable.razor +++ b/apps/server/AliasVault.Admin/Main/Pages/Users/View/Components/EmailClaimTable.razor @@ -1,7 +1,7 @@ @using AliasVault.RazorComponents.Tables
-
+
@@ -134,7 +134,7 @@ else StateHasChanged(); } - /// + /// /// This method will toggle the disabled status of an email claim. /// private async Task ToggleEmailClaimStatus(UserEmailClaimWithCount entry) diff --git a/apps/server/AliasVault.Admin/Main/Pages/Users/View/Index.razor b/apps/server/AliasVault.Admin/Main/Pages/Users/View/Index.razor index 7f6e28db1..8f8d52f52 100644 --- a/apps/server/AliasVault.Admin/Main/Pages/Users/View/Index.razor +++ b/apps/server/AliasVault.Admin/Main/Pages/Users/View/Index.razor @@ -101,6 +101,18 @@ else
+
+
+
+

Recent authentication logs

+

+ Shows the last 5 authentication attempts for this user. Click "View all auth logs" to see complete history. +

+ +
+
+
+
@@ -130,6 +142,7 @@ else private int TwoFactorKeysCount { get; set; } private List RefreshTokenList { get; set; } = []; private List VaultList { get; set; } = []; + private List AuthLogList { get; set; } = []; /// protected override async Task OnInitializedAsync() @@ -204,6 +217,13 @@ else .OrderBy(x => x.UpdatedAt) .ToListAsync(); + // Load recent auth logs for this user (last 3 records). + AuthLogList = await dbContext.AuthLogs + .Where(x => x.Username == User.UserName) + .OrderByDescending(x => x.Timestamp) + .Take(5) + .ToListAsync(); + IsLoading = false; StateHasChanged(); } diff --git a/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css b/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css index 1db02e6dd..ef36aad8e 100644 --- a/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css +++ b/apps/server/AliasVault.Admin/wwwroot/css/tailwind.css @@ -1541,6 +1541,11 @@ video { line-height: 1rem; } +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + .font-bold { font-weight: 700; } @@ -1561,6 +1566,10 @@ video { font-weight: 600; } +.italic { + font-style: italic; +} + .leading-6 { line-height: 1.5rem; }