diff --git a/README.md b/README.md index b762d82..7449907 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # JWT API -This example API shows how to implement JSON Web Token authentication and authorization with ASP.NET 6, built from scratch. +This example API shows how to implement JSON Web Token authentication and authorization with ASP.NET Core 7, built from scratch. ### Features - User registration; diff --git a/src/JWTAPI/JWTAPI/Controllers/LoginController.cs b/src/JWTAPI/JWTAPI/Controllers/LoginController.cs index 26611a2..c1f0280 100644 --- a/src/JWTAPI/JWTAPI/Controllers/LoginController.cs +++ b/src/JWTAPI/JWTAPI/Controllers/LoginController.cs @@ -4,53 +4,43 @@ namespace JWTAPI.Controllers; [Route("api/")] public class AuthController : ControllerBase { - private readonly IMapper _mapper; - private readonly IAuthenticationService _authenticationService; - - public AuthController(IMapper mapper, IAuthenticationService authenticationService) - { - _authenticationService = authenticationService; - _mapper = mapper; - } - - [HttpPost("login")] - public async Task LoginAsync( - [FromBody] UserCredentialsResource userCredentials) - { - var response = await _authenticationService - .CreateAccessTokenAsync(userCredentials.Email, userCredentials.Password); - - if (!response.Success) - { - return BadRequest(response.Message); - } - - var accessTokenResource = _mapper.Map(response.Token); - - return Ok(accessTokenResource); - } - - [HttpPost("token/refresh")] - public async Task RefreshTokenAsync( - [FromBody] RefreshTokenResource refreshTokenResource) - { - var response = await _authenticationService - .RefreshTokenAsync(refreshTokenResource.Token, refreshTokenResource.UserEmail); - - if (!response.Success) - { - return BadRequest(response.Message); - } - - var tokenResource = _mapper.Map(response.Token); - - return Ok(tokenResource); - } - - [HttpPost("token/revoke")] - public IActionResult RevokeToken([FromBody] RevokeTokenResource resource) - { - _authenticationService.RevokeRefreshToken(resource.Token, resource.Email); - return NoContent(); - } + private readonly IMapper _mapper; + private readonly IAuthenticationService _authenticationService; + + public AuthController(IMapper mapper, IAuthenticationService authenticationService) + { + _authenticationService = authenticationService; + _mapper = mapper; + } + + [HttpPost("login")] + public async Task LoginAsync([FromBody] UserCredentialsResource userCredentials) + { + var response = await _authenticationService.CreateAccessTokenAsync(userCredentials.Email!, userCredentials.Password!); + if (!response.Success) + { + return BadRequest(response.Message); + } + + return Ok(_mapper.Map(response.Token)); + } + + [HttpPost("token/refresh")] + public async Task RefreshTokenAsync([FromBody] RefreshTokenResource refreshTokenResource) + { + var response = await _authenticationService.RefreshTokenAsync(refreshTokenResource.Token!, refreshTokenResource.UserEmail!); + if (!response.Success) + { + return BadRequest(response.Message); + } + + return Ok(_mapper.Map(response.Token)); + } + + [HttpPost("token/revoke")] + public IActionResult RevokeToken([FromBody] RevokeTokenResource resource) + { + _authenticationService.RevokeRefreshToken(resource.Token!, resource.Email!); + return NoContent(); + } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Controllers/Resources/RefreshTokenResource.cs b/src/JWTAPI/JWTAPI/Controllers/Resources/RefreshTokenResource.cs index a5c2a29..604dfd2 100644 --- a/src/JWTAPI/JWTAPI/Controllers/Resources/RefreshTokenResource.cs +++ b/src/JWTAPI/JWTAPI/Controllers/Resources/RefreshTokenResource.cs @@ -1,11 +1,12 @@ namespace JWTAPI.Controllers.Resources; -public class RefreshTokenResource + +public record RefreshTokenResource { [Required] - public string Token { get; set; } + public string? Token { get; init; } [Required] [EmailAddress] [StringLength(255)] - public string UserEmail { get; set; } + public string? UserEmail { get; init; } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Controllers/Resources/RevokeTokenResource.cs b/src/JWTAPI/JWTAPI/Controllers/Resources/RevokeTokenResource.cs index 05bfecd..48fc5d6 100644 --- a/src/JWTAPI/JWTAPI/Controllers/Resources/RevokeTokenResource.cs +++ b/src/JWTAPI/JWTAPI/Controllers/Resources/RevokeTokenResource.cs @@ -1,9 +1,10 @@ namespace JWTAPI.Controllers.Resources; + public class RevokeTokenResource { [Required] - public string Token { get; set; } + public string? Token { get; init; } [Required] - public string Email { get; set; } + public string? Email { get; init; } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Controllers/Resources/TokenResource.cs b/src/JWTAPI/JWTAPI/Controllers/Resources/TokenResource.cs index a66a7a7..1c408c0 100644 --- a/src/JWTAPI/JWTAPI/Controllers/Resources/TokenResource.cs +++ b/src/JWTAPI/JWTAPI/Controllers/Resources/TokenResource.cs @@ -1,7 +1,8 @@ namespace JWTAPI.Controllers.Resources; + public class AccessTokenResource { - public string AccessToken { get; set; } - public string RefreshToken { get; set; } - public long Expiration { get; set; } + public string AccessToken { get; init; } = null!; + public string RefreshToken { get; init; } = null!; + public long Expiration { get; set; } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Controllers/Resources/UserCredentialsResource.cs b/src/JWTAPI/JWTAPI/Controllers/Resources/UserCredentialsResource.cs index 5d1133e..dd87a62 100644 --- a/src/JWTAPI/JWTAPI/Controllers/Resources/UserCredentialsResource.cs +++ b/src/JWTAPI/JWTAPI/Controllers/Resources/UserCredentialsResource.cs @@ -1,12 +1,13 @@ namespace JWTAPI.Controllers.Resources; + public class UserCredentialsResource { [Required] [EmailAddress] [StringLength(255)] - public string Email { get; set; } + public string? Email { get; init; } [Required] [StringLength(32)] - public string Password { get; set; } + public string? Password { get; init; } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Controllers/Resources/UserResource.cs b/src/JWTAPI/JWTAPI/Controllers/Resources/UserResource.cs index da32e91..ae6a67d 100644 --- a/src/JWTAPI/JWTAPI/Controllers/Resources/UserResource.cs +++ b/src/JWTAPI/JWTAPI/Controllers/Resources/UserResource.cs @@ -1,7 +1,8 @@ namespace JWTAPI.Controllers.Resources; -public class UserResource + +public record UserResource { public int Id { get; set; } - public string Email { get; set; } - public IEnumerable Roles { get; set; } + public string Email { get; set; } = null!; + public IEnumerable Roles { get; set; } = new List(); } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Controllers/UsersController.cs b/src/JWTAPI/JWTAPI/Controllers/UsersController.cs index d08e155..494df34 100644 --- a/src/JWTAPI/JWTAPI/Controllers/UsersController.cs +++ b/src/JWTAPI/JWTAPI/Controllers/UsersController.cs @@ -4,30 +4,26 @@ namespace JWTAPI.Controllers; [Route("/api/users")] public class UsersController : ControllerBase { - private readonly IMapper _mapper; - private readonly IUserService _userService; + private readonly IMapper _mapper; + private readonly IUserService _userService; - public UsersController(IUserService userService, IMapper mapper) - { - _userService = userService; - _mapper = mapper; - } + public UsersController(IUserService userService, IMapper mapper) + { + _userService = userService; + _mapper = mapper; + } - [HttpPost] - public async Task CreateUserAsync( - [FromBody] UserCredentialsResource userCredentials) - { - var user = _mapper.Map(userCredentials); + [HttpPost] + public async Task CreateUserAsync([FromBody] UserCredentialsResource userCredentials) + { + var user = _mapper.Map(userCredentials); - var response = await _userService.CreateUserAsync(user, ApplicationRole.Common); + var response = await _userService.CreateUserAsync(user, ApplicationRole.Common); + if (!response.Success) + { + return BadRequest(response.Message); + } - if (!response.Success) - { - return BadRequest(response.Message); - } - - var userResource = _mapper.Map(response.User); - - return Ok(userResource); - } + return Ok(_mapper.Map(response.User)); + } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Core/Models/Role.cs b/src/JWTAPI/JWTAPI/Core/Models/Role.cs index 5761761..357b603 100644 --- a/src/JWTAPI/JWTAPI/Core/Models/Role.cs +++ b/src/JWTAPI/JWTAPI/Core/Models/Role.cs @@ -5,7 +5,7 @@ public class Role [Required] [StringLength(50)] - public string Name { get; set; } + public string Name { get; set; } = null!; public virtual ICollection UsersRole { get; set; } = new Collection(); } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Core/Models/User.cs b/src/JWTAPI/JWTAPI/Core/Models/User.cs index 139edaf..78d0f5f 100644 --- a/src/JWTAPI/JWTAPI/Core/Models/User.cs +++ b/src/JWTAPI/JWTAPI/Core/Models/User.cs @@ -6,10 +6,10 @@ public class User [Required] [EmailAddress] [StringLength(255)] - public string Email { get; set; } + public string Email { get; set; } = null!; [Required] - public string Password { get; set; } + public string Password { get; set; } = null!; - public ICollection UserRoles { get; set; } = new Collection(); + public ICollection UserRoles { get; set; } = new Collection(); } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Core/Models/UserRole.cs b/src/JWTAPI/JWTAPI/Core/Models/UserRole.cs index a715050..d85bcda 100644 --- a/src/JWTAPI/JWTAPI/Core/Models/UserRole.cs +++ b/src/JWTAPI/JWTAPI/Core/Models/UserRole.cs @@ -4,8 +4,8 @@ namespace JWTAPI.Core.Models; public class UserRole { public int UserId { get; set; } - public User User { get; set; } + public User? User { get; set; } public int RoleId { get; set; } - public Role Role { get; set; } + public Role? Role { get; set; } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Core/Repositories/IUserRepository.cs b/src/JWTAPI/JWTAPI/Core/Repositories/IUserRepository.cs index 4a429b2..3adac36 100644 --- a/src/JWTAPI/JWTAPI/Core/Repositories/IUserRepository.cs +++ b/src/JWTAPI/JWTAPI/Core/Repositories/IUserRepository.cs @@ -2,5 +2,5 @@ namespace JWTAPI.Core.Repositories; public interface IUserRepository { Task AddAsync(User user, ApplicationRole[] userRoles); - Task FindByEmailAsync(string email); + Task FindByEmailAsync(string email); } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Core/Security/Tokens/AccessToken.cs b/src/JWTAPI/JWTAPI/Core/Security/Tokens/AccessToken.cs index d737ea5..62ec18c 100644 --- a/src/JWTAPI/JWTAPI/Core/Security/Tokens/AccessToken.cs +++ b/src/JWTAPI/JWTAPI/Core/Security/Tokens/AccessToken.cs @@ -6,6 +6,6 @@ public class AccessToken : JsonWebToken public AccessToken(string token, long expiration, RefreshToken refreshToken) : base(token, expiration) { RefreshToken = refreshToken - ?? throw new ArgumentException("Specify a valid refresh token."); + ?? throw new ArgumentNullException("Specify a valid refresh token."); } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Core/Security/Tokens/ITokenHandler.cs b/src/JWTAPI/JWTAPI/Core/Security/Tokens/ITokenHandler.cs index 325fdd8..72fb5fd 100644 --- a/src/JWTAPI/JWTAPI/Core/Security/Tokens/ITokenHandler.cs +++ b/src/JWTAPI/JWTAPI/Core/Security/Tokens/ITokenHandler.cs @@ -3,6 +3,6 @@ namespace JWTAPI.Core.Security.Tokens; public interface ITokenHandler { AccessToken CreateAccessToken(User user); - RefreshToken TakeRefreshToken(string token, string userEmail); + RefreshToken? TakeRefreshToken(string token, string userEmail); void RevokeRefreshToken(string token, string userEmail); } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Core/Security/Tokens/RefreshTokenWithEmail.cs b/src/JWTAPI/JWTAPI/Core/Security/Tokens/RefreshTokenWithEmail.cs index 0a867ae..5654c85 100644 --- a/src/JWTAPI/JWTAPI/Core/Security/Tokens/RefreshTokenWithEmail.cs +++ b/src/JWTAPI/JWTAPI/Core/Security/Tokens/RefreshTokenWithEmail.cs @@ -1,6 +1,6 @@ namespace JWTAPI.Core.Security.Tokens; public class RefreshTokenWithEmail { - public string Email { get; set; } - public RefreshToken RefreshToken { get; set; } + public string Email { get; set; } = null!; + public RefreshToken RefreshToken { get; set; } = null!; } diff --git a/src/JWTAPI/JWTAPI/Core/Services/Communication/BaseResponse.cs b/src/JWTAPI/JWTAPI/Core/Services/Communication/BaseResponse.cs index 54be2e4..fef3036 100644 --- a/src/JWTAPI/JWTAPI/Core/Services/Communication/BaseResponse.cs +++ b/src/JWTAPI/JWTAPI/Core/Services/Communication/BaseResponse.cs @@ -2,9 +2,9 @@ namespace JWTAPI.Core.Services.Communication; public abstract class BaseResponse { public bool Success { get; protected set; } - public string Message { get; protected set; } + public string? Message { get; protected set; } - public BaseResponse(bool success, string message) + public BaseResponse(bool success, string? message) { Success = success; Message = message; diff --git a/src/JWTAPI/JWTAPI/Core/Services/Communication/CreateUserResponse.cs b/src/JWTAPI/JWTAPI/Core/Services/Communication/CreateUserResponse.cs index af4d1b8..164913c 100644 --- a/src/JWTAPI/JWTAPI/Core/Services/Communication/CreateUserResponse.cs +++ b/src/JWTAPI/JWTAPI/Core/Services/Communication/CreateUserResponse.cs @@ -1,9 +1,9 @@ namespace JWTAPI.Core.Services.Communication; public class CreateUserResponse : BaseResponse { - public User User { get; private set; } + public User? User { get; private set; } - public CreateUserResponse(bool success, string message, User user) : base(success, message) + public CreateUserResponse(bool success, string? message, User? user) : base(success, message) { User = user; } diff --git a/src/JWTAPI/JWTAPI/Core/Services/Communication/TokenResponse.cs b/src/JWTAPI/JWTAPI/Core/Services/Communication/TokenResponse.cs index 82c4d10..7041b95 100644 --- a/src/JWTAPI/JWTAPI/Core/Services/Communication/TokenResponse.cs +++ b/src/JWTAPI/JWTAPI/Core/Services/Communication/TokenResponse.cs @@ -1,9 +1,9 @@ namespace JWTAPI.Core.Services.Communication; public class TokenResponse : BaseResponse { - public AccessToken Token { get; set; } + public AccessToken? Token { get; set; } - public TokenResponse(bool success, string message, AccessToken token) : base(success, message) + public TokenResponse(bool success, string? message, AccessToken? token) : base(success, message) { Token = token; } diff --git a/src/JWTAPI/JWTAPI/Core/Services/IUserService.cs b/src/JWTAPI/JWTAPI/Core/Services/IUserService.cs index 2f47f19..0737268 100644 --- a/src/JWTAPI/JWTAPI/Core/Services/IUserService.cs +++ b/src/JWTAPI/JWTAPI/Core/Services/IUserService.cs @@ -3,5 +3,5 @@ namespace JWTAPI.Core.Services; public interface IUserService { Task CreateUserAsync(User user, params ApplicationRole[] userRoles); - Task FindByEmailAsync(string email); + Task FindByEmailAsync(string email); } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Extensions/IdentityServiceExtenstions.cs b/src/JWTAPI/JWTAPI/Extensions/IdentityServiceExtenstions.cs index 3d08d3e..d2067a5 100644 --- a/src/JWTAPI/JWTAPI/Extensions/IdentityServiceExtenstions.cs +++ b/src/JWTAPI/JWTAPI/Extensions/IdentityServiceExtenstions.cs @@ -7,7 +7,7 @@ public static IServiceCollection AddIdentityServices( { services.Configure(configuration.GetSection("TokenOptions")); - var tokenOptions = configuration.GetSection("TokenOptions").Get(); + var tokenOptions = configuration.GetSection("TokenOptions").Get() ?? throw new ArgumentNullException(nameof(TokenOptions)); var signingConfigurations = new SigningConfigurations(tokenOptions.Secret); diff --git a/src/JWTAPI/JWTAPI/Extensions/MiddlewareExtensions.cs b/src/JWTAPI/JWTAPI/Extensions/MiddlewareExtensions.cs index 1d277fb..d97e0f4 100644 --- a/src/JWTAPI/JWTAPI/Extensions/MiddlewareExtensions.cs +++ b/src/JWTAPI/JWTAPI/Extensions/MiddlewareExtensions.cs @@ -10,7 +10,7 @@ public static IServiceCollection AddCustomSwagger(this IServiceCollection servic { Title = "JWT API", Version = "v4", - Description = "Example API that shows how to implement JSON Web Token authentication and authorization with ASP.NET 6, built from scratch.", + Description = "Example API that shows how to implement JSON Web Token authentication and authorization with ASP.NET Core 7, built from scratch.", Contact = new OpenApiContact { Name = "Evandro Gayer Gomes", diff --git a/src/JWTAPI/JWTAPI/JWTAPI.csproj b/src/JWTAPI/JWTAPI/JWTAPI.csproj index 645536b..b03ebe3 100644 --- a/src/JWTAPI/JWTAPI/JWTAPI.csproj +++ b/src/JWTAPI/JWTAPI/JWTAPI.csproj @@ -1,8 +1,12 @@  - net6.0 + net7.0 enable + enable + True + bin\Debug\net7.0\JWTAPI.xml + 1591 @@ -10,17 +14,17 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/JWTAPI/JWTAPI/Mapping/ModelToResourceProfile.cs b/src/JWTAPI/JWTAPI/Mapping/ModelToResourceProfile.cs index 6ac95ad..379121c 100644 --- a/src/JWTAPI/JWTAPI/Mapping/ModelToResourceProfile.cs +++ b/src/JWTAPI/JWTAPI/Mapping/ModelToResourceProfile.cs @@ -4,7 +4,7 @@ public class ModelToResourceProfile : Profile public ModelToResourceProfile() { CreateMap() - .ForMember(u => u.Roles, opt => opt.MapFrom(u => u.UserRoles.Select(ur => ur.Role.Name))); + .ForMember(u => u.Roles, opt => opt.MapFrom(u => u.UserRoles.Select(ur => ur.Role!.Name))); CreateMap() .ForMember(a => a.AccessToken, opt => opt.MapFrom(a => a.Token)) diff --git a/src/JWTAPI/JWTAPI/Persistence/DatabaseSeed.cs b/src/JWTAPI/JWTAPI/Persistence/DatabaseSeed.cs index 8404b1f..e464530 100644 --- a/src/JWTAPI/JWTAPI/Persistence/DatabaseSeed.cs +++ b/src/JWTAPI/JWTAPI/Persistence/DatabaseSeed.cs @@ -30,7 +30,7 @@ public static async Task SeedAsync(AppDbContext context, IPasswordHasher passwor users[0].UserRoles.Add(new UserRole { - RoleId = context.Roles.SingleOrDefault(r => r.Name == ApplicationRole.Administrator.ToString()).Id, + RoleId = context.Roles.Single(r => r.Name == ApplicationRole.Administrator.ToString()).Id, Role = new Role { Name = ApplicationRole.Administrator.ToString() @@ -39,7 +39,7 @@ public static async Task SeedAsync(AppDbContext context, IPasswordHasher passwor users[1].UserRoles.Add(new UserRole { - RoleId = context.Roles.SingleOrDefault(r => r.Name == ApplicationRole.Common.ToString()).Id, + RoleId = context.Roles.Single(r => r.Name == ApplicationRole.Common.ToString()).Id, Role = new Role { Name = ApplicationRole.Common.ToString() diff --git a/src/JWTAPI/JWTAPI/Persistence/UserRepository.cs b/src/JWTAPI/JWTAPI/Persistence/UserRepository.cs index 460b55c..c5f4ef6 100644 --- a/src/JWTAPI/JWTAPI/Persistence/UserRepository.cs +++ b/src/JWTAPI/JWTAPI/Persistence/UserRepository.cs @@ -21,7 +21,7 @@ public async Task AddAsync(User user, ApplicationRole[] userRoles) _context.Users.Add(user); } - public async Task FindByEmailAsync(string email) + public async Task FindByEmailAsync(string email) { return await _context.Users .Include(_ => _.UserRoles) diff --git a/src/JWTAPI/JWTAPI/Program.cs b/src/JWTAPI/JWTAPI/Program.cs index 9145346..cf95558 100644 --- a/src/JWTAPI/JWTAPI/Program.cs +++ b/src/JWTAPI/JWTAPI/Program.cs @@ -18,22 +18,24 @@ app.UseAuthorization(); +#pragma warning disable ASP0014 // Suggest using top level route registrations app.UseEndpoints(endpoints => { - endpoints.MapControllers(); + endpoints.MapControllers(); }); +#pragma warning restore ASP0014 // Suggest using top level route registrations using var scope = app.Services.CreateScope(); try { - var dbContext = scope.ServiceProvider.GetRequiredService(); - var passwordHasher = scope.ServiceProvider.GetRequiredService(); - await DatabaseSeed.SeedAsync(dbContext, passwordHasher); + var dbContext = scope.ServiceProvider.GetRequiredService(); + var passwordHasher = scope.ServiceProvider.GetRequiredService(); + await DatabaseSeed.SeedAsync(dbContext, passwordHasher); } catch (Exception ex) { - var logger = scope.ServiceProvider.GetRequiredService>(); - logger.LogError(ex, "An error occured while applying migrations"); + var logger = scope.ServiceProvider.GetRequiredService>(); + logger.LogError(ex, "An error occured while applying migrations"); } await app.RunAsync(); \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Security/Hashing/PasswordHasher.cs b/src/JWTAPI/JWTAPI/Security/Hashing/PasswordHasher.cs index 68f5d78..ed3eeea 100644 --- a/src/JWTAPI/JWTAPI/Security/Hashing/PasswordHasher.cs +++ b/src/JWTAPI/JWTAPI/Security/Hashing/PasswordHasher.cs @@ -7,70 +7,70 @@ namespace JWTAPI.Security.Hashing; /// public class PasswordHasher : IPasswordHasher { - public string HashPassword(string password) - { - byte[] salt; - byte[] buffer2; - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentNullException(nameof(password)); - } - using (Rfc2898DeriveBytes bytes = new(password, 0x10, 0x3e8)) - { - salt = bytes.Salt; - buffer2 = bytes.GetBytes(0x20); - } - byte[] dst = new byte[0x31]; - Buffer.BlockCopy(salt, 0, dst, 1, 0x10); - Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20); - return Convert.ToBase64String(dst); - } + public string HashPassword(string password) + { + byte[] salt; + byte[] buffer2; + if (string.IsNullOrEmpty(password)) + { + throw new ArgumentNullException(nameof(password)); + } + using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8, HashAlgorithmName.SHA512)) + { + salt = bytes.Salt; + buffer2 = bytes.GetBytes(0x20); + } + byte[] dst = new byte[0x31]; + Buffer.BlockCopy(salt, 0, dst, 1, 0x10); + Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20); + return Convert.ToBase64String(dst); + } - public bool PasswordMatches(string providedPassword, string passwordHash) - { - byte[] buffer4; - if (passwordHash == null) - { - return false; - } - if (providedPassword == null) - { - throw new ArgumentNullException(nameof(providedPassword)); - } - byte[] src = Convert.FromBase64String(passwordHash); - if ((src.Length != 0x31) || (src[0] != 0)) - { - return false; - } - byte[] dst = new byte[0x10]; - Buffer.BlockCopy(src, 1, dst, 0, 0x10); - byte[] buffer3 = new byte[0x20]; - Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20); - using (Rfc2898DeriveBytes bytes = new(providedPassword, dst, 0x3e8)) - { - buffer4 = bytes.GetBytes(0x20); - } - return ByteArraysEqual(buffer3, buffer4); - } + public bool PasswordMatches(string providedPassword, string passwordHash) + { + byte[] buffer4; + if (passwordHash == null) + { + return false; + } + if (providedPassword == null) + { + throw new ArgumentNullException(nameof(providedPassword)); + } + byte[] src = Convert.FromBase64String(passwordHash); + if ((src.Length != 0x31) || (src[0] != 0)) + { + return false; + } + byte[] dst = new byte[0x10]; + Buffer.BlockCopy(src, 1, dst, 0, 0x10); + byte[] buffer3 = new byte[0x20]; + Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20); + using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(providedPassword, dst, 0x3e8, HashAlgorithmName.SHA512)) + { + buffer4 = bytes.GetBytes(0x20); + } + return ByteArraysEqual(buffer3, buffer4); + } - [MethodImpl(MethodImplOptions.NoOptimization)] - private bool ByteArraysEqual(byte[] a, byte[] b) - { - if (ReferenceEquals(a, b)) - { - return true; - } + [MethodImpl(MethodImplOptions.NoOptimization)] + private bool ByteArraysEqual(byte[] a, byte[] b) + { + if (ReferenceEquals(a, b)) + { + return true; + } - if (a == null || b == null || a.Length != b.Length) - { - return false; - } + if (a == null || b == null || a.Length != b.Length) + { + return false; + } - bool areSame = true; - for (int i = 0; i < a.Length; i++) - { - areSame &= (a[i] == b[i]); - } - return areSame; - } + bool areSame = true; + for (int i = 0; i < a.Length; i++) + { + areSame &= (a[i] == b[i]); + } + return areSame; + } } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Security/Tokens/TokenHandler.cs b/src/JWTAPI/JWTAPI/Security/Tokens/TokenHandler.cs index 6f1d48f..e38e751 100644 --- a/src/JWTAPI/JWTAPI/Security/Tokens/TokenHandler.cs +++ b/src/JWTAPI/JWTAPI/Security/Tokens/TokenHandler.cs @@ -32,7 +32,7 @@ public AccessToken CreateAccessToken(User user) return accessToken; } - public RefreshToken TakeRefreshToken(string token, string userEmail) + public RefreshToken? TakeRefreshToken(string token, string userEmail) { if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(userEmail)) { @@ -97,7 +97,7 @@ private IEnumerable GetClaims(User user) foreach (var userRole in user.UserRoles) { - claims.Add(new Claim(ClaimTypes.Role, userRole.Role.Name)); + claims.Add(new Claim(ClaimTypes.Role, userRole.Role!.Name)); } return claims; diff --git a/src/JWTAPI/JWTAPI/Security/Tokens/TokenOptions.cs b/src/JWTAPI/JWTAPI/Security/Tokens/TokenOptions.cs index ec14526..2bdf2ad 100644 --- a/src/JWTAPI/JWTAPI/Security/Tokens/TokenOptions.cs +++ b/src/JWTAPI/JWTAPI/Security/Tokens/TokenOptions.cs @@ -1,9 +1,9 @@ namespace JWTAPI.Security.Tokens; public class TokenOptions { - public string Audience { get; set; } - public string Issuer { get; set; } + public string Audience { get; set; } = null!; + public string Issuer { get; set; } = null!; public long AccessTokenExpiration { get; set; } public long RefreshTokenExpiration { get; set; } - public string Secret { get; set; } + public string Secret { get; set; } = null!; } \ No newline at end of file diff --git a/src/JWTAPI/JWTAPI/Services/UserService.cs b/src/JWTAPI/JWTAPI/Services/UserService.cs index 0e92f5b..d2b7d7c 100644 --- a/src/JWTAPI/JWTAPI/Services/UserService.cs +++ b/src/JWTAPI/JWTAPI/Services/UserService.cs @@ -32,7 +32,7 @@ public async Task CreateUserAsync(User user, params Applicat return new CreateUserResponse(true, null, user); } - public async Task FindByEmailAsync(string email) + public async Task FindByEmailAsync(string email) { return await _userRepository.FindByEmailAsync(email); } diff --git a/src/JWTAPI/JWTAPI/appsettings.Development.json b/src/JWTAPI/JWTAPI/appsettings.Development.json index 7c9d508..35b5eac 100644 --- a/src/JWTAPI/JWTAPI/appsettings.Development.json +++ b/src/JWTAPI/JWTAPI/appsettings.Development.json @@ -7,7 +7,6 @@ "Secret": "very_long_but_insecure_token_here_be_sure_to_use_env_var" }, "Logging": { - "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", diff --git a/tests/JWTAPI.Tests/JWTAPI.Tests.csproj b/tests/JWTAPI.Tests/JWTAPI.Tests.csproj index 00f708c..e2bc764 100644 --- a/tests/JWTAPI.Tests/JWTAPI.Tests.csproj +++ b/tests/JWTAPI.Tests/JWTAPI.Tests.csproj @@ -1,16 +1,16 @@  - net6 + net7.0 false - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive