diff --git a/src/ServiceDefaults/ServiceDefaults.csproj b/src/ServiceDefaults/ServiceDefaults.csproj
index 19a3a614..42f6e61d 100644
--- a/src/ServiceDefaults/ServiceDefaults.csproj
+++ b/src/ServiceDefaults/ServiceDefaults.csproj
@@ -11,11 +11,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/src/Web/Features/UserManagement/UserManagementHandler.cs b/src/Web/Features/UserManagement/UserManagementHandler.cs
index 73183c1b..ef1c5f15 100644
--- a/src/Web/Features/UserManagement/UserManagementHandler.cs
+++ b/src/Web/Features/UserManagement/UserManagementHandler.cs
@@ -8,115 +8,130 @@
//=======================================================
using Auth0.ManagementApi;
-using Auth0.ManagementApi.Models;
-using Auth0.ManagementApi.Paging;
+using Auth0.ManagementApi.Users;
namespace MyBlog.Web.Features.UserManagement;
public sealed class UserManagementHandler(
- IConfiguration configuration,
- IHttpClientFactory httpClientFactory)
- : IRequestHandler>>,
- IRequestHandler,
- IRequestHandler,
- IRequestHandler>>
-{
- public async Task>> Handle(
- GetUsersWithRolesQuery request, CancellationToken ct)
- {
- try
- {
- var client = await GetManagementClientAsync(ct);
- var users = await client.Users.GetAllAsync(new GetUsersRequest(), new PaginationInfo(), ct);
- var result = new List();
- foreach (var user in users)
- {
- var roles = await client.Users.GetRolesAsync(user.UserId, new PaginationInfo(), ct);
- result.Add(new UserWithRolesDto(
- user.UserId,
- user.Email ?? string.Empty,
- user.FullName ?? user.Email ?? string.Empty,
- roles.Select(r => r.Name ?? string.Empty).ToList()));
- }
- return Result.Ok>(result);
- }
- catch (Exception ex)
- {
- return Result.Fail>(ex.Message);
- }
- }
+IConfiguration configuration,
+IHttpClientFactory httpClientFactory)
+: IRequestHandler>>,
+IRequestHandler,
+IRequestHandler,
+IRequestHandler>>
+{
+public async Task>> Handle(
+GetUsersWithRolesQuery request, CancellationToken ct)
+{
+try
+{
+var client = await GetManagementClientAsync(ct);
+var usersPager = await client.Users.ListAsync(new ListUsersRequestParameters(), cancellationToken: ct);
+var result = new List();
+await foreach (var user in usersPager)
+{
+var rolesPager = await client.Users.Roles.ListAsync(
+user.UserId ?? string.Empty, new ListUserRolesRequestParameters(), cancellationToken: ct);
+var roles = new List();
+await foreach (var role in rolesPager)
+{
+roles.Add(role.Name ?? string.Empty);
+}
+result.Add(new UserWithRolesDto(
+user.UserId ?? string.Empty,
+user.Email ?? string.Empty,
+user.Name ?? user.Email ?? string.Empty,
+roles));
+}
+return Result.Ok>(result);
+}
+catch (Exception ex)
+{
+return Result.Fail>(ex.Message);
+}
+}
- public async Task Handle(AssignRoleCommand request, CancellationToken ct)
- {
- try
- {
- var client = await GetManagementClientAsync(ct);
- await client.Users.AssignRolesAsync(request.UserId,
- new AssignRolesRequest { Roles = [request.RoleId] }, ct);
- return Result.Ok();
- }
- catch (Exception ex)
- {
- return Result.Fail(ex.Message);
- }
- }
+public async Task Handle(AssignRoleCommand request, CancellationToken ct)
+{
+try
+{
+var client = await GetManagementClientAsync(ct);
+await client.Users.Roles.AssignAsync(
+request.UserId,
+new AssignUserRolesRequestContent { Roles = [request.RoleId] },
+cancellationToken: ct);
+return Result.Ok();
+}
+catch (Exception ex)
+{
+return Result.Fail(ex.Message);
+}
+}
- public async Task Handle(RemoveRoleCommand request, CancellationToken ct)
- {
- try
- {
- var client = await GetManagementClientAsync(ct);
- await client.Users.RemoveRolesAsync(request.UserId,
- new AssignRolesRequest { Roles = [request.RoleId] }, ct);
- return Result.Ok();
- }
- catch (Exception ex)
- {
- return Result.Fail(ex.Message);
- }
- }
+public async Task Handle(RemoveRoleCommand request, CancellationToken ct)
+{
+try
+{
+var client = await GetManagementClientAsync(ct);
+await client.Users.Roles.DeleteAsync(
+request.UserId,
+new DeleteUserRolesRequestContent { Roles = [request.RoleId] },
+cancellationToken: ct);
+return Result.Ok();
+}
+catch (Exception ex)
+{
+return Result.Fail(ex.Message);
+}
+}
- public async Task>> Handle(GetAvailableRolesQuery request, CancellationToken ct)
- {
- try
- {
- var client = await GetManagementClientAsync(ct);
- var roles = await client.Roles.GetAllAsync(new GetRolesRequest(), new PaginationInfo(), ct);
- return Result.Ok>(
- roles.Select(r => new RoleDto(r.Id ?? string.Empty, r.Name ?? string.Empty)).ToList());
- }
- catch (Exception ex)
- {
- return Result.Fail>(ex.Message);
- }
- }
+public async Task>> Handle(GetAvailableRolesQuery request, CancellationToken ct)
+{
+try
+{
+var client = await GetManagementClientAsync(ct);
+var rolesPager = await client.Roles.ListAsync(new ListRolesRequestParameters(), cancellationToken: ct);
+var roles = new List();
+await foreach (var role in rolesPager)
+{
+roles.Add(new RoleDto(role.Id ?? string.Empty, role.Name ?? string.Empty));
+}
+return Result.Ok>(roles);
+}
+catch (Exception ex)
+{
+return Result.Fail>(ex.Message);
+}
+}
- private async Task GetManagementClientAsync(CancellationToken ct)
- {
- var domain = configuration["Auth0:ManagementApiDomain"]
- ?? throw new InvalidOperationException("Auth0:ManagementApiDomain not configured.");
- var clientId = configuration["Auth0:ManagementApiClientId"]
- ?? throw new InvalidOperationException("Auth0:ManagementApiClientId not configured.");
- var clientSecret = configuration["Auth0:ManagementApiClientSecret"]
- ?? throw new InvalidOperationException("Auth0:ManagementApiClientSecret not configured.");
+private async Task GetManagementClientAsync(CancellationToken ct)
+{
+var domain = configuration["Auth0:ManagementApiDomain"]
+?? throw new InvalidOperationException("Auth0:ManagementApiDomain not configured.");
+var clientId = configuration["Auth0:ManagementApiClientId"]
+?? throw new InvalidOperationException("Auth0:ManagementApiClientId not configured.");
+var clientSecret = configuration["Auth0:ManagementApiClientSecret"]
+?? throw new InvalidOperationException("Auth0:ManagementApiClientSecret not configured.");
- var httpClient = httpClientFactory.CreateClient();
- var tokenResponse = await httpClient.PostAsJsonAsync(
- $"https://{domain}/oauth/token",
- new
- {
- client_id = clientId,
- client_secret = clientSecret,
- audience = $"https://{domain}/api/v2/",
- grant_type = "client_credentials"
- }, ct);
- tokenResponse.EnsureSuccessStatusCode();
- var tokenData = await tokenResponse.Content.ReadFromJsonAsync(ct);
- return new ManagementApiClient(tokenData!.AccessToken, domain);
- }
+var httpClient = httpClientFactory.CreateClient();
+var tokenResponse = await httpClient.PostAsJsonAsync(
+$"https://{domain}/oauth/token",
+new
+{
+client_id = clientId,
+client_secret = clientSecret,
+audience = $"https://{domain}/api/v2/",
+grant_type = "client_credentials"
+}, ct);
+tokenResponse.EnsureSuccessStatusCode();
+var tokenData = await tokenResponse.Content.ReadFromJsonAsync(ct);
+return new ManagementApiClient(
+token: tokenData!.AccessToken,
+clientOptions: new ClientOptions { BaseUrl = $"https://{domain}/api/v2" });
+}
- private sealed class TokenResponse
- {
- public string AccessToken { get; init; } = string.Empty;
- }
+private sealed class TokenResponse
+{
+public string AccessToken { get; init; } = string.Empty;
+}
}
diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj
index b798d3df..c81835dc 100644
--- a/src/Web/Web.csproj
+++ b/src/Web/Web.csproj
@@ -9,8 +9,9 @@
-
+
+
diff --git a/tests/Architecture.Tests/Architecture.Tests.csproj b/tests/Architecture.Tests/Architecture.Tests.csproj
index df12ee9a..cbad7d9e 100644
--- a/tests/Architecture.Tests/Architecture.Tests.csproj
+++ b/tests/Architecture.Tests/Architecture.Tests.csproj
@@ -9,12 +9,12 @@
-
+
-
+
-
+
diff --git a/tests/Integration.Tests/Integration.Tests.csproj b/tests/Integration.Tests/Integration.Tests.csproj
index 71054000..ec7602f4 100644
--- a/tests/Integration.Tests/Integration.Tests.csproj
+++ b/tests/Integration.Tests/Integration.Tests.csproj
@@ -11,13 +11,13 @@
-
+
-
+
-
+
diff --git a/tests/Unit.Tests/Unit.Tests.csproj b/tests/Unit.Tests/Unit.Tests.csproj
index a929434e..b1f08bbe 100644
--- a/tests/Unit.Tests/Unit.Tests.csproj
+++ b/tests/Unit.Tests/Unit.Tests.csproj
@@ -16,16 +16,16 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
-
+