Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,32 @@ namespace TickAPI.Tests.Common.Auth.Services;
public class GoogleAuthServiceTests
{
[Fact]
public async Task LoginAsync_WhenTokenValidatorReturnsPayload_ShouldReturnEmailFromPayload()
public async Task GetUserDataFromToken_WhenTokenValidatorReturnsPayload_ShouldReturnEmailFromPayload()
{
var googleTokenValidatorMock = new Mock<IGoogleTokenValidator>();
googleTokenValidatorMock
.Setup(m => m.ValidateAsync("validToken"))
.ReturnsAsync(new GoogleJsonWebSignature.Payload { Email = "example@test.com" });
.ReturnsAsync(new GoogleJsonWebSignature.Payload { Email = "example@test.com", GivenName = "First", FamilyName = "Last"});
var sut = new GoogleAuthService(googleTokenValidatorMock.Object);

var result = await sut.LoginAsync("validToken");
var result = await sut.GetUserDataFromToken("validToken");

Assert.True(result.IsSuccess);
Assert.Equal("example@test.com", result.Value);
Assert.Equal("example@test.com", result.Value?.Email);
Assert.Equal("First", result.Value?.FirstName);
Assert.Equal("Last", result.Value?.LastName);
}

[Fact]
public async Task LoginAsync_WhenTokenValidatorThrowsException_ShouldReturnFailure()
public async Task GetUserDataFromToken_WhenTokenValidatorThrowsException_ShouldReturnFailure()
{
var googleTokenValidatorMock = new Mock<IGoogleTokenValidator>();
googleTokenValidatorMock
.Setup(m => m.ValidateAsync("invalidToken"))
.Throws(new InvalidJwtException("Invalid Google ID token"));
var sut = new GoogleAuthService(googleTokenValidatorMock.Object);

var result = await sut.LoginAsync("invalidToken");
var result = await sut.GetUserDataFromToken("invalidToken");

Assert.True(result.IsError);
Assert.Equal(StatusCodes.Status401Unauthorized, result.StatusCode);
Expand Down
127 changes: 78 additions & 49 deletions TickAPI/TickAPI.Tests/Customers/Controllers/CustomerControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Moq;
using TickAPI.Common.Auth.Abstractions;
using TickAPI.Common.Auth.Enums;
using TickAPI.Common.Auth.Responses;
using TickAPI.Common.Result;
using TickAPI.Customers.Abstractions;
using TickAPI.Customers.Controllers;
Expand All @@ -15,15 +16,16 @@ namespace TickAPI.Tests.Customers.Controllers;
public class CustomerControllerTests
{
[Fact]
public async Task GoogleLogin_WhenAuthSuccessAndCustomerExists_ShouldReturnTokenAndNotNewCustomer()
public async Task GoogleLogin_WhenAuthSuccessAndCustomerExists_ShouldReturnToken()
{
// Arrange
const string email = "existing@test.com";
const string idToken = "valid-google-token";
const string jwtToken = "valid-jwt-token";

var authServiceMock = new Mock<IAuthService>();
authServiceMock.Setup(m => m.LoginAsync(idToken))
.ReturnsAsync(Result<string>.Success(email));
var googleAuthServiceMock = new Mock<IGoogleAuthService>();
googleAuthServiceMock.Setup(m => m.GetUserDataFromToken(idToken))
.ReturnsAsync(Result<GoogleUserData>.Success(new GoogleUserData(email, "First", "Last")));

var customerServiceMock = new Mock<ICustomerService>();
customerServiceMock.Setup(m => m.GetCustomerByEmailAsync(email))
Expand All @@ -34,115 +36,142 @@ public async Task GoogleLogin_WhenAuthSuccessAndCustomerExists_ShouldReturnToken
.Returns(Result<string>.Success(jwtToken));

var sut = new CustomerController(
authServiceMock.Object,
googleAuthServiceMock.Object,
jwtServiceMock.Object,
customerServiceMock.Object);

// Act
var actionResult = await sut.GoogleLogin(new GoogleLoginDto(idToken));

// Assert
Assert.Equal(jwtToken, actionResult.Value?.Token);
Assert.False(actionResult.Value?.IsNewCustomer);
}

[Fact]
public async Task GoogleLogin_WhenAuthSuccessAndCustomerDoesNotExist_ShouldReturnTokenAndNewCustomer()
public async Task GoogleLogin_WhenAuthSuccessAndCustomerDoesNotExist_ShouldCreateCustomerAndReturnToken()
{
// Arrange
const string email = "new@test.com";
const string idToken = "valid-google-token";
const string firstName = "First";
const string lastName = "Last";
const string jwtToken = "valid-jwt-token";

var authServiceMock = new Mock<IAuthService>();
authServiceMock.Setup(m => m.LoginAsync(idToken))
.ReturnsAsync(Result<string>.Success(email));
var googleAuthServiceMock = new Mock<IGoogleAuthService>();
googleAuthServiceMock.Setup(m => m.GetUserDataFromToken(idToken))
.ReturnsAsync(Result<GoogleUserData>.Success(new GoogleUserData(email, "First", "Last")));

var customerServiceMock = new Mock<ICustomerService>();
customerServiceMock.Setup(m => m.GetCustomerByEmailAsync(email))
.ReturnsAsync(Result<Customer>.Failure(StatusCodes.Status404NotFound, $"customer with email '{email}' not found"));
customerServiceMock.Setup(m => m.CreateNewCustomerAsync(email, firstName, lastName))
.ReturnsAsync(Result<Customer>.Success(new Customer
{
Id = Guid.NewGuid(),
Email = email,
FirstName = firstName,
LastName = lastName
}));

var jwtServiceMock = new Mock<IJwtService>();
jwtServiceMock.Setup(m => m.GenerateJwtToken(email, UserRole.NewCustomer))
jwtServiceMock.Setup(m => m.GenerateJwtToken(email, UserRole.Customer))
.Returns(Result<string>.Success(jwtToken));

var sut = new CustomerController(
authServiceMock.Object,
googleAuthServiceMock.Object,
jwtServiceMock.Object,
customerServiceMock.Object);

var result = await sut.GoogleLogin(new GoogleLoginDto(idToken));
// Act
var result = await sut.GoogleLogin(new GoogleLoginDto( idToken ));

// Assert
Assert.Equal(jwtToken, result.Value?.Token);
Assert.True(result.Value?.IsNewCustomer);
}

[Fact]
public async Task GoogleCreateNewAccount_WhenCreatingAccountIsSuccessful_ShouldReturnToken()
public async Task AboutMe_WithValidEmailClaim_ShouldReturnCustomerDetails()
{
const string email = "new@test.com";
const string firstName = "First";
const string lastName = "Last";
const string jwtToken = "valid-jwt-token";
// Arrange
const string email = "test@example.com";
const string firstName = "John";
const string lastName = "Doe";
var creationDate = new DateTime(1970, 1, 1, 8, 0, 0, DateTimeKind.Utc);

var customer = new Customer
{
Email = email,
FirstName = firstName,
LastName = lastName,
CreationDate = creationDate
};

var authServiceMock = new Mock<IAuthService>();
var customerServiceMock = new Mock<ICustomerService>();
customerServiceMock.Setup(m => m.CreateNewCustomerAsync(email, firstName, lastName))
.ReturnsAsync(Result<Customer>.Success(new Customer
{
Id = Guid.NewGuid(),
Email = email,
FirstName = firstName,
LastName = lastName
}));
customerServiceMock.Setup(m => m.GetCustomerByEmailAsync(email))
.ReturnsAsync(Result<Customer>.Success(customer));

var googleAuthServiceMock = new Mock<IGoogleAuthService>();
var jwtServiceMock = new Mock<IJwtService>();
jwtServiceMock.Setup(m => m.GenerateJwtToken(email, UserRole.Customer))
.Returns(Result<string>.Success(jwtToken));

var sut = new CustomerController(
authServiceMock.Object,
jwtServiceMock.Object,
googleAuthServiceMock.Object,
jwtServiceMock.Object,
customerServiceMock.Object);

var claims = new List<Claim>
{
new Claim(ClaimTypes.Email, email)
};
var identity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(identity);

sut.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(new ClaimsIdentity(claims))
User = claimsPrincipal
}
};

var result = await sut.GoogleCreateNewAccount(
new GoogleCreateNewAccountDto( firstName, lastName ));
// Act
var result = await sut.AboutMe();

Assert.Equal(jwtToken, result.Value?.Token);
// Assert
Assert.Equal(email, result.Value?.Email);
Assert.Equal(firstName, result.Value?.FirstName);
Assert.Equal(lastName, result.Value?.LastName);
Assert.Equal(creationDate, result.Value?.CreationDate);
}

[Fact]
public async Task GoogleCreateNewAccount_WhenEmailClaimIsMissing_ShouldReturnBadRequest()
public async Task AboutMe_WithMissingEmailClaim_ShouldReturnBadRequest()
{
var authServiceMock = new Mock<IAuthService>();
var jwtServiceMock = new Mock<IJwtService>();
// Arrange
var customerServiceMock = new Mock<ICustomerService>();

var googleAuthServiceMock = new Mock<IGoogleAuthService>();
var jwtServiceMock = new Mock<IJwtService>();

var sut = new CustomerController(
authServiceMock.Object,
jwtServiceMock.Object,
googleAuthServiceMock.Object,
jwtServiceMock.Object,
customerServiceMock.Object);


var claims = new List<Claim>();
var identity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(identity);

sut.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>()))
User = claimsPrincipal
}
};

