First Commit

This commit is contained in:
2026-03-18 01:14:49 +01:00
commit 306248b11c
39 changed files with 1361 additions and 0 deletions
@@ -0,0 +1,15 @@
namespace Musify.Infrastructure.Identity
{
public class KeycloakConfiguration
{
public const string SectionName = "Keycloak";
public string BaseUrl { get; set; } = string.Empty;
public string Realm { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
}
@@ -0,0 +1,61 @@
using Ardalis.Result;
using Keycloak.Net;
using Keycloak.Net.Models.Users;
using Microsoft.Extensions.Logging;
using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Users.Contracts;
namespace Musify.Infrastructure.Identity
{
public class KeycloakIdentityService(
KeycloakClient keycloakClient,
KeycloakConfiguration keycloakConfiguration,
ILogger<KeycloakIdentityService> logger) : IKeycloakUserClient
{
public async Task<Result<KeycloakUserResponse>> GetUserByIdAsync(string keycloakId, CancellationToken cancellationToken)
{
User keycloakUser;
try
{
keycloakUser = await keycloakClient.GetUserAsync(keycloakConfiguration.Realm, keycloakId, cancellationToken);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception exception)
{
logger.LogWarning(exception, "Keycloak user with id {KeycloakId} could not be retrieved.", keycloakId);
return Result.NotFound();
}
return Result.Success(KeycloakUserMapper.Map(keycloakUser));
}
public async Task<IReadOnlyCollection<KeycloakUserResponse>> GetUsersAsync(CancellationToken cancellationToken, string search = "", int first = 0, int max = 20, string username = "")
{
var users = await keycloakClient.GetUsersAsync(
realm: keycloakConfiguration.Realm,
first: first,
max: max,
search: search,
username: username,
cancellationToken: cancellationToken);
return users
.Select(KeycloakUserMapper.Map)
.ToArray();
}
public Task<int> GetUsersCountAsync(CancellationToken cancellationToken, string search = "", string username = "")
{
return keycloakClient.GetUsersCountAsync(
keycloakConfiguration.Realm,
search: search,
username: username,
cancellationToken: cancellationToken);
}
}
}
@@ -0,0 +1,17 @@
using Keycloak.Net.Models.Users;
using Musify.Application.Users.Contracts;
namespace Musify.Infrastructure.Identity
{
public static class KeycloakUserMapper
{
public static KeycloakUserResponse Map(User keycloakUser)
{
return new KeycloakUserResponse(
Guid.Parse(keycloakUser.Id ?? throw new InvalidOperationException("Keycloak user Id is required.")),
keycloakUser.UserName ?? throw new InvalidOperationException("Keycloak user Name is required."),
keycloakUser.FirstName ?? throw new InvalidOperationException("Keycloak user FirstName is required."),
keycloakUser.LastName ?? throw new InvalidOperationException("Keycloak user SecondName is required."));
}
}
}