Skip to content

Commit

Permalink
Merge pull request #100 from thiagoloureiro/development
Browse files Browse the repository at this point in the history
Hybrid Login + Refactoring
  • Loading branch information
thiagoloureiro committed Jul 1, 2024
2 parents c6c1d27 + 39b4511 commit 061c5eb
Show file tree
Hide file tree
Showing 48 changed files with 830 additions and 387 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace AlertHawk.Application.Interfaces;

public interface IJwtTokenService
{
string GenerateToken(UserDto user);
string GenerateToken(UserDto? user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ public interface IUserService
Task<UserDto?> GetByUsername(string username);
Task<IEnumerable<UserDto>?> GetAll();

Task<UserDto?> GetUserByToken(string? jwtToken);
Task UpdateUserToken(string token, string username);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace AlertHawk.Application.Services;

public class GetOrCreateUserService(IUserService userService) : IGetOrCreateUserService
{

public async Task<UserDto> GetUserOrCreateUser(ClaimsPrincipal claims)
{
string? userEmail = "";
Expand All @@ -28,6 +27,11 @@ public async Task<UserDto> GetUserOrCreateUser(ClaimsPrincipal claims)
userEmail = claims.Claims?.FirstOrDefault(c => c.Type == "preferred_username")?.Value;
}

if (string.IsNullOrWhiteSpace(userEmail))
{
userEmail = claims.Claims?.FirstOrDefault(c => c.Type == "emailaddress")?.Value;
}

var user = await userService.GetByEmail(userEmail);

// This is for AD First Login only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,33 @@ namespace AlertHawk.Application.Services;
public class JwtTokenService : IJwtTokenService
{
private readonly string? _secret;
private readonly string? _issuer;
private readonly string? _audience;
private readonly string? _issuers;
private readonly string? _audiences;

public JwtTokenService(IConfiguration configuration)
{
_secret = configuration["Jwt:Key"];
_issuer = configuration["Jwt:Issuer"];
_audience = configuration["Jwt:Audience"];
_issuers = configuration["Jwt:Issuers"];
_audiences = configuration["Jwt:Audiences"];
}

public string GenerateToken(UserDto user)
public string GenerateToken(UserDto? user)
{
var claims = new[]
{
new Claim("id", user.Id.ToString()),
new Claim("username", user.Username),
new Claim("givenname", user.Username),
new Claim("surname", user.Username),
new Claim("emailaddress", user.Email),
new Claim("isAdmin", user.IsAdmin.ToString())
};

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret ?? throw new InvalidOperationException("Secret key is undefined.")));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var token = new JwtSecurityToken(
_issuer,
_audience,
_issuers?.Split(",")[0],
_audiences?.Split(",")[0],
claims,
expires: DateTime.UtcNow.AddHours(1),
signingCredentials: credentials);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,14 @@ public async Task ResetPassword(string username)
{
return _userRepository.GetAll();
}

public async Task<UserDto?> GetUserByToken(string? jwtToken)
{
return await _userRepository.GetUserByToken(jwtToken);
}

public async Task UpdateUserToken(string token, string username)
{
await _userRepository.UpdateUserToken(token, username);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public class User
public required string Password { get; set; }
public required string Salt { get; set; }
public bool IsAdmin { get; set; }
public string? Token { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ public interface IUserRepository
Task<UserDto?> GetByUsername(string username);
Task<IEnumerable<UserDto>?> GetAll();
Task Delete(Guid id);
Task<UserDto?> GetUserByToken(string? jwtToken);
Task UpdateUserToken(string token, string username);
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ public async Task Delete(Guid id)
await ExecuteNonQueryAsync(sql, new { Id = id });
}

public async Task<UserDto?> GetUserByToken(string? jwtToken)
{
const string sql = "SELECT Username, Email, IsAdmin FROM Users WHERE Token = @jwtToken";
var user = await ExecuteQueryFirstOrDefaultAsync<User>(sql, new { jwtToken });
return _mapper.Map<UserDto>(user);
}

public async Task UpdateUserToken(string token, string username)
{
const string sql = "UPDATE Users SET Token = @token WHERE LOWER(Username) = @username";
await ExecuteNonQueryAsync(sql, new { token, username });
}

public async Task Create(UserCreation userCreation)
{
string checkExistingUserSql = "SELECT Id FROM Users WHERE LOWER(Email) = @Email";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace AlertHawk.Authentication.Infrastructure.Utils;

public static class TokenUtils
{
public static string? GetJwtToken(string? token)
{
if (token == null)
{
return null;
}

string[] tokenParts = token.Split(' ');
if (tokenParts.Length != 2 || !tokenParts[0].Equals("Bearer", StringComparison.OrdinalIgnoreCase))
{
return null;
}

string jwtToken = tokenParts[1];
return jwtToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ public class AuthControllerTests
{
private readonly Mock<IUserService> _mockUserService;
private readonly Mock<IJwtTokenService> _mockJwtTokenService;
private readonly Mock<IGetOrCreateUserService> _mockGetOrCreateUserService;
private readonly AuthController _controller;

public AuthControllerTests()
{
_mockGetOrCreateUserService = new Mock<IGetOrCreateUserService>();
_mockUserService = new Mock<IUserService>();
_mockJwtTokenService = new Mock<IJwtTokenService>();
_controller = new AuthController(_mockUserService.Object, _mockJwtTokenService.Object);

_controller = new AuthController(_mockUserService.Object, _mockJwtTokenService.Object, _mockGetOrCreateUserService.Object);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using AlertHawk.Application.Interfaces;
using AlertHawk.Authentication.Domain.Custom;
using AlertHawk.Authentication.Domain.Dto;
using AlertHawk.Authentication.Domain.Entities;
using AlertHawk.Authentication.Infrastructure.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Sentry;
using Swashbuckle.AspNetCore.Annotations;
Expand All @@ -13,11 +16,42 @@ public class AuthController : ControllerBase
{
private readonly IUserService _userService;
private readonly IJwtTokenService _jwtTokenService;
private readonly IGetOrCreateUserService _getOrCreateUserService;

public AuthController(IUserService userService, IJwtTokenService jwtTokenService)
public AuthController(IUserService userService, IJwtTokenService jwtTokenService,
IGetOrCreateUserService getOrCreateUserService)
{
_userService = userService;
_jwtTokenService = jwtTokenService;
_getOrCreateUserService = getOrCreateUserService;
}

[HttpPost("refreshToken")]
[SwaggerOperation(Summary = "Refresh User Token")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Message), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> RefreshUserToken()
{
try
{
var jwtToken = TokenUtils.GetJwtToken(Request.Headers["Authorization"].ToString());
var user = await _userService.GetUserByToken(jwtToken);

if (user is null)
{
return BadRequest(new Message("Invalid token."));
}

var token = _jwtTokenService.GenerateToken(user);
await _userService.UpdateUserToken(token, user.Username.ToLower());

return Ok(new { token });
}
catch (Exception err)
{
SentrySdk.CaptureException(err);
return StatusCode(StatusCodes.Status500InternalServerError, new Message("Something went wrong."));
}
}

[HttpPost("login")]
Expand All @@ -37,6 +71,8 @@ public async Task<IActionResult> PostUserAuth([FromBody] UserAuth userAuth)

var token = _jwtTokenService.GenerateToken(user);

await _userService.UpdateUserToken(token, user.Username.ToLower());

return Ok(new { token });
}
catch (Exception err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace AlertHawk.Authentication.Controllers;

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UserController : Controller
Expand All @@ -20,14 +21,15 @@ public UserController(IUserService userService, IGetOrCreateUserService getOrCre
_userService = userService;
_getOrCreateUserService = getOrCreateUserService;
}


[AllowAnonymous]
[HttpPost("create")]
[SwaggerOperation(Summary = "Create User")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> PostUserCreation([FromBody] UserCreation userCreation)
{
await IsUserAdmin();
//await IsUserAdmin();

if (!ModelState.IsValid)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
using AlertHawk.Authentication.Domain.Custom;
using AlertHawk.Authentication.Domain.Dto;
using AlertHawk.Authentication.Domain.Entities;
using AlertHawk.Authentication.Helpers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Sentry;
using Swashbuckle.AspNetCore.Annotations;

namespace AlertHawk.Authentication.Controllers
{
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersMonitorGroupController : Controller
Expand Down
Loading

0 comments on commit 061c5eb

Please sign in to comment.