Test: Google oauth
This commit is contained in:
@@ -7,11 +7,12 @@
|
|||||||
<PackageVersion Include="Ardalis.GuardClauses" Version="4.2.0" />
|
<PackageVersion Include="Ardalis.GuardClauses" Version="4.2.0" />
|
||||||
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
|
<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="coverlet.collector" Version="6.0.0" />
|
||||||
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
|
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
|
||||||
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
||||||
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.8.1" />
|
<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="MediatR" Version="12.2.0" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.3" />
|
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.3" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.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="Ardalis.GuardClauses" />
|
||||||
<PackageReference Include="AutoMapper" />
|
<PackageReference Include="AutoMapper" />
|
||||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
|
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
|
||||||
|
<PackageReference Include="Google.Apis.Oauth2.v2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||||
</ItemGroup>
|
</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;
|
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(string userName, string password);
|
||||||
|
|
||||||
|
Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo);
|
||||||
|
|
||||||
Task<Result> DeleteUserAsync(string userId);
|
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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Google.Apis.Oauth2.v2" />
|
||||||
<PackageReference Include="MediatR" />
|
<PackageReference Include="MediatR" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Hutopy.Domain.Models;
|
using Google.Apis.Oauth2.v2.Data;
|
||||||
|
using Hutopy.Domain.Models;
|
||||||
|
|
||||||
namespace Hutopy.Domain.Interfaces;
|
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(string email, string userName, string firstName, string lastName, string password);
|
||||||
|
|
||||||
|
Task CreateUserAsync(Userinfo userInfo);
|
||||||
|
|
||||||
Task<UserModel?> FindUserByIdAsync(string id);
|
Task<UserModel?> FindUserByIdAsync(string id);
|
||||||
|
|
||||||
Task<UserModel?> FindUserByEmailAsync(string id);
|
Task<UserModel?> FindUserByEmailAsync(string id);
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ public class ApplicationUser : IdentityUser
|
|||||||
{
|
{
|
||||||
public string FirstName { get; set; } = string.Empty;
|
public string FirstName { get; set; } = string.Empty;
|
||||||
public string LastName { 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.Interfaces;
|
||||||
using Hutopy.Application.Common.Models;
|
using Hutopy.Application.Common.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@@ -31,6 +32,23 @@ public class IdentityService(
|
|||||||
return (result.ToApplicationResult(), user.Id);
|
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)
|
public async Task<bool> IsInRoleAsync(string userId, string role)
|
||||||
{
|
{
|
||||||
var user = await userManager.FindByIdAsync(userId);
|
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.Domain.Models;
|
||||||
using Hutopy.Infrastructure.Identity;
|
using Hutopy.Infrastructure.Identity;
|
||||||
using Microsoft.AspNetCore.Http.HttpResults;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
namespace Hutopy.Infrastructure.Services;
|
namespace Hutopy.Infrastructure.Services;
|
||||||
|
|
||||||
public class UserService(UserManager<ApplicationUser> userManager) : IUserService
|
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)
|
public async Task CreateUserAsync(string email, string userName, string firstName, string lastName, string password)
|
||||||
{
|
{
|
||||||
var applicationUser = new ApplicationUser
|
var applicationUser = new ApplicationUser
|
||||||
@@ -20,18 +18,35 @@ public class UserService(UserManager<ApplicationUser> userManager) : IUserServic
|
|||||||
LastName = lastName
|
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)
|
public async Task<UserModel?> FindUserByIdAsync(string id)
|
||||||
{
|
{
|
||||||
var response = await _userManager.FindByIdAsync(id);
|
var response = await userManager.FindByIdAsync(id);
|
||||||
|
|
||||||
if (response == null) return null;
|
if (response == null) return null;
|
||||||
|
|
||||||
@@ -49,7 +64,7 @@ public class UserService(UserManager<ApplicationUser> userManager) : IUserServic
|
|||||||
|
|
||||||
public async Task<UserModel?> FindUserByEmailAsync(string email)
|
public async Task<UserModel?> FindUserByEmailAsync(string email)
|
||||||
{
|
{
|
||||||
var response = await _userManager.FindByEmailAsync(email);
|
var response = await userManager.FindByEmailAsync(email);
|
||||||
|
|
||||||
if (response == null) return null;
|
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