using Google.Apis.Oauth2.v2.Data; using System.Security.Claims; using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Models; using Hutopy.Infrastructure.Utils; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; namespace Hutopy.Infrastructure.Identity; public class IdentityService( ApplicationUserManager userManager, SignInManager signInManager, IUserClaimsPrincipalFactory userClaimsPrincipalFactory, IAuthorizationService authorizationService, IHttpContextAccessor contextAccessor, IOptionsSnapshot jwtOptions ) : IIdentityService { public async Task GetUserNameAsync(Guid userId) { var user = await userManager.FindByIdAsync(userId.ToString()); return user?.UserName; } public async Task GetUserByUserNameAsync(string userName) { var user = await userManager.FindByNameAsync(userName); if (user == null) return null; return new() { Id = user.Id, Username = user.UserName!, PhoneNumber = user.PhoneNumber, Email = user.Email, Alias = user.Alias, Firstname = user.Firstname, Lastname = user.Lastname, BirthDate = user.BirthDate, Address = user.Address, PortraitUrl = user.PortraitUrl }; } public async Task> CreateUserAsync(Userinfo userInfo) { var applicationUser = new ApplicationUser { UserName = userInfo.Name, Email = userInfo.Email, Firstname = userInfo.GivenName, Lastname = userInfo.FamilyName }; var password = Guid.NewGuid().ToString("N")[..32]; var identityResult = await userManager.CreateAsync(applicationUser, password); var applicationResult = identityResult.ToApplicationResult(); var result = new Result(applicationUser.Id, applicationResult.Succeeded, applicationResult.Errors); return result; } public async Task> CreateUserAsync(string email, string userName, string firstName, string lastName, string password) { var applicationUser = new ApplicationUser { UserName = userName, Email = email, Firstname = firstName, Lastname = lastName }; var response = await userManager.CreateAsync(applicationUser, password); if (!response.Succeeded) { throw new BadHttpRequestException(response.Errors.First().Description); } var result = new Result(applicationUser.Id, response.Succeeded, response.ToApplicationResult().Errors); return result; } public async Task> UpdateCurrentUserAsync(UserModel userModel) { var applicationUser = await userManager.FindByIdAsync(userModel.Id.ToString()); if (applicationUser is null) return Result.Failure(Guid.Empty, new[] { "User not found." }); applicationUser.Id = userModel.Id; applicationUser.Email = userModel.Email; applicationUser.PhoneNumber = userModel.PhoneNumber; applicationUser.Alias = userModel.Alias; applicationUser.Firstname = userModel.Firstname; applicationUser.Lastname = userModel.Lastname; applicationUser.BirthDate = userModel.BirthDate; applicationUser.Address = userModel.Address; applicationUser.PortraitUrl = userModel.PortraitUrl; var response = await userManager.UpdateAsync(applicationUser); var applicationResult = response.ToApplicationResult(); var result = new Result( userModel.Id, applicationResult.Succeeded, applicationResult.Errors); return result; } private static UserModel BuildModelFrom(ApplicationUser response) { var userModel = new UserModel { Id = response.Id, Username = response.UserName ?? string.Empty, PhoneNumber = response.PhoneNumber ?? string.Empty, Email = response.Email ?? string.Empty, PortraitUrl = response.PortraitUrl, Alias = response.Alias, Firstname = response.Firstname, Lastname = response.Lastname, BirthDate = response.BirthDate, Address = response.Address, }; return userModel; } public async Task FindUserByIdAsync(string id) { var user = await userManager.FindByIdAsync(id); if (user == null) return null; var userModel = BuildModelFrom(user); return userModel; } public async Task FindUserByEmailAsync(string email) { var response = await userManager.FindByEmailAsync(email); if (response == null) return null; return BuildModelFrom(response); } public async Task GetCurrentUserAsync() { var currentUserId = contextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(currentUserId)) { return null; } return await FindUserByIdAsync(currentUserId); } public async Task UpdateCurrentUserPortraitUrlAsync(string url) { var userModel = await GetCurrentUserAsync(); var applicationUser = await userManager.FindByIdAsync(userModel.Id.ToString()); if (applicationUser is null) return Result.Failure(["ApplicationUser not found."]); applicationUser.PortraitUrl = url; var response = await userManager.UpdateAsync(applicationUser); return response.ToApplicationResult(); } public async Task IsInRoleAsync(Guid userId, string role) { var user = await userManager.FindByIdAsync(userId.ToString()); return user != null && await userManager.IsInRoleAsync(user, role); } public async Task CurrentUserIsInRoleAsync(string role) { var currentUserModel = await GetCurrentUserAsync(); var currentUser = await userManager.FindByIdAsync(currentUserModel.Id.ToString()); return currentUser != null && await userManager.IsInRoleAsync(currentUser, role); } public async Task AuthorizeAsync(Guid userId, string policyName) { var user = await userManager.FindByIdAsync(userId.ToString()); if (user == null) { return false; } var principal = await userClaimsPrincipalFactory.CreateAsync(user); var result = await authorizationService.AuthorizeAsync(principal, policyName); return result.Succeeded; } public async Task DeleteUserAsync(string userId) { var user = await userManager.FindByIdAsync(userId); return user != null ? await DeleteUserAsync(user) : Result.Success(); } public async Task DeleteUserAsync(ApplicationUser user) { var result = await userManager.DeleteAsync(user); return result.ToApplicationResult(); } public async Task AddRoleAsync(string userId, string role) { var hasAdminAccess = await CurrentUserIsInRoleAsync("Administrator"); if (!hasAdminAccess) return Result.Failure(new[] { "Only administrator can assign new roles to a user." }); var user = await userManager.FindByIdAsync(userId); if (user is null) return Result.Failure(new[] { "User not found." }); var result = await userManager.AddToRoleAsync(user, role); return result.ToApplicationResult(); } public async Task> GetCurrentUserRolesAsync() { var currentUserModel = await GetCurrentUserAsync(); var currentUser = await userManager.FindByIdAsync(currentUserModel.Id.ToString()); if (currentUser is null) return []; var userRoles = await userManager.GetRolesAsync(currentUser); return userRoles; } public async Task LoginAsync(string userName, string password) { var result = await signInManager.PasswordSignInAsync(userName, password, isPersistent: false, lockoutOnFailure: false); if (!result.Succeeded) { return null; } var user = await GetUserByUserNameAsync(userName); if (user is null) throw new InvalidOperationException(); var token = JwtTokenHelper.GenerateJwtToken( expiresIn: jwtOptions.Value.Lifetime, issuer: jwtOptions.Value.Issuer, audience: jwtOptions.Value.Audience, key: jwtOptions.Value.Key, userId: user.Id.ToString(), email: user.Email, alias: user.Alias, firstname: user.Firstname, lastname: user.Lastname, portraitUrl: user.PortraitUrl); return token; } }