From 5f8b8f39711e180e280975bc94e0ce12f6356826 Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Tue, 29 Jul 2025 15:40:47 +0200 Subject: [PATCH] resend --- .../Controllers/Auth/AuthController.cs | 56 +++++++- .../Customers/CustomerController.cs | 26 +++- Webshop.Api/Program.cs | 13 +- .../SwaggerFilters/AddExampleSchemaFilter.cs | 8 ++ Webshop.Api/Webshop.Api.csproj | 2 + Webshop.Api/appsettings.Development.json | 6 +- Webshop.Api/appsettings.json | 6 +- .../Services/Auth/AuthService.cs | 123 +++++++++++++----- .../Services/Auth/IAuthService.cs | 19 +-- .../Services/Customers/CustomerService.cs | 66 ++++++++++ .../Customers/Interfaces/ICustomerService.cs | 3 +- .../Webshop.Application.csproj | 1 + 12 files changed, 269 insertions(+), 60 deletions(-) diff --git a/Webshop.Api/Controllers/Auth/AuthController.cs b/Webshop.Api/Controllers/Auth/AuthController.cs index c6e1ff6..4d82fbd 100644 --- a/Webshop.Api/Controllers/Auth/AuthController.cs +++ b/Webshop.Api/Controllers/Auth/AuthController.cs @@ -2,6 +2,8 @@ using Webshop.Application.DTOs.Auth; using Webshop.Application.Services.Auth; using Microsoft.AspNetCore.Authorization; +using System.ComponentModel.DataAnnotations; +using Webshop.Application.Services.Customers; namespace Webshop.Api.Controllers.Auth // Beachten Sie den Namespace { @@ -10,18 +12,33 @@ namespace Webshop.Api.Controllers.Auth // Beachten Sie den Namespace public class AuthController : ControllerBase { private readonly IAuthService _authService; + private readonly ICustomerService _customerService; - public AuthController(IAuthService authService) + public class ResendEmailConfirmationRequestDto { - _authService = authService; + [Required] + [EmailAddress] + public string Email { get; set; } = string.Empty; } - [HttpPost("register")] // /api/v1/auth/register (für Kunden) - [AllowAnonymous] // Jeder darf sich registrieren + public AuthController(IAuthService authService, ICustomerService customerService) + { + _authService = authService; + _customerService = customerService; + } + + + [HttpPost("register")] + [AllowAnonymous] public async Task Register([FromBody] RegisterRequestDto request) { if (!ModelState.IsValid) return BadRequest(ModelState); var result = await _authService.RegisterUserAsync(request); + // Wenn Registrierung erfolgreich, aber E-Mail-Bestätigung aussteht, sollte der Token leer sein + if (result.IsAuthSuccessful && string.IsNullOrEmpty(result.Token)) + { + return Ok(new { Message = result.ErrorMessage, Email = result.Email }); // Sende Status und Info, dass Mail gesendet + } if (!result.IsAuthSuccessful) return BadRequest(new { Message = result.ErrorMessage }); return Ok(result); } @@ -45,5 +62,36 @@ namespace Webshop.Api.Controllers.Auth // Beachten Sie den Namespace if (!result.IsAuthSuccessful) return Unauthorized(new { Message = result.ErrorMessage }); return Ok(result); } + + [HttpGet("confirm-email")] // Für Registrierungsbestätigung + [AllowAnonymous] + public async Task ConfirmEmail([FromQuery] string userId, [FromQuery] string token) + { + var (success, errorMessage) = await _authService.ConfirmEmailAsync(userId, token); + if (!success) return BadRequest(new { Message = errorMessage }); + return Ok(new { Message = "E-Mail-Adresse erfolgreich bestätigt. Sie können sich jetzt anmelden!" }); + } + + [HttpPost("resend-email-confirmation")] + [AllowAnonymous] + public async Task ResendEmailConfirmation([FromBody] ResendEmailConfirmationRequestDto request) + { + if (string.IsNullOrEmpty(request.Email)) return BadRequest(new { Message = "E-Mail ist erforderlich." }); + var (success, errorMessage) = await _authService.ResendEmailConfirmationAsync(request.Email); + if (!success) return BadRequest(new { Message = errorMessage }); + return Ok(new { Message = errorMessage }); + } + + [HttpGet("change-email-confirm")] // Für E-Mail-Änderungsbestätigung + [AllowAnonymous] + public async Task ConfirmEmailChange([FromQuery] string userId, [FromQuery] string newEmail, [FromQuery] string token) + { + var (success, errorMessage) = await _customerService.ConfirmEmailChangeAsync(userId, newEmail, token); // << Jetzt korrekt >> + if (!success) return BadRequest(new { Message = errorMessage }); + return Ok(new { Message = "Ihre E-Mail-Adresse wurde erfolgreich geändert und bestätigt!" }); + } + } + + } \ No newline at end of file diff --git a/Webshop.Api/Controllers/Customers/CustomerController.cs b/Webshop.Api/Controllers/Customers/CustomerController.cs index 6096e92..20b2b71 100644 --- a/Webshop.Api/Controllers/Customers/CustomerController.cs +++ b/Webshop.Api/Controllers/Customers/CustomerController.cs @@ -8,9 +8,20 @@ using Webshop.Application.DTOs.Customers; // UpdateCustomerProfileDto using Webshop.Application.Services; using System.Threading.Tasks; using Webshop.Application.Services.Customers; +using System.ComponentModel.DataAnnotations; namespace Webshop.Api.Controllers.Customer { + public class ChangeEmailRequestDto + { + [Required(ErrorMessage = "Neue E-Mail ist erforderlich.")] + [EmailAddress(ErrorMessage = "Ungültiges E-Mail-Format.")] + public string NewEmail { get; set; } = string.Empty; + + [Required(ErrorMessage = "Aktuelles Passwort ist erforderlich.")] + public string CurrentPassword { get; set; } = string.Empty; + } + [ApiController] [Route("api/v1/customer/[controller]")] // z.B. /api/v1/customer/profile [Authorize(Roles = "Customer")] @@ -66,6 +77,19 @@ namespace Webshop.Api.Controllers.Customer return Ok(new { Message = "Profil erfolgreich aktualisiert." }); } - // << ENTFERNT: UpdateContactInfo Endpoint >> + [HttpPost("change-email-request")] // /api/v1/customer/profile/change-email-request + public async Task ChangeEmailRequest([FromBody] ChangeEmailRequestDto request) // DTO erstellen + { + if (!ModelState.IsValid) return BadRequest(ModelState); + + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); + if (string.IsNullOrEmpty(userId)) return Unauthorized(new { Message = "Benutzer-ID nicht im Token gefunden." }); + + var (success, errorMessage) = await _customerService.ChangeEmailAsync(userId, request.NewEmail, request.CurrentPassword); + + if (!success) return BadRequest(new { Message = errorMessage }); + + return Ok(new { Message = errorMessage }); // Sendet Info, dass Mail gesendet wurde + } } } \ No newline at end of file diff --git a/Webshop.Api/Program.cs b/Webshop.Api/Program.cs index bb76208..a83344c 100644 --- a/Webshop.Api/Program.cs +++ b/Webshop.Api/Program.cs @@ -19,6 +19,7 @@ using Webshop.Application.Services.Public.Interfaces; using Webshop.Application.Services.Customers.Interfaces; using Webshop.Application.Services.Customers; using Webshop.Domain.Identity; +using Resend; var builder = WebApplication.CreateBuilder(args); @@ -45,7 +46,7 @@ builder.Services.Configure(options => options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; options.Password.RequireLowercase = false; - options.SignIn.RequireConfirmedEmail = false; // Für einfache Entwicklung/Tests + options.SignIn.RequireConfirmedEmail = true; // Für einfache Entwicklung/Tests }); @@ -93,6 +94,16 @@ builder.Services.AddScoped(); // CUSTOMER Services (später Implementierungen hinzufügen) builder.Services.AddScoped(); +// --- NEU: Resend E-Mail-Dienst konfigurieren --- +builder.Services.AddOptions(); // Stellt sicher, dass Options konfiguriert werden können +builder.Services.AddHttpClient(); // Fügt HttpClient für Resend hinzu +builder.Services.Configure(options => +{ + // Der API-Token kommt aus den Konfigurationen (z.B. appsettings.json oder Umgebungsvariablen) + options.ApiToken = builder.Configuration["Resend:ApiToken"]!; // << ANPASSEN >> +}); +builder.Services.AddTransient(); + // 5. Controller und Swagger/OpenAPI hinzufügen builder.Services.AddControllers(); diff --git a/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs b/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs index f4be996..eb6f6da 100644 --- a/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs +++ b/Webshop.Api/SwaggerFilters/AddExampleSchemaFilter.cs @@ -15,6 +15,7 @@ using Webshop.Application.DTOs.Payments; using Webshop.Application.DTOs.Orders; using Webshop.Application.DTOs.Discounts; using Webshop.Application.DTOs.Categorys; +using Webshop.Api.Controllers.Auth; namespace Webshop.Api.SwaggerFilters { @@ -70,6 +71,13 @@ namespace Webshop.Api.SwaggerFilters ["emailConfirmed"] = new OpenApiBoolean(true) }; } + else if (type == typeof(AuthController.ResendEmailConfirmationRequestDto)) + { + schema.Example = new OpenApiObject + { + ["email"] = new OpenApiString("me@tzbre.dev") + }; + } // --- Produkte & Lieferanten --- else if (type == typeof(ProductDto)) { diff --git a/Webshop.Api/Webshop.Api.csproj b/Webshop.Api/Webshop.Api.csproj index 702967d..ac123bd 100644 --- a/Webshop.Api/Webshop.Api.csproj +++ b/Webshop.Api/Webshop.Api.csproj @@ -18,6 +18,8 @@ + + diff --git a/Webshop.Api/appsettings.Development.json b/Webshop.Api/appsettings.Development.json index d05194d..5687517 100644 --- a/Webshop.Api/appsettings.Development.json +++ b/Webshop.Api/appsettings.Development.json @@ -14,5 +14,9 @@ "Issuer": "https://dein-webshop.com", "Audience": "webshop-users", "ExpirationMinutes": 120 - } + }, + "Resend": { + "ApiToken": "re_7uRkboan_6M5QGD36TpnADwTwbVLCmAz9" + }, + "App:BaseUrl": "https://shopsolution-backend.tzbre.dev" } \ No newline at end of file diff --git a/Webshop.Api/appsettings.json b/Webshop.Api/appsettings.json index 01d6f9f..2e6ff8a 100644 --- a/Webshop.Api/appsettings.json +++ b/Webshop.Api/appsettings.json @@ -14,5 +14,9 @@ "Issuer": "https://dein-webshop.com", "Audience": "webshop-users", "ExpirationMinutes": 120 - } + }, + "Resend": { + "ApiToken": "re_7uRkboan_6M5QGD36TpnADwTwbVLCmAz9" + }, + "App:BaseUrl": "https://shopsolution-backend.tzbre.dev" } \ No newline at end of file diff --git a/Webshop.Application/Services/Auth/AuthService.cs b/Webshop.Application/Services/Auth/AuthService.cs index b9d898c..9cffe7c 100644 --- a/Webshop.Application/Services/Auth/AuthService.cs +++ b/Webshop.Application/Services/Auth/AuthService.cs @@ -8,31 +8,35 @@ using Webshop.Application.DTOs.Auth; using Webshop.Domain.Entities; using Webshop.Infrastructure.Data; using Webshop.Domain.Identity; +using Resend; +using System.Web; +using System.Net.Mail; namespace Webshop.Application.Services.Auth { public class AuthService : IAuthService { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; + private readonly UserManager _userManager; // << WICHTIG: ApplicationUser >> + private readonly SignInManager _signInManager; // << WICHTIG: ApplicationUser >> private readonly IConfiguration _configuration; private readonly RoleManager _roleManager; - private readonly ApplicationDbContext _context; + private readonly IResend _resend; // << NEU >> public AuthService( - UserManager userManager, - SignInManager signInManager, - IConfiguration configuration, - RoleManager roleManager, - ApplicationDbContext context) + UserManager userManager, + SignInManager signInManager, + IConfiguration configuration, + RoleManager roleManager, + IResend resend) // << NEU: IResend injizieren >> { _userManager = userManager; _signInManager = signInManager; _configuration = configuration; _roleManager = roleManager; - _context = context; + _resend = resend; } + public async Task RegisterUserAsync(RegisterRequestDto request) { var existingUser = await _userManager.FindByEmailAsync(request.Email); @@ -41,12 +45,7 @@ namespace Webshop.Application.Services.Auth return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "E-Mail ist bereits registriert." }; } - var user = new ApplicationUser - { - Email = request.Email, - UserName = request.Email, - CreatedDate = DateTimeOffset.UtcNow - }; + var user = new ApplicationUser { Email = request.Email, UserName = request.Email, CreatedDate = DateTimeOffset.UtcNow }; // << ApplicationUser >> var result = await _userManager.CreateAsync(user, request.Password); if (!result.Succeeded) @@ -55,36 +54,83 @@ namespace Webshop.Application.Services.Auth return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = errors }; } - // Zugehöriges kaufmännisches Kundenprofil erstellen - var customer = new Customer - { - AspNetUserId = user.Id, - FirstName = request.FirstName, - LastName = request.LastName - }; - _context.Customers.Add(customer); - await _context.SaveChangesAsync(); - - // Dem Benutzer die "Customer"-Rolle zuweisen if (!await _roleManager.RoleExistsAsync("Customer")) { await _roleManager.CreateAsync(new IdentityRole("Customer")); } await _userManager.AddToRoleAsync(user, "Customer"); - var roles = await _userManager.GetRolesAsync(user); - var token = await GenerateJwtToken(user, roles); + // << NEU: E-Mail-Bestätigung senden >> + await SendEmailConfirmationEmail(user); + // Initial kein Token, bis E-Mail bestätigt wurde (wenn options.SignIn.RequireConfirmedEmail = true) return new AuthResponseDto { IsAuthSuccessful = true, - Token = token, + ErrorMessage = "Registrierung erfolgreich. Bitte bestätigen Sie Ihre E-Mail-Adresse.", + Token = "", // Kein Token, bis E-Mail bestätigt ist UserId = user.Id, Email = user.Email, - Roles = roles.ToList() + Roles = (await _userManager.GetRolesAsync(user)).ToList() }; } + public async Task<(bool Success, string ErrorMessage)> ConfirmEmailAsync(string userId, string token) + { + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return (false, "Benutzer nicht gefunden."); + } + + var result = await _userManager.ConfirmEmailAsync(user, HttpUtility.UrlDecode(token)); + if (!result.Succeeded) + { + var errors = string.Join(" ", result.Errors.Select(e => e.Description)); + return (false, $"E-Mail-Bestätigung fehlgeschlagen: {errors}"); + } + + return (true, "E-Mail-Adresse erfolgreich bestätigt."); + } + + public async Task<(bool Success, string ErrorMessage)> ResendEmailConfirmationAsync(string email) + { + var user = await _userManager.FindByEmailAsync(email); + if (user == null) + { + return (false, "Benutzer nicht gefunden oder E-Mail existiert nicht."); + } + if (await _userManager.IsEmailConfirmedAsync(user)) + { + return (false, "E-Mail-Adresse ist bereits bestätigt."); + } + + await SendEmailConfirmationEmail(user); + return (true, "Bestätigungs-E-Mail wurde erneut gesendet. Bitte prüfen Sie Ihr Postfach."); + } + + private async Task SendEmailConfirmationEmail(ApplicationUser user) // << WICHTIG: ApplicationUser als Typ >> + { + var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var encodedToken = HttpUtility.UrlEncode(token); + var baseUrl = _configuration["App:BaseUrl"]; // Von appsettings.json oder Umgebungsvariablen + + var confirmationLink = $"{baseUrl}/api/v1/auth/confirm-email?userId={user.Id}&token={encodedToken}"; + + var message = new EmailMessage(); + message.From = "Your Webshop "; // << ANPASSEN: Absender-E-Mail und Domain >> + message.To.Add(user.Email); + message.Subject = "Bestätigen Sie Ihre E-Mail-Adresse für Your Webshop"; + message.HtmlBody = $@" +

