From bd2410a98eeda264b57c72f4f4b9046055c86a0f Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:10:32 -0400 Subject: [PATCH] Test: Google oauth --- Directory.Packages.props | 3 +- src/Application/Application.csproj | 1 + .../Common/Interfaces/IGoogleService.cs | 8 ++++ .../Common/Interfaces/IIdentityService.cs | 5 ++- .../Google/Commands/CreateGoogleUser.cs | 20 ++++++++++ src/Domain/Domain.csproj | 1 + src/Domain/Interfaces/IUserService.cs | 5 ++- .../Identity/ApplicationUser.cs | 1 + .../Identity/IdentityService.cs | 18 +++++++++ src/Infrastructure/Services/GoogleService.cs | 24 ++++++++++++ src/Infrastructure/Services/UserService.cs | 39 +++++++++++++------ src/Web/Endpoints/Google.cs | 22 +++++++++++ 12 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 src/Application/Common/Interfaces/IGoogleService.cs create mode 100644 src/Application/Google/Commands/CreateGoogleUser.cs create mode 100644 src/Infrastructure/Services/GoogleService.cs create mode 100644 src/Web/Endpoints/Google.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 74620e0..758ff65 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,11 +7,12 @@ - + + diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index 8544947..34570d3 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Application/Common/Interfaces/IGoogleService.cs b/src/Application/Common/Interfaces/IGoogleService.cs new file mode 100644 index 0000000..aba62f7 --- /dev/null +++ b/src/Application/Common/Interfaces/IGoogleService.cs @@ -0,0 +1,8 @@ +using Google.Apis.Oauth2.v2.Data; + +namespace Hutopy.Application.Common.Interfaces; + +public interface IGoogleService +{ + Task GetUserInfoAsync(string accessToken); +} diff --git a/src/Application/Common/Interfaces/IIdentityService.cs b/src/Application/Common/Interfaces/IIdentityService.cs index 5dbcb4c..399b19c 100644 --- a/src/Application/Common/Interfaces/IIdentityService.cs +++ b/src/Application/Common/Interfaces/IIdentityService.cs @@ -1,4 +1,5 @@ -using Hutopy.Application.Common.Models; +using Google.Apis.Oauth2.v2.Data; +using Hutopy.Application.Common.Models; namespace Hutopy.Application.Common.Interfaces; @@ -11,6 +12,8 @@ public interface IIdentityService Task AuthorizeAsync(string userId, string policyName); Task<(Result Result, string UserId)> CreateUserAsync(string userName, string password); + + Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo); Task DeleteUserAsync(string userId); } diff --git a/src/Application/Google/Commands/CreateGoogleUser.cs b/src/Application/Google/Commands/CreateGoogleUser.cs new file mode 100644 index 0000000..a5f2bca --- /dev/null +++ b/src/Application/Google/Commands/CreateGoogleUser.cs @@ -0,0 +1,20 @@ +using Hutopy.Application.Common.Interfaces; + +namespace Hutopy.Application.Google.Commands; + +public record CreateGoogleUserCommand : IRequest +{ + public required string AccessToken { get; init; } +} + +public class CreateGoogleUser( + IApplicationDbContext context + ) : IRequestHandler +{ + public async Task Handle(CreateGoogleUserCommand request, CancellationToken cancellationToken) + { + await context.SaveChangesAsync(cancellationToken); + + return Guid.NewGuid(); + } +} diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index efab6e8..56e216d 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Domain/Interfaces/IUserService.cs b/src/Domain/Interfaces/IUserService.cs index c3bd5ac..c59cc2a 100644 --- a/src/Domain/Interfaces/IUserService.cs +++ b/src/Domain/Interfaces/IUserService.cs @@ -1,4 +1,5 @@ -using Hutopy.Domain.Models; +using Google.Apis.Oauth2.v2.Data; +using Hutopy.Domain.Models; namespace Hutopy.Domain.Interfaces; @@ -6,6 +7,8 @@ public interface IUserService { Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password); + Task CreateUserAsync(Userinfo userInfo); + Task FindUserByIdAsync(string id); Task FindUserByEmailAsync(string id); diff --git a/src/Infrastructure/Identity/ApplicationUser.cs b/src/Infrastructure/Identity/ApplicationUser.cs index 0462aff..39cb433 100644 --- a/src/Infrastructure/Identity/ApplicationUser.cs +++ b/src/Infrastructure/Identity/ApplicationUser.cs @@ -6,4 +6,5 @@ public class ApplicationUser : IdentityUser { public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; + //public string Gender { get; set; } = string.Empty; } diff --git a/src/Infrastructure/Identity/IdentityService.cs b/src/Infrastructure/Identity/IdentityService.cs index 71a3886..5b32670 100644 --- a/src/Infrastructure/Identity/IdentityService.cs +++ b/src/Infrastructure/Identity/IdentityService.cs @@ -1,3 +1,4 @@ +using Google.Apis.Oauth2.v2.Data; using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Models; using Microsoft.AspNetCore.Authorization; @@ -30,6 +31,23 @@ public class IdentityService( return (result.ToApplicationResult(), user.Id); } + + public async Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo) + { + var user = new ApplicationUser + { + UserName = userInfo.Name, + Email = userInfo.Email, + FirstName = userInfo.GivenName, + LastName = userInfo.FamilyName + }; + + var password = Guid.NewGuid().ToString("N")[..32]; + + var result = await userManager.CreateAsync(user, password); + + return (result.ToApplicationResult(), user.Id); + } public async Task IsInRoleAsync(string userId, string role) { diff --git a/src/Infrastructure/Services/GoogleService.cs b/src/Infrastructure/Services/GoogleService.cs new file mode 100644 index 0000000..b8abdf2 --- /dev/null +++ b/src/Infrastructure/Services/GoogleService.cs @@ -0,0 +1,24 @@ +using Google.Apis.Auth.OAuth2; +using Google.Apis.Services; +using Google.Apis.Oauth2.v2; +using Google.Apis.Oauth2.v2.Data; +using Hutopy.Application.Common.Interfaces; + +namespace Hutopy.Infrastructure.Services; + +public class GoogleService : IGoogleService +{ + public async Task GetUserInfoAsync(string accessToken) + { + var user = GoogleCredential.FromAccessToken(accessToken); + + var service = new Oauth2Service( + new BaseClientService.Initializer + { + HttpClientInitializer = user, + ApplicationName = "Hutopy" + }); + + return await service.Userinfo.Get().ExecuteAsync(); + } +} diff --git a/src/Infrastructure/Services/UserService.cs b/src/Infrastructure/Services/UserService.cs index 98c0c7c..169c5f3 100644 --- a/src/Infrastructure/Services/UserService.cs +++ b/src/Infrastructure/Services/UserService.cs @@ -1,15 +1,13 @@ -using Hutopy.Domain.Interfaces; +using Google.Apis.Oauth2.v2.Data; +using Hutopy.Domain.Interfaces; using Hutopy.Domain.Models; using Hutopy.Infrastructure.Identity; -using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Identity; namespace Hutopy.Infrastructure.Services; public class UserService(UserManager userManager) : IUserService { - private readonly UserManager _userManager = userManager; - public async Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password) { var applicationUser = new ApplicationUser @@ -19,19 +17,36 @@ public class UserService(UserManager userManager) : IUserServic FirstName = firstName, LastName = lastName }; - - //todo: Need to handle errors better for the user. - var response = await _userManager.CreateAsync(applicationUser, password); - - if (response.Errors.Any()) + + var response = await userManager.CreateAsync(applicationUser, password); + + if (!response.Succeeded) { - throw new InvalidOperationException(response.Errors.First().Description); + throw new Exception("Failed to create user"); + } + } + + public async Task CreateUserAsync(Userinfo userInfo) + { + var applicationUser = new ApplicationUser + { + UserName = userInfo.Name, + Email = userInfo.Email, + FirstName = userInfo.GivenName, + LastName = userInfo.FamilyName + }; + + var response = await userManager.CreateAsync(applicationUser, Guid.NewGuid().ToString("N")[..32]); + + if (!response.Succeeded) + { + throw new Exception("Failed to create user"); } } public async Task FindUserByIdAsync(string id) { - var response = await _userManager.FindByIdAsync(id); + var response = await userManager.FindByIdAsync(id); if (response == null) return null; @@ -49,7 +64,7 @@ public class UserService(UserManager userManager) : IUserServic public async Task FindUserByEmailAsync(string email) { - var response = await _userManager.FindByEmailAsync(email); + var response = await userManager.FindByEmailAsync(email); if (response == null) return null; diff --git a/src/Web/Endpoints/Google.cs b/src/Web/Endpoints/Google.cs new file mode 100644 index 0000000..129e71f --- /dev/null +++ b/src/Web/Endpoints/Google.cs @@ -0,0 +1,22 @@ +using Hutopy.Application.Common.Interfaces; +using Hutopy.Application.Google.Commands; +using Hutopy.Domain.Interfaces; + +namespace Hutopy.Web.Endpoints; + +public class Google : EndpointGroupBase +{ + public override void Map(WebApplication app) + { + app.MapGroup(this) + .MapPost(CreateGoogleUser); + } + + public static async Task CreateGoogleUser(ISender sender, CreateGoogleUserCommand command, IUserService userService, IGoogleService googleService) + { + var user = await googleService.GetUserInfoAsync(command.AccessToken) ?? throw new Exception("Failed to get user info from Google"); + Console.WriteLine(user); + await userService.CreateUserAsync(user); + return await sender.Send(command); + } +}