Test: Google oauth
This commit is contained in:
@@ -7,11 +7,12 @@
|
||||
<PackageVersion Include="Ardalis.GuardClauses" Version="4.2.0" />
|
||||
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.10.4" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.11.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
||||
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.8.1" />
|
||||
<PackageVersion Include="Google.Apis.Oauth2.v2" Version="1.67.0.1869" />
|
||||
<PackageVersion Include="MediatR" Version="12.2.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.3" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<PackageReference Include="Ardalis.GuardClauses" />
|
||||
<PackageReference Include="AutoMapper" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
|
||||
<PackageReference Include="Google.Apis.Oauth2.v2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
8
src/Application/Common/Interfaces/IGoogleService.cs
Normal file
8
src/Application/Common/Interfaces/IGoogleService.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Google.Apis.Oauth2.v2.Data;
|
||||
|
||||
namespace Hutopy.Application.Common.Interfaces;
|
||||
|
||||
public interface IGoogleService
|
||||
{
|
||||
Task<Userinfo?> GetUserInfoAsync(string accessToken);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -12,5 +13,7 @@ public interface IIdentityService
|
||||
|
||||
Task<(Result Result, string UserId)> CreateUserAsync(string userName, string password);
|
||||
|
||||
Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo);
|
||||
|
||||
Task<Result> DeleteUserAsync(string userId);
|
||||
}
|
||||
|
||||
20
src/Application/Google/Commands/CreateGoogleUser.cs
Normal file
20
src/Application/Google/Commands/CreateGoogleUser.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
|
||||
namespace Hutopy.Application.Google.Commands;
|
||||
|
||||
public record CreateGoogleUserCommand : IRequest<Guid>
|
||||
{
|
||||
public required string AccessToken { get; init; }
|
||||
}
|
||||
|
||||
public class CreateGoogleUser(
|
||||
IApplicationDbContext context
|
||||
) : IRequestHandler<CreateGoogleUserCommand, Guid>
|
||||
{
|
||||
public async Task<Guid> Handle(CreateGoogleUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Guid.NewGuid();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Apis.Oauth2.v2" />
|
||||
<PackageReference Include="MediatR" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -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<UserModel?> FindUserByIdAsync(string id);
|
||||
|
||||
Task<UserModel?> FindUserByEmailAsync(string id);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Google.Apis.Oauth2.v2.Data;
|
||||
using Hutopy.Application.Common.Interfaces;
|
||||
using Hutopy.Application.Common.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -31,6 +32,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<bool> IsInRoleAsync(string userId, string role)
|
||||
{
|
||||
var user = await userManager.FindByIdAsync(userId);
|
||||
|
||||
24
src/Infrastructure/Services/GoogleService.cs
Normal file
24
src/Infrastructure/Services/GoogleService.cs
Normal file
@@ -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<Userinfo?> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<ApplicationUser> userManager) : IUserService
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager = userManager;
|
||||
|
||||
public async Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password)
|
||||
{
|
||||
var applicationUser = new ApplicationUser
|
||||
@@ -20,18 +18,35 @@ public class UserService(UserManager<ApplicationUser> userManager) : IUserServic
|
||||
LastName = lastName
|
||||
};
|
||||
|
||||
//todo: Need to handle errors better for the user.
|
||||
var response = await _userManager.CreateAsync(applicationUser, password);
|
||||
var response = await userManager.CreateAsync(applicationUser, password);
|
||||
|
||||
if (response.Errors.Any())
|
||||
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<UserModel?> 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<ApplicationUser> userManager) : IUserServic
|
||||
|
||||
public async Task<UserModel?> FindUserByEmailAsync(string email)
|
||||
{
|
||||
var response = await _userManager.FindByEmailAsync(email);
|
||||
var response = await userManager.FindByEmailAsync(email);
|
||||
|
||||
if (response == null) return null;
|
||||
|
||||
|
||||
22
src/Web/Endpoints/Google.cs
Normal file
22
src/Web/Endpoints/Google.cs
Normal file
@@ -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<Guid> 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user