Willkommen bei Your Webshop!

+

Bitte klicken Sie auf den folgenden Link, um Ihre E-Mail-Adresse zu bestätigen:

+

{confirmationLink}

+

Vielen Dank!

"; + + await _resend.EmailSendAsync(message); + } + + public async Task LoginUserAsync(LoginRequestDto request) { var user = await _userManager.FindByEmailAsync(request.Email); @@ -93,16 +139,18 @@ namespace Webshop.Application.Services.Auth return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "Ungültige Anmeldeinformationen." }; } + // << NEU: Prüfen, ob E-Mail bestätigt ist >> + if (!await _userManager.IsEmailConfirmedAsync(user)) + { + return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "E-Mail-Adresse wurde noch nicht bestätigt. Bitte prüfen Sie Ihr Postfach." }; + } + var signInResult = await _signInManager.CheckPasswordSignInAsync(user, request.Password, false); if (!signInResult.Succeeded) { return new AuthResponseDto { IsAuthSuccessful = false, ErrorMessage = "Ungültige Anmeldeinformationen." }; } - // Zeitstempel für "Zuletzt aktiv" aktualisieren - user.LastActive = DateTimeOffset.UtcNow; - await _userManager.UpdateAsync(user); - var roles = await _userManager.GetRolesAsync(user); var token = await GenerateJwtToken(user, roles); @@ -133,13 +181,15 @@ namespace Webshop.Application.Services.Auth return authResponse; } - private async Task GenerateJwtToken(ApplicationUser user, IList roles) + private async Task GenerateJwtToken(ApplicationUser user, IList roles) // << WICHTIG: ApplicationUser >> { var claims = new List { new Claim(JwtRegisteredClaimNames.Sub, user.Id), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Email, user.Email!) + // Optional: Fügen Sie Custom Claims von ApplicationUser hinzu + // new Claim("created_date", user.CreatedDate.ToString("o")) }; foreach (var role in roles) @@ -162,5 +212,6 @@ namespace Webshop.Application.Services.Auth return new JwtSecurityTokenHandler().WriteToken(token); } - } + +} } \ No newline at end of file diff --git a/Webshop.Application/Services/Auth/IAuthService.cs b/Webshop.Application/Services/Auth/IAuthService.cs index 9276cff..7e36ef8 100644 --- a/Webshop.Application/Services/Auth/IAuthService.cs +++ b/Webshop.Application/Services/Auth/IAuthService.cs @@ -1,26 +1,15 @@ -using System.Threading.Tasks; +// src/Webshop.Application/Services/Auth/IAuthService.cs +using System.Threading.Tasks; using Webshop.Application.DTOs.Auth; namespace Webshop.Application.Services.Auth { - /// - /// Definiert den Vertrag für den Authentifizierungsdienst. - /// public interface IAuthService { - /// - /// Registriert einen neuen Benutzer und erstellt ein zugehöriges Kundenprofil. - /// Task RegisterUserAsync(RegisterRequestDto request); - - /// - /// Meldet einen Benutzer an und gibt einen JWT zurück. - /// Task LoginUserAsync(LoginRequestDto request); - - /// - /// Meldet einen Benutzer an und überprüft, ob er die Admin-Rolle hat. - /// Task LoginAdminAsync(LoginRequestDto request); + Task<(bool Success, string ErrorMessage)> ConfirmEmailAsync(string userId, string token); + Task<(bool Success, string ErrorMessage)> ResendEmailConfirmationAsync(string email); } } \ No newline at end of file diff --git a/Webshop.Application/Services/Customers/CustomerService.cs b/Webshop.Application/Services/Customers/CustomerService.cs index a478a70..70eb25a 100644 --- a/Webshop.Application/Services/Customers/CustomerService.cs +++ b/Webshop.Application/Services/Customers/CustomerService.cs @@ -10,6 +10,10 @@ using Webshop.Domain.Interfaces; // ICustomerRepository using Webshop.Domain.Identity; // Für ApplicationUser using System.Linq; // Für Select using System.Collections.Generic; // Für IEnumerable +using System.Web; +using Resend; +using Microsoft.Extensions.Configuration; +using System.Net.Mail; namespace Webshop.Application.Services.Customers { @@ -17,11 +21,15 @@ namespace Webshop.Application.Services.Customers { private readonly ICustomerRepository _customerRepository; private readonly UserManager _userManager; + private readonly IConfiguration _configuration; // << NEU: Für BaseUrl >> + private readonly IResend _resend; // << NEU >> public CustomerService(ICustomerRepository customerRepository, UserManager userManager) { _customerRepository = customerRepository; _userManager = userManager; + _configuration = configuration; + _resend = resend; } public async Task GetMyProfileAsync(string userId) @@ -117,5 +125,63 @@ namespace Webshop.Application.Services.Customers return (true, "Profil und Kontaktdaten erfolgreich aktualisiert."); } + + public async Task<(bool Success, string ErrorMessage)> ChangeEmailAsync(string userId, string newEmail, string currentPassword) + { + var user = await _userManager.FindByIdAsync(userId); + if (user == null) return (false, "Benutzer nicht gefunden."); + + if (!await _userManager.CheckPasswordAsync(user, currentPassword)) + { + return (false, "Falsches aktuelles Passwort."); + } + + // Prüfen, ob die neue E-Mail bereits vergeben ist (außer sie ist die aktuelle E-Mail) + if (user.Email != newEmail && await _userManager.FindByEmailAsync(newEmail) != null) + { + return (false, "Die neue E-Mail-Adresse ist bereits registriert."); + } + + var token = await _userManager.GenerateChangeEmailTokenAsync(user, newEmail); + var encodedToken = HttpUtility.UrlEncode(token); + var baseUrl = _configuration["App:BaseUrl"]; + + // Link für E-Mail-Bestätigung der Änderung + var confirmationLink = $"{baseUrl}/api/v1/auth/change-email-confirm?userId={user.Id}&newEmail={HttpUtility.UrlEncode(newEmail)}&token={encodedToken}"; + + var message = new EmailMessage(); + message.From = "Your Webshop "; // << ANPASSEN >> + message.To.Add(newEmail); + message.Subject = "Bestätigen Sie Ihre E-Mail-Änderung für Your Webshop"; + message.HtmlBody = $@" +

