-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add send register email verification
- Loading branch information
1 parent
f14566e
commit 7d47776
Showing
13 changed files
with
200 additions
and
21 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
Unitagram.Application/Contracts/Identity/IEmailVerificationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
|
||
namespace Unitagram.Application.Contracts.Identity; | ||
|
||
public interface IEmailVerificationService | ||
{ | ||
Task<Result<Unit>> GenerateAsync(Guid userId, string purpose); | ||
Task<Result<bool>> ValidateAsync(Guid userId, string purpose, string token, int maxRetryCount); | ||
} |
17 changes: 17 additions & 0 deletions
17
Unitagram.Application/Contracts/Persistence/IOtpConfirmationRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using Unitagram.Domain; | ||
|
||
namespace Unitagram.Application.Contracts.Persistence; | ||
|
||
public interface IOtpConfirmationRepository : IGenericRepository<OtpConfirmation> | ||
{ | ||
/// <summary> | ||
/// Retrieves an OTP confirmation by the user's unique identifier and name which are both primary keys. | ||
/// </summary> | ||
/// <param name="userId">The unique identifier of the user.</param> | ||
/// <param name="name">The name or identifier associated with the OTP confirmation.</param> | ||
/// <returns> | ||
/// A task representing the asynchronous operation. The task result is an instance of OtpConfirmation | ||
/// if a matching confirmation is found; otherwise, it returns null. | ||
/// </returns> | ||
Task<OtpConfirmation?> GetByUserIdAndName(Guid userId, string name); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
Unitagram.Identity/Services/EmailVerificationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
using System.Security.Cryptography; | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
using Microsoft.AspNetCore.Identity; | ||
using Unitagram.Application.Contracts.Email; | ||
using Unitagram.Application.Contracts.Identity; | ||
using Unitagram.Application.Contracts.Persistence; | ||
using Unitagram.Application.Exceptions; | ||
using Unitagram.Application.Models.Email; | ||
using Unitagram.Domain; | ||
using Unitagram.Identity.Models; | ||
|
||
namespace Unitagram.Identity.Services; | ||
|
||
public class EmailVerificationService : IEmailVerificationService | ||
{ | ||
private readonly IOtpConfirmationRepository _otpConfirmationRepository; | ||
private readonly UserManager<ApplicationUser> _userManager; | ||
private readonly IEmailSender _emailSender; | ||
|
||
public EmailVerificationService( | ||
IOtpConfirmationRepository otpConfirmationRepository, | ||
UserManager<ApplicationUser> userManager, | ||
IEmailSender emailSender) | ||
{ | ||
_otpConfirmationRepository = otpConfirmationRepository; | ||
_userManager = userManager; | ||
_emailSender = emailSender; | ||
} | ||
|
||
public async Task<Result<Unit>> GenerateAsync(Guid userId, string purpose) | ||
{ | ||
var otpConfirmation = await _otpConfirmationRepository.GetByUserIdAndName(userId, purpose); | ||
|
||
if (otpConfirmation is null || IsRetryTimeElapsed(otpConfirmation)) | ||
{ | ||
var token = GenerateRandom6DigitCode(); | ||
|
||
await CreateOrUpdateOtpConfirmation(userId, purpose, token); | ||
|
||
var user = await _userManager.FindByIdAsync(userId.ToString()); | ||
|
||
await _emailSender.SendEmail(ConfirmationEmailTemplate.ToEmailMessage(user!.Email!, token), isBodyHtml: true); | ||
|
||
return Unit.Default; | ||
} | ||
|
||
var minutesDifference = CalculateMinutesDifference(otpConfirmation.RetryDateTimeUtc!.Value); | ||
var exception = new BadRequestException($"Please try again after {minutesDifference} minutes later"); | ||
return new Result<Unit>(exception); | ||
} | ||
|
||
public async Task<Result<bool>> ValidateAsync(Guid userId, string purpose, string token, int maxRetryCount) | ||
{ | ||
// get token using purpose | ||
var otpConfirmation = await _otpConfirmationRepository.GetByUserIdAndName(userId, purpose); | ||
|
||
if (otpConfirmation is null || IsRetryTimeElapsed(otpConfirmation)) | ||
{ | ||
var exception = new BadRequestException("Please create new confirmation code"); | ||
return new Result<bool>(exception); | ||
} | ||
|
||
// check max retry count is passed | ||
if (otpConfirmation.RetryCount > maxRetryCount) | ||
{ | ||
var exception = new BadRequestException("You've exceeded the maximum code usage"); | ||
return new Result<bool>(exception); | ||
} | ||
|
||
// if invalid token then increment maxRetryCount | ||
if (token != otpConfirmation.Value) | ||
{ | ||
otpConfirmation.RetryCount++; | ||
await _otpConfirmationRepository.UpdateAsync(otpConfirmation); | ||
var exception = new BadRequestException("Invalid code"); | ||
return new Result<bool>(exception); | ||
} | ||
|
||
// if code reaches this part everything is ok and email can be confirmed | ||
|
||
// confirm email | ||
var user = await _userManager.FindByIdAsync(userId.ToString()); | ||
user!.EmailConfirmed = true; | ||
await _userManager.UpdateAsync(user); | ||
|
||
// remove otpConfirmation | ||
await _otpConfirmationRepository.DeleteAsync(otpConfirmation); | ||
|
||
return true; | ||
} | ||
|
||
private string GenerateRandom6DigitCode() | ||
{ | ||
using var rng = RandomNumberGenerator.Create(); | ||
var bytes = new byte[4]; // Dört bayt uzunluğunda bir dizi oluşturuyoruz. | ||
rng.GetBytes(bytes); // Rastgele baytları dolduruyoruz. | ||
int code = BitConverter.ToInt32(bytes, 0) % 1000000; // 6 haneli bir sayı üretiyoruz. | ||
if (code < 0) code *= -1; // Negatif bir sayıyı pozitife çeviriyoruz. | ||
return code.ToString("D6"); // Format as a 6-digit string | ||
} | ||
private bool IsRetryTimeElapsed(OtpConfirmation otpConfirmation) | ||
{ | ||
var now = DateTimeOffset.UtcNow; | ||
return (otpConfirmation.RetryDateTimeUtc.HasValue && now >= otpConfirmation.RetryDateTimeUtc.Value); | ||
} | ||
|
||
private double CalculateMinutesDifference(DateTimeOffset retryDateTime) | ||
{ | ||
var now = DateTimeOffset.UtcNow; | ||
var timeDifference = retryDateTime - now; | ||
return timeDifference.TotalMinutes + 1; | ||
} | ||
|
||
private async Task CreateOrUpdateOtpConfirmation(Guid userId, string purpose, string token) | ||
{ | ||
var retryDateTime = DateTimeOffset.Now.AddMinutes(15); | ||
var otpConfirmation = new OtpConfirmation() | ||
{ | ||
UserId = userId, | ||
Name = purpose, | ||
RetryDateTimeUtc = retryDateTime, | ||
RetryCount = 0, | ||
Value = token, | ||
}; | ||
|
||
await _otpConfirmationRepository.CreateAsync(otpConfirmation); | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
...0910144223_AddOtpConfirmation.Designer.cs → ...0910155933_AddOtpConfirmation.Designer.cs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
Unitagram.Persistence/Repositories/OtpConfirmationRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
using Unitagram.Application.Contracts.Persistence; | ||
using Unitagram.Domain; | ||
using Unitagram.Persistence.DatabaseContext; | ||
|
||
namespace Unitagram.Persistence.Repositories; | ||
|
||
public class OtpConfirmationRepository : GenericRepository<OtpConfirmation>, IOtpConfirmationRepository | ||
{ | ||
public OtpConfirmationRepository(UnitagramDatabaseContext context) : base(context) | ||
{ | ||
|
||
} | ||
|
||
public async Task<OtpConfirmation?> GetByUserIdAndName(Guid userId, string name) | ||
{ | ||
var otpConfirmation = await _context.OtpConfirmation | ||
.Where(o => o.UserId == userId && o.Name == name) | ||
.FirstOrDefaultAsync(); | ||
|
||
return otpConfirmation; | ||
} | ||
} |