diff --git a/CleanArchitecture/CleanArchitecture.Application/Constants/CustomClimTypes.cs b/CleanArchitecture/CleanArchitecture.Application/Constants/CustomClimTypes.cs new file mode 100644 index 0000000..935e117 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/Constants/CustomClimTypes.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CleanArchitecture.Application.Constants +{ + public static class CustomClaimTypes + { + public const string Uid = "Uid"; + } +} diff --git a/CleanArchitecture/CleanArchitecture.Application/Models/Identity/AuthResponse.cs b/CleanArchitecture/CleanArchitecture.Application/Models/Identity/AuthResponse.cs index f85e5b6..be8cc81 100644 --- a/CleanArchitecture/CleanArchitecture.Application/Models/Identity/AuthResponse.cs +++ b/CleanArchitecture/CleanArchitecture.Application/Models/Identity/AuthResponse.cs @@ -2,7 +2,7 @@ { public class AuthResponse { - public string Id { get; set; } = string.Empty; + public string UserId { get; set; } = string.Empty; public string Username { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; public string Token { get; set; } = string.Empty; diff --git a/CleanArchitecture/CleanArchitecture.Application/Models/Identity/JwtSettings.cs b/CleanArchitecture/CleanArchitecture.Application/Models/Identity/JwtSettings.cs index 25fa665..fabadfb 100644 --- a/CleanArchitecture/CleanArchitecture.Application/Models/Identity/JwtSettings.cs +++ b/CleanArchitecture/CleanArchitecture.Application/Models/Identity/JwtSettings.cs @@ -6,6 +6,6 @@ namespace CleanArchitecture.Application.Models.Identity public string Key { get; set; } = string.Empty; public string Issuer { get; set; } = string.Empty; public string Audience { get; set; } = string.Empty; - public double DuracionInMinutes { get; set; } + public double DurationInMinutes { get; set; } } } diff --git a/CleanArchitecture/CleanArchitecture.Identity/Services/AuthService.cs b/CleanArchitecture/CleanArchitecture.Identity/Services/AuthService.cs new file mode 100644 index 0000000..34c3e8a --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Identity/Services/AuthService.cs @@ -0,0 +1,119 @@ +using CleanArchitecture.Application.Constants; +using CleanArchitecture.Application.Contracts.Identity; +using CleanArchitecture.Application.Models.Identity; +using CleanArchitecture.Identity.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; + +namespace CleanArchitecture.Identity.Services +{ + public class AuthService : IAuthService + { + private readonly UserManager userManager; + private readonly SignInManager signInManager; + private readonly JwtSettings jwtSettings; + public AuthService(UserManager _userManager, SignInManager _signInManager, JwtSettings _jwtSettings) + { + userManager = _userManager; + signInManager = _signInManager; + jwtSettings = _jwtSettings; + } + + + public async Task Login(AuthRequest request) + { + var user = await userManager.FindByEmailAsync(request.Email); + if (user == null) + { + throw new Exception($"User with email: {request.Email} does not exist"); + } + + var result = await signInManager.CheckPasswordSignInAsync(user, request.Password, false); + if (!result.Succeeded) + { + throw new Exception("Invalid Login Attempt"); + } + var token = await GenerateToken(user); + var authResponse = new AuthResponse + { + UserId = user.Id, + Token = new JwtSecurityTokenHandler().WriteToken(token), + Email = user.Email!, + Username = user.UserName! + }; + return authResponse; + } + + public async Task Register(RegistrationRequest request) + { + var existingUser = await userManager.FindByEmailAsync(request.Email); + if (existingUser != null) + { + throw new Exception($"User with email: {request.Email} already exists"); + } + var existingEmail = await userManager.FindByEmailAsync(request.Email); + if (existingEmail != null) + { + throw new Exception($"Email: {request.Email} is already in use"); + } + var newUser = new ApplicationUser + { + Nombre = request.Nombre, + Apellidos = request.Apellidos, + Email = request.Email, + UserName = request.Username + }; + var result = await userManager.CreateAsync(newUser, request.Password); + if (!result.Succeeded) + { + var errors = string.Empty; + foreach (var error in result.Errors) + { + errors += $"{error.Description}, "; + } + throw new Exception($"User Registration has failed: {errors}"); + } + var token = await GenerateToken(newUser); + var registrationResponse = new RegistrationResponse + { + UserId = newUser.Id, + Email = newUser.Email, + Token = new JwtSecurityTokenHandler().WriteToken(token), + Username = newUser.UserName + }; + return registrationResponse; + } + + private async Task GenerateToken(ApplicationUser user) + { + var userClaims = await userManager.GetClaimsAsync(user); + var roles = await userManager.GetRolesAsync(user); + var roleClaims = roles.Select(role => new Claim(ClaimTypes.Role, role)); + + var claims = new[] + { + new Claim(JwtRegisteredClaimNames.Sub, user.UserName!), + new Claim(JwtRegisteredClaimNames.Email, user.Email!), + new Claim(CustomClaimTypes.Uid, user.Id), + }.Union(userClaims).Union(roleClaims); + + var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key)); + var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256); + + var token = new JwtSecurityToken( + issuer: jwtSettings.Issuer, + audience: jwtSettings.Audience, + claims: claims, + notBefore: DateTime.UtcNow, + expires: DateTime.UtcNow.AddMinutes(jwtSettings.DurationInMinutes), + signingCredentials: signingCredentials); + + return token; + + } + } +}