diff --git a/services/identity/src/EShopOnAbp.IdentityService.Application.Contracts/ETOs/UserLoggedInEto.cs b/services/identity/src/EShopOnAbp.IdentityService.Application.Contracts/ETOs/UserLoggedInEto.cs new file mode 100644 index 00000000..d163d5f4 --- /dev/null +++ b/services/identity/src/EShopOnAbp.IdentityService.Application.Contracts/ETOs/UserLoggedInEto.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus; + +namespace EShopOnAbp.IdentityService.ETOs +{ + [EventName("EShopOnAbp.Identity.UserLoggedIn")] + [Serializable] + public class UserLoggedInEto : EtoBase + { + public Guid Id { get; set; } + public string Email { get; set; } + public string Phone { get; set; } + public string UserName { get; set; } + public bool IsEmailVerified { get; set; } + } +} \ No newline at end of file diff --git a/services/identity/src/EShopOnAbp.IdentityService.Application/UserLoggedInEventHandler.cs b/services/identity/src/EShopOnAbp.IdentityService.Application/UserLoggedInEventHandler.cs new file mode 100644 index 00000000..058db2d8 --- /dev/null +++ b/services/identity/src/EShopOnAbp.IdentityService.Application/UserLoggedInEventHandler.cs @@ -0,0 +1,98 @@ +using System.Threading.Tasks; +using EShopOnAbp.IdentityService.ETOs; +using Microsoft.Extensions.Logging; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Identity; +using Volo.Abp.Uow; + +namespace EShopOnAbp.IdentityService; + +public class UserLoggedInEventHandler : IDistributedEventHandler, ITransientDependency +{ + private readonly IdentityUserManager _userManager; + private readonly ILogger _logger; + + public UserLoggedInEventHandler(IdentityUserManager userManager, ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + + [UnitOfWork] + public async virtual Task HandleEventAsync(UserLoggedInEto eventData) + { + if (eventData == null) + { + _logger.LogWarning($"Handling UserLoggedInEvent failed! No user information found!"); + return; + } + + var user = await _userManager.FindByIdAsync(eventData.Id.ToString()); + + if (user == null) + { + await CreateCurrentUserAsync(eventData); + } + else + { + await UpdateCurrentUserAsync(user, eventData); + } + } + + protected virtual async Task CreateCurrentUserAsync(UserLoggedInEto userInfo) + { + var user = new IdentityUser( + userInfo.Id, + userInfo.UserName, + userInfo.Email); + + user.SetEmailConfirmed(userInfo.IsEmailVerified); + + if (!string.IsNullOrEmpty(userInfo.Phone)) + { + user.SetPhoneNumber(userInfo.Phone, false); + } + + // This should run once to sync the admin userIds that seeded by IdentityModule and the Keycloak admin + if (userInfo.UserName == "admin") + { + var adminUser = await _userManager.FindByNameAsync("admin"); + await _userManager.DeleteAsync(adminUser); + } + + var result = await _userManager.CreateAsync(user); + + if (!result.Succeeded) + { + throw new AbpException(string.Join('\n', result.Errors)); + } + + _logger.LogInformation($"Handling UserLoggedInEvent... Created new user with Id:{userInfo.Id}"); + } + + protected virtual async Task UpdateCurrentUserAsync(IdentityUser user, UserLoggedInEto userInfo) + { + if (user.Email != userInfo.Email) + { + _logger.LogInformation($"Handling UserLoggedInEvent... Updating the user email with:{userInfo.Email}"); + await _userManager.SetEmailAsync(user, userInfo.Email); + } + + if (user.PhoneNumber != userInfo.Phone) + { + _logger.LogInformation( + $"Handling UserLoggedInEvent... Updating the user phone with:{userInfo.Phone}"); + await _userManager.SetPhoneNumberAsync(user, userInfo.Phone); + } + + if (user.UserName != userInfo.UserName) + { + _logger.LogInformation( + $"Handling UserLoggedInEvent... Updating the user name with:{userInfo.UserName}"); + await _userManager.SetUserNameAsync(user, userInfo.UserName); + } + } +} \ No newline at end of file diff --git a/services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs b/services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs index 1d837bb5..c979c499 100644 --- a/services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs +++ b/services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Volo.Abp; using Volo.Abp.Modularity; @@ -43,7 +44,6 @@ public class IdentityServiceHttpApiHostModule : AbpModule apiTitle: "IdentityService API" ); - context.Services.AddCors(options => { options.AddDefaultPolicy(builder => @@ -62,6 +62,12 @@ public class IdentityServiceHttpApiHostModule : AbpModule .AllowCredentials(); }); }); + + // Keycloak handles the user creation that a user name can be multiple words + Configure(options => + { + options.User.AllowedUserNameCharacters = null; + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context)