diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs index 649139f..6db5dc3 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs @@ -11,7 +11,7 @@ public class CreateUserCommandHandler(IUserRepository userRepository) : IRequest public async Task Handle(CreateUserCommand request, CancellationToken cancellationToken) { - var resultUserCreated = await _userRepository.DoNewUserCreationActions(request. AddUserRequest.Tenant!, request.AddUserRequest.ToDomain()); + var resultUserCreated = await _userRepository.CreateNewUserActions(request. AddUserRequest.Tenant!, request.AddUserRequest.ToDomain()); if (resultUserCreated.IsSuccess) { return Result.Success(); diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs index b5e5865..6a01fae 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs @@ -9,7 +9,7 @@ public static class UserMapper public static User ToDomain(this AddUserRequest userRequest) { var attributes = userRequest.Attributes!.ToDomain(userRequest.Tenant!); - return new User(userRequest.Username!, userRequest.Email!, userRequest.FirstName!, userRequest.LastName!, attributes); + return new User(userRequest.Username!, userRequest.Password, userRequest.Email!, userRequest.FirstName!, userRequest.LastName!, attributes); } public static Attributes ToDomain(this AttributesRequest attributes, string tenant) diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs index bd06882..b873a01 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs @@ -2,7 +2,7 @@ namespace TokenManager.Application.Services.Requests.User { - public record AddUserRequest(string? Username, string? Email, string? FirstName, string? LastName, AttributesRequest? Attributes) + public record AddUserRequest(string Username, string Password, string Email, string FirstName, string LastName, AttributesRequest? Attributes) { [JsonIgnore] public string? Tenant { get; set; } diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs index 3e7550f..260db5e 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs @@ -1,13 +1,16 @@ -namespace TokenManager.Domain.Entities +using Newtonsoft.Json; + +namespace TokenManager.Domain.Entities { public class User { + [JsonIgnore] + public string Password { get; set; } = null!; public string? Id { get; set; } public bool Enabled { get; set; } public bool EmailVerified { get; set; } public string? Username { get; set; } = null!; - public string? Email { get; set; } - public string? Password { get; set; } + public string? Email { get; set; } public string? FirstName { get; set; } public string? LastName { get; set; } public bool Totp { get; set; } @@ -29,11 +32,12 @@ public User(string userName, string password) Password = password; } - public User(string userName, string email, string firstName, string lastName, Attributes attributes) + public User(string userName, string password, string email, string firstName, string lastName, Attributes attributes) { Enabled = true; - EmailVerified = false; + EmailVerified = true; Email = email; + Password = password; FirstName = firstName; Username = userName; LastName = lastName; diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Errors/UserErrors.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Errors/UserErrors.cs index 3035567..b1c1af4 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Errors/UserErrors.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Errors/UserErrors.cs @@ -16,6 +16,11 @@ public static class UserErrors $"An error occurred while trying to get JWT token. Please check username and password. {TechnicalMessage}" ); + public static Error WrongPasswordDefinition => new( + "User.WrongPasswordDefinition", + $"An error occurred while trying to add a new password to the user. {TechnicalMessage}" + ); + public static void SetTechnicalMessage(string technicalMessage) { TechnicalMessage = technicalMessage; diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs index d4f50c3..239da69 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs @@ -6,9 +6,11 @@ namespace TokenManager.Domain.Interfaces public interface IUserRepository { Task> GetAccessTokenAsync(string tenant); - Task DoNewUserCreationActions(string tenant, User user); + Task CreateNewUserActions(string tenant, User user); Task CreateNewUserAsync(User user); Task> GetUserAsync(string userName); + Task ResetPasswordAsync(string userId, string password); + Task SendEmailVerificationAsync(string userId); Task> LoginAsync(User user); } } diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs index fbb5786..e30cdae 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs @@ -64,7 +64,7 @@ public async Task> GetAccessTokenAsync(string tenant) return Result.Failure(UserErrors.TokenGenerationError); } - public async Task DoNewUserCreationActions(string tenant, User user) + public async Task CreateNewUserActions(string tenant, User user) { var tokenBearerResult = await GetAccessTokenAsync(tenant); if (tokenBearerResult.IsSuccess) @@ -75,7 +75,8 @@ public async Task DoNewUserCreationActions(string tenant, User user) if (response.IsSuccessStatusCode) { - var userName = await GetUserAsync(user.Username); + var keycloakUser = await GetUserAsync(user.Username!); + await ResetPasswordAsync(keycloakUser.Value.Id!, user.Password!); return Result.Success(); } @@ -115,6 +116,59 @@ public async Task> GetUserAsync(string userName) return Result.Success(user[0]); } + public async Task ResetPasswordAsync(string userId, string password) + { + var url = urlUserActions + .AppendPathSegment(userId) + .AppendPathSegment("reset-password"); + + var passwordData = new + { + type = "password", + temporary = false, + value = password + }; + + var json = JsonConvert.SerializeObject(passwordData, Settings); + var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await _httpClient.PutAsync(url, httpContent); + + if (response.IsSuccessStatusCode) + { + return Result.Success(); + } + + var responseMessage = await response.Content.ReadAsStringAsync(); + UserErrors.SetTechnicalMessage(responseMessage); + return Result.Failure(UserErrors.InvalidUserNameOrPasswordError); + } + + public async Task SendEmailVerificationAsync(string userId) + { + var url = urlUserActions + .AppendPathSegment(userId); + + var requestData = new + { + requiredActions = new string[] { "VERIFY_EMAIL" } + }; + + var json = JsonConvert.SerializeObject(requestData, Settings); + var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await _httpClient.PutAsync(url, httpContent); + + if (response.IsSuccessStatusCode) + { + url = url.AppendPathSegment("send-verify-email"); + await _httpClient.PutAsync(url, default!); + return Result.Success(); + } + + var responseMessage = await response.Content.ReadAsStringAsync(); + UserErrors.SetTechnicalMessage(responseMessage); + return Result.Failure(UserErrors.InvalidUserNameOrPasswordError); + } + public async Task> LoginAsync(User user) { var requestData = new FormUrlEncodedContent(