using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using GermanApp.Application.DTOs.Auth; using GermanApp.Application.Interfaces; using GermanApp.Domain.Entities; using GermanApp.Infrastructure.Data.DbContext; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; namespace GermanApp.Infrastructure.Services; /// /// Authentication service implementation. /// Part of the Infrastructure layer. /// public class AuthService : IAuthService { private readonly AppDbContext _dbContext; private readonly IPasswordHasher _passwordHasher; private readonly IConfiguration _configuration; public AuthService( AppDbContext dbContext, IPasswordHasher passwordHasher, IConfiguration configuration) { _dbContext = dbContext; _passwordHasher = passwordHasher; _configuration = configuration; } /// /// Registers a new user. /// public async Task RegisterAsync(RegisterDto registerDto) { // Check if username or email already exists if (await _dbContext.Users.AnyAsync(u => u.Username == registerDto.Username)) throw new InvalidOperationException("Username already taken"); if (await _dbContext.Users.AnyAsync(u => u.Email == registerDto.Email)) throw new InvalidOperationException("Email already in use"); // Hash password and create user var user = User.Create(registerDto.Username, registerDto.Email.ToLowerInvariant(), string.Empty); var passwordHash = _passwordHasher.HashPassword(user, registerDto.Password); user.ChangePassword(passwordHash); _dbContext.Users.Add(user); await _dbContext.SaveChangesAsync(); // Generate JWT token var token = GenerateJwtToken(user); return new AuthResponse { UserId = user.Id, Username = user.Username, Email = user.Email, Token = token, ExpiresAt = DateTime.UtcNow.AddHours(24) }; } /// /// Authenticates a user and returns a JWT token. /// public async Task LoginAsync(LoginDto loginDto) { var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Email == loginDto.Email); if (user == null) throw new UnauthorizedAccessException("Invalid email or password"); // Verify password var result = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, loginDto.Password); if (result == PasswordVerificationResult.Failed) throw new UnauthorizedAccessException("Invalid email or password"); // Generate JWT token var token = GenerateJwtToken(user); return new AuthResponse { UserId = user.Id, Username = user.Username, Email = user.Email, Token = token, ExpiresAt = DateTime.UtcNow.AddHours(24) }; } /// /// Gets the current authenticated user. /// public async Task GetCurrentUserAsync(int userId) { return await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == userId); } /// /// Generates a JWT token for the given user. /// private string GenerateJwtToken(User user) { var securityKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(_configuration["Jwt:Key"] ?? "super-secret-key-at-least-32-characters")); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username), new Claim(ClaimTypes.Email, user.Email), new Claim(ClaimTypes.Role, "User") }; var token = new JwtSecurityToken( issuer: _configuration["Jwt:Issuer"] ?? "DeutschLernen", audience: _configuration["Jwt:Audience"] ?? "DeutschLernen", claims: claims, expires: DateTime.UtcNow.AddHours(24), signingCredentials: credentials ); return new JwtSecurityTokenHandler().WriteToken(token); } }