E-Mail-Änderung Bestätigung

+

Sie haben eine Änderung Ihrer E-Mail-Adresse beantragt. Bitte klicken Sie auf den folgenden Link, um dies zu bestätigen:

+

{confirmationLink}

+

Wenn Sie diese Änderung nicht angefordert haben, können Sie diese E-Mail ignorieren.

+

Vielen Dank!

"; + + await _resend.EmailSendAsync(message); + + return (true, "Bestätigungs-E-Mail für die E-Mail-Änderung wurde gesendet. Bitte prüfen Sie Ihr Postfach."); + } + + public async Task<(bool Success, string ErrorMessage)> ConfirmEmailChangeAsync(string userId, string newEmail, string token) + { + var user = await _userManager.FindByIdAsync(userId); + if (user == null) return (false, "Benutzer nicht gefunden."); + + var result = await _userManager.ChangeEmailAsync(user, newEmail, HttpUtility.UrlDecode(token)); + if (!result.Succeeded) + { + var errors = string.Join(" ", result.Errors.Select(e => e.Description)); + return (false, $"E-Mail-Änderung konnte nicht bestätigt werden: {errors}"); + } + + // Optional: Bestätigung der Telefonnummer zurücksetzen, wenn E-Mail geändert wurde + // user.PhoneNumberConfirmed = false; + // await _userManager.UpdateAsync(user); + + return (true, "E-Mail-Adresse erfolgreich geändert und bestätigt."); + } } } \ No newline at end of file diff --git a/Webshop.Application/Services/Customers/Interfaces/ICustomerService.cs b/Webshop.Application/Services/Customers/Interfaces/ICustomerService.cs index f8100ee..92e20b9 100644 --- a/Webshop.Application/Services/Customers/Interfaces/ICustomerService.cs +++ b/Webshop.Application/Services/Customers/Interfaces/ICustomerService.cs @@ -11,6 +11,7 @@ namespace Webshop.Application.Services.Customers Task GetMyProfileAsync(string userId); Task<(bool Success, string ErrorMessage)> ChangePasswordAsync(string userId, ChangePasswordRequestDto request); Task<(bool Success, string ErrorMessage)> UpdateMyProfileAsync(string userId, UpdateCustomerDto profileDto); - + Task<(bool Success, string ErrorMessage)> ChangeEmailAsync(string userId, string newEmail, string currentPassword); + Task<(bool Success, string ErrorMessage)> ConfirmEmailChangeAsync(string userId, string newEmail, string token); } } \ No newline at end of file diff --git a/Webshop.Application/Webshop.Application.csproj b/Webshop.Application/Webshop.Application.csproj index 6a8d599..649d1f2 100644 --- a/Webshop.Application/Webshop.Application.csproj +++ b/Webshop.Application/Webshop.Application.csproj @@ -11,6 +11,7 @@ +