diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index 288e791..2f8b722 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -48,6 +48,8 @@ public class Translations (TranslationService translationService) public string MinStorageSpaceFormat => translationService.Translate(nameof(MinStorageSpaceFormat)); public string MaxStorageSpaceFormat => translationService.Translate(nameof(MaxStorageSpaceFormat)); public string MinMaxStorageSpaceFormat => translationService.Translate(nameof(MinMaxStorageSpaceFormat)); + public string UserArgumentsNull => translationService.Translate(nameof(UserArgumentsNull)); + public string InvalidToken => translationService.Translate(nameof(InvalidToken)); } public static class ResourcesKey @@ -104,4 +106,6 @@ public static class ResourcesKey public static string MinStorageSpaceFormat => _instance?.MinStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MinStorageSpaceFormat is not initialized."); public static string MaxStorageSpaceFormat => _instance?.MaxStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MaxStorageSpaceFormat is not initialized."); public static string MinMaxStorageSpaceFormat => _instance?.MinMaxStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MinMaxStorageSpaceFormat is not initialized."); + public static string UserArgumentsNull => _instance?.UserArgumentsNull ?? throw new InvalidOperationException("ResourcesKey.UserArgumentsNull is not initialized."); + public static string InvalidToken => _instance?.InvalidToken ?? throw new InvalidOperationException("ResourcesKey.InvalidToken is not initialized."); } \ No newline at end of file diff --git a/src/GameIdeas/GameIdeas.Shared/Constants/GlobalConstants.cs b/src/GameIdeas/GameIdeas.Shared/Constants/GlobalConstants.cs index 464a750..7989c69 100644 --- a/src/GameIdeas/GameIdeas.Shared/Constants/GlobalConstants.cs +++ b/src/GameIdeas/GameIdeas.Shared/Constants/GlobalConstants.cs @@ -9,6 +9,8 @@ public class GlobalConstants public readonly static string ADMINISTRATOR = "Administrateur"; public readonly static string MEMBER = "Membre"; + public readonly static int JWT_DURATION_HOUR = 12; + public readonly static int NUMBER_PER_PAGE = 50; } \ No newline at end of file diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/LoginDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/LoginDto.cs deleted file mode 100644 index 33c81a7..0000000 --- a/src/GameIdeas/GameIdeas.Shared/Dto/LoginDto.cs +++ /dev/null @@ -1,11 +0,0 @@ -using GameIdeas.Shared.Enum; - -namespace GameIdeas.Shared.Dto; - -public class LoginDto -{ - public int? Id { get; set; } - public string? Username { get; set; } - public string? Password { get; set; } - public Role? Role { get; set; } -} diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/TokenDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/TokenDto.cs new file mode 100644 index 0000000..f571aa5 --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Dto/TokenDto.cs @@ -0,0 +1,7 @@ +namespace GameIdeas.Shared.Dto; + +public class TokenDto +{ + public string? Token { get; set; } + public DateTime? Expiration { get; set; } +} diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs index d98d15d..11d6da0 100644 --- a/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs +++ b/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs @@ -7,5 +7,5 @@ public class UserDto public int? Id { get; set; } public string? Username { get; set; } public string? Password { get; set; } - public Role? Role { get; set; } + public string? RoleId { get; set; } } diff --git a/src/GameIdeas/GameIdeas.Shared/Enum/Role.cs b/src/GameIdeas/GameIdeas.Shared/Enum/Role.cs deleted file mode 100644 index 25ed04c..0000000 --- a/src/GameIdeas/GameIdeas.Shared/Enum/Role.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace GameIdeas.Shared.Enum; - -public enum Role -{ - Guest = 1, - Member = 2, - Administrator = 3 -} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs new file mode 100644 index 0000000..23049f8 --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs @@ -0,0 +1,54 @@ +using GameIdeas.Resources; +using GameIdeas.Shared.Constants; +using GameIdeas.Shared.Dto; +using GameIdeas.Shared.Model; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; + +namespace GameIdeas.WebAPI.Controllers; + +public class UserController(UserManager userManager) : Controller +{ + [HttpPost("login")] + public async Task> Login([FromBody] UserDto model) + { + if (model.Username == null || model.Password == null) + throw new ArgumentNullException(paramName: nameof(model), ResourcesKey.UserArgumentsNull); + + var user = await userManager.FindByNameAsync(model.Username); + + if (user != null && await userManager.CheckPasswordAsync(user, model.Password)) + { + List authClaims = + [ + new Claim(ClaimTypes.Name, user.UserName ?? string.Empty), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + ]; + + var jwtKey = Environment.GetEnvironmentVariable("JWT_KEY") + ?? throw new ArgumentNullException(message: ResourcesKey.InvalidToken, null); + + var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)); + + var token = new JwtSecurityToken( + issuer: Environment.GetEnvironmentVariable("JWT_ISSUER"), + audience: Environment.GetEnvironmentVariable("JWT_AUDIENCE"), + expires: DateTime.Now.AddHours(GlobalConstants.JWT_DURATION_HOUR), + claims: authClaims, + signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256) + ); + + return Ok(new TokenDto + { + Token = new JwtSecurityTokenHandler().WriteToken(token), + Expiration = token.ValidTo + }); + } + + return Unauthorized(); + } +} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json index c0bffdf..e2f9a4e 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -43,5 +43,7 @@ "ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage", "MinStorageSpaceFormat": "Jusqu'à {0}", "MaxStorageSpaceFormat": "Plus de {0}", - "MinMaxStorageSpaceFormat": "{0} à {1}" + "MinMaxStorageSpaceFormat": "{0} à {1}", + "UserArgumentsNull": "Nom d'utilisateur ou mot de passe invalide", + "InvalidToken": "Le token JWT est invalide" } \ No newline at end of file