var result = await sut.GoogleCreateNewAccount(
new GoogleCreateNewAccountDto("First","Last"));


// Act
var result = await sut.AboutMe();

// Assert
var objectResult = Assert.IsType<ObjectResult>(result.Result);
Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode);
Assert.Equal("missing email claim", objectResult.Value);
Expand Down
8 changes: 0 additions & 8 deletions TickAPI/TickAPI/Common/Auth/Abstractions/IAuthService.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using TickAPI.Common.Auth.Responses;
using TickAPI.Common.Result;

namespace TickAPI.Common.Auth.Abstractions;

public interface IGoogleAuthService
{
Task<Result<GoogleUserData>> GetUserDataFromToken(string token);
}
14 changes: 7 additions & 7 deletions TickAPI/TickAPI/Common/Auth/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ namespace TickAPI.Common.Auth.Controllers;
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly IAuthService _authService;
private readonly IGoogleAuthService _googleAuthService;
private readonly IJwtService _jwtService;

public AuthController(IAuthService authService, IJwtService jwtService)
public AuthController(IGoogleAuthService googleAuthService, IJwtService jwtService)
{
_authService = authService;
_googleAuthService = googleAuthService;
_jwtService = jwtService;
}

Expand All @@ -22,12 +22,12 @@ public AuthController(IAuthService authService, IJwtService jwtService)
[HttpPost("google-login")]
public async Task<IActionResult> GoogleLogin([FromBody] GoogleLoginRequest request)
{
var loginResult = await _authService.LoginAsync(request.IdToken);
var userDataResult = await _googleAuthService.GetUserDataFromToken(request.IdToken);

if(loginResult.IsError)
return StatusCode(loginResult.StatusCode, loginResult.ErrorMsg);
if(userDataResult.IsError)
return StatusCode(userDataResult.StatusCode, userDataResult.ErrorMsg);

var jwtTokenResult = _jwtService.GenerateJwtToken(loginResult.Value, UserRole.Customer);
var jwtTokenResult = _jwtService.GenerateJwtToken(userDataResult.Value?.Email, UserRole.Customer);

if(jwtTokenResult.IsError)
return StatusCode(jwtTokenResult.StatusCode, jwtTokenResult.ErrorMsg);
Expand Down
1 change: 0 additions & 1 deletion TickAPI/TickAPI/Common/Auth/Enums/AuthPolicies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ public enum AuthPolicies
OrganizerPolicy,
CustomerPolicy,
NewOrganizerPolicy,
NewCustomerPolicy,
}
1 change: 0 additions & 1 deletion TickAPI/TickAPI/Common/Auth/Enums/UserRole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ public enum UserRole
Organizer,
Customer,
NewOrganizer,
NewCustomer,
}
7 changes: 7 additions & 0 deletions TickAPI/TickAPI/Common/Auth/Responses/GoogleUserData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TickAPI.Common.Auth.Responses;

