@page "/admin/users" @attribute [Microsoft.AspNetCore.Authorization.Authorize] @inject IUserService UserService @inject IUserContextAccessor UserContext @inject IAuditService Audit @inject NavigationManager Nav @rendermode InteractiveServer @using SharepointToolbox.Web.Core.Models @using SharepointToolbox.Web.Services.Audit @using SharepointToolbox.Web.Services.Auth @using SharepointToolbox.Web.Services.Session

User Management

Manage technician accounts and roles. Entra users are auto-provisioned on first OIDC login; local users are created here.

@if (!UserContext.IsAuthenticated || UserContext.Role != UserRole.Admin) {
Access denied. Admin role required.
return; } @if (!string.IsNullOrEmpty(_message)) {
@_message
}

Create local user

@if (_users.Count == 0) {
No users provisioned yet.
} else {
@foreach (var user in _users) { }
User Email Source Role Last Login Actions
@user.DisplayName @user.Email @(user.Provider == AuthProvider.Local ? "Local" : "Entra") @(user.LastLogin?.ToString("yyyy-MM-dd HH:mm") ?? "Never") @if (user.Provider == AuthProvider.Local) { } @if (user.Email != UserContext.Email) { } else { You }
} @if (_resetUser is not null) {

Reset password — @_resetUser.DisplayName

} @code { private List _users = new(); private string _message = string.Empty; private bool _isError; private string _newEmail = string.Empty; private string _newName = string.Empty; private UserRole _newRole = UserRole.TechN0; private string _newPassword = string.Empty; private AppUser? _resetUser; private string _resetPassword = string.Empty; protected override async Task OnInitializedAsync() { _users = (await UserService.GetAllAsync()).ToList(); } private async Task CreateLocalUserAsync() { try { var user = await UserService.CreateLocalUserAsync(_newEmail, _newName, _newRole, _newPassword); _users.Add(user); await Audit.LogAsync("UserCreated", "", Array.Empty(), $"Created local user {user.Email} ({user.DisplayName}) with role {user.Role}."); _message = $"Local user {user.DisplayName} created."; _isError = false; _newEmail = _newName = _newPassword = string.Empty; _newRole = UserRole.TechN0; } catch (Exception ex) { _message = $"Error: {ex.Message}"; _isError = true; } } private void OpenReset(AppUser user) { _resetUser = user; _resetPassword = string.Empty; } private async Task ResetPasswordAsync() { if (_resetUser is null) return; try { await UserService.SetPasswordAsync(_resetUser.Id, _resetPassword); await Audit.LogAsync("PasswordReset", "", Array.Empty(), $"Reset password for local user {_resetUser.Email} ({_resetUser.DisplayName})."); _message = $"Password reset for {_resetUser.DisplayName}."; _isError = false; _resetUser = null; } catch (Exception ex) { _message = $"Error: {ex.Message}"; _isError = true; } } private async Task OnRoleChange(AppUser user, ChangeEventArgs e) { if (!Enum.TryParse(e.Value?.ToString(), out var newRole)) return; try { var oldRole = user.Role; await UserService.UpdateRoleAsync(user.Id, newRole); user.Role = newRole; await Audit.LogAsync("RoleChanged", "", Array.Empty(), $"Changed role for {user.Email} ({user.DisplayName}) from {oldRole} to {newRole}."); _message = $"Role updated for {user.DisplayName}."; _isError = false; } catch (Exception ex) { _message = $"Error: {ex.Message}"; _isError = true; } } private async Task DeleteUserAsync(AppUser user) { await UserService.DeleteAsync(user.Id); _users.Remove(user); await Audit.LogAsync("UserDeleted", "", Array.Empty(), $"Removed {user.Provider} user {user.Email} ({user.DisplayName}), role {user.Role}."); _message = $"User {user.DisplayName} removed."; _isError = false; } }