public record GoogleUserData(
string Email,
string FirstName,
string LastName
);
13 changes: 7 additions & 6 deletions TickAPI/TickAPI/Common/Auth/Services/GoogleAuthService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Google.Apis.Auth;
using TickAPI.Common.Auth.Abstractions;
using TickAPI.Common.Auth.Abstractions;
using TickAPI.Common.Auth.Responses;
using TickAPI.Common.Result;

namespace TickAPI.Common.Auth.Services;

public class GoogleAuthService : IAuthService
public class GoogleAuthService : IGoogleAuthService
{
private readonly IGoogleTokenValidator _googleTokenValidator;

Expand All @@ -13,16 +13,17 @@ public GoogleAuthService(IGoogleTokenValidator googleTokenValidator)
_googleTokenValidator = googleTokenValidator;
}

public async Task<Result<string>> LoginAsync(string idToken)
public async Task<Result<GoogleUserData>> GetUserDataFromToken(string idToken)
{
try
{
var payload = await _googleTokenValidator.ValidateAsync(idToken);
return Result<string>.Success(payload.Email);
var userData = new GoogleUserData(payload.Email, payload.GivenName, payload.FamilyName);
return Result<GoogleUserData>.Success(userData);
}
catch (Exception)
{
return Result<string>.Failure(StatusCodes.Status401Unauthorized, "Invalid Google ID token");
return Result<GoogleUserData>.Failure(StatusCodes.Status401Unauthorized, "Invalid Google ID token");
}
}
}
2 changes: 2 additions & 0 deletions TickAPI/TickAPI/Common/Auth/Services/GoogleTokenValidator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Google.Apis.Auth;
using TickAPI.Common.Auth.Abstractions;

namespace TickAPI.Common.Auth.Services;

public class GoogleTokenValidator : IGoogleTokenValidator
{
private readonly IConfiguration _configuration;
Expand Down
Loading
Loading