refactor(HomeController): LogInEnvelope aktualisiert, um SMS-Code als TOTP zu verifizieren
This commit is contained in:
@@ -13,7 +13,5 @@
|
||||
/// The placeholder {0} represents the envelopeReceiverId.
|
||||
/// </summary>
|
||||
public string CodeExpirationCacheKeyFormat { get; init; } = "sms-code-expiration-{0}";
|
||||
|
||||
public TimeSpan CodeCacheValidityPeriod { get; init; } = new(0, 5, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using DigitalData.Core.Abstractions.Client;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using OtpNet;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Configurations.GtxMessaging
|
||||
{
|
||||
@@ -21,5 +22,9 @@ namespace EnvelopeGenerator.Application.Configurations.GtxMessaging
|
||||
public string MessageQueryParamName { get; init; } = "text";
|
||||
|
||||
public int CodeLength { get; init; } = 5;
|
||||
|
||||
public int SmsTotpStep { get; init; } = 300;
|
||||
|
||||
public string DefaultTotpMessageFormat { get; init; } = "{0}";
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,7 @@ namespace EnvelopeGenerator.Application.Contracts
|
||||
string GenerateTotp(string secretKey, int step = 30);
|
||||
|
||||
bool VerifyTotp(string totpCode, string secretKey, int step = 30, VerificationWindow? window = null);
|
||||
|
||||
bool GetTotpExpirationTime(int step = 30);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace EnvelopeGenerator.Application.Contracts
|
||||
{
|
||||
public interface IEnvelopeReceiverCache
|
||||
{
|
||||
Task<string?> GetSmsCodeAsync(string envelopeReceiverId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores an SMS verification code in the cache and returns the expiration date of the code.
|
||||
/// </summary>
|
||||
/// <param name="envelopeReceiverId">The unique identifier for the recipient of the envelope to associate with the SMS code.</param>
|
||||
/// <param name="code">The SMS verification code to be stored.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the expiration date and time of the stored SMS code.</returns>
|
||||
Task<DateTime> SetSmsCodeAsync(string envelopeReceiverId, string code);
|
||||
|
||||
Task<DateTime?> GetSmsCodeExpirationAsync(string envelopeReceiverId);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
using EnvelopeGenerator.Application.DTOs.Messaging;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Contracts
|
||||
namespace EnvelopeGenerator.Application.Contracts;
|
||||
|
||||
public interface IMessagingService
|
||||
{
|
||||
public interface IMessagingService
|
||||
{
|
||||
string ServiceProvider { get; }
|
||||
string ServiceProvider { get; }
|
||||
|
||||
Task<SmsResponse> SendSmsAsync(string recipient, string message);
|
||||
Task<SmsResponse> SendSmsAsync(string recipient, string message);
|
||||
|
||||
Task<SmsResponse> SendSmsCodeAsync(string recipient, string envelopeReceiverId);
|
||||
}
|
||||
Task<SmsResponse> SendSmsCodeAsync(string recipient, string secretKey, string messageFormat = "{0}");
|
||||
}
|
||||
@@ -4,16 +4,6 @@
|
||||
{
|
||||
public required bool Ok { get; init; }
|
||||
|
||||
public DateTime? Expiration { get; set; }
|
||||
|
||||
public DateTime? AllowedAt { get; set; }
|
||||
|
||||
public TimeSpan AllowedAfter => Allowed ? TimeSpan.Zero : AllowedAt!.Value - DateTime.Now;
|
||||
|
||||
public bool Allowed => AllowedAt is null || DateTime.Now >= AllowedAt;
|
||||
|
||||
public bool Error => !Ok && Allowed;
|
||||
|
||||
public dynamic? Errors { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -67,5 +67,10 @@ namespace EnvelopeGenerator.Application.Services
|
||||
|
||||
public bool VerifyTotp(string totpCode, string secretKey, int step = 30, VerificationWindow? window = null)
|
||||
=> new Totp(Base32Encoding.ToBytes(secretKey), step).VerifyTotp(totpCode, out _, window);
|
||||
|
||||
public bool GetTotpExpirationTime(int step = 30)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using AngleSharp.Dom;
|
||||
using EnvelopeGenerator.Application.Configurations;
|
||||
using EnvelopeGenerator.Application.Contracts;
|
||||
using EnvelopeGenerator.Application.Extensions;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Services
|
||||
{
|
||||
public class EnvelopeReceiverCache : IEnvelopeReceiverCache
|
||||
{
|
||||
private readonly EnvelopeReceiverCacheParams _cacheParams;
|
||||
|
||||
private readonly DistributedCacheEntryOptions _codeCacheOptions;
|
||||
|
||||
private readonly IDistributedCache _cache;
|
||||
|
||||
public EnvelopeReceiverCache(IOptions<EnvelopeReceiverCacheParams> cacheParamOptions, IDistributedCache cache)
|
||||
{
|
||||
_cacheParams = cacheParamOptions.Value;
|
||||
_codeCacheOptions = new() { AbsoluteExpirationRelativeToNow = cacheParamOptions.Value.CodeCacheValidityPeriod };
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public async Task<string?> GetSmsCodeAsync(string envelopeReceiverId)
|
||||
{
|
||||
var code_key = string.Format(_cacheParams.CodeCacheKeyFormat, envelopeReceiverId);
|
||||
return await _cache.GetStringAsync(code_key);
|
||||
}
|
||||
|
||||
public async Task<DateTime> SetSmsCodeAsync(string envelopeReceiverId, string code)
|
||||
{
|
||||
// set key
|
||||
var code_key = string.Format(_cacheParams.CodeCacheKeyFormat, envelopeReceiverId);
|
||||
await _cache.SetStringAsync(code_key, code, _codeCacheOptions);
|
||||
|
||||
// set expiration
|
||||
var code_expiration_key = string.Format(_cacheParams.CodeExpirationCacheKeyFormat, envelopeReceiverId);
|
||||
var expiration = DateTime.Now + _cacheParams.CodeCacheValidityPeriod;
|
||||
await _cache.SetDateTimeAsync(code_expiration_key, expiration, _codeCacheOptions);
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public async Task<DateTime?> GetSmsCodeExpirationAsync(string envelopeReceiverId)
|
||||
{
|
||||
var code_expiration_key = string.Format(_cacheParams.CodeExpirationCacheKeyFormat, envelopeReceiverId);
|
||||
return await _cache.GetDateTimeAsync(code_expiration_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,62 +6,44 @@ using EnvelopeGenerator.Application.Contracts;
|
||||
using EnvelopeGenerator.Application.DTOs.Messaging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Services
|
||||
namespace EnvelopeGenerator.Application.Services;
|
||||
|
||||
public class GtxMessagingService : IMessagingService
|
||||
{
|
||||
public class GtxMessagingService : IMessagingService
|
||||
private readonly IHttpClientService<SmsParams> _smsClient;
|
||||
|
||||
private readonly SmsParams _smsParams;
|
||||
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
private readonly ICodeGenerator _codeGen;
|
||||
|
||||
public string ServiceProvider { get; }
|
||||
|
||||
public GtxMessagingService(IHttpClientService<SmsParams> smsClient, IOptions<SmsParams> smsParamsOptions, IMapper mapper, ICodeGenerator codeGenerator)
|
||||
{
|
||||
private readonly IHttpClientService<SmsParams> _smsClient;
|
||||
_smsClient = smsClient;
|
||||
_smsParams = smsParamsOptions.Value;
|
||||
_mapper = mapper;
|
||||
ServiceProvider = GetType().Name.Replace("Service", string.Empty);
|
||||
_codeGen = codeGenerator;
|
||||
}
|
||||
|
||||
private readonly SmsParams _smsParams;
|
||||
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
private readonly ICodeGenerator _codeGen;
|
||||
|
||||
private readonly IEnvelopeReceiverCache _erCache;
|
||||
|
||||
public string ServiceProvider { get; }
|
||||
|
||||
public GtxMessagingService(IHttpClientService<SmsParams> smsClient, IOptions<SmsParams> smsParamsOptions, IMapper mapper, ICodeGenerator codeGenerator, IEnvelopeReceiverCache envelopeReceiverCache)
|
||||
public async Task<SmsResponse> SendSmsAsync(string recipient, string message)
|
||||
{
|
||||
return await _smsClient.FetchAsync(queryParams: new Dictionary<string, object?>()
|
||||
{
|
||||
_smsClient = smsClient;
|
||||
_smsParams = smsParamsOptions.Value;
|
||||
_mapper = mapper;
|
||||
ServiceProvider = GetType().Name.Replace("Service", string.Empty);
|
||||
_codeGen = codeGenerator;
|
||||
_erCache = envelopeReceiverCache;
|
||||
}
|
||||
{ _smsParams.RecipientQueryParamName, recipient },
|
||||
{ _smsParams.MessageQueryParamName, message }
|
||||
})
|
||||
.ThenAsync(res => res.Json<GtxMessagingResponse>())
|
||||
.ThenAsync(_mapper.Map<SmsResponse>);
|
||||
}
|
||||
|
||||
public async Task<SmsResponse> SendSmsAsync(string recipient, string message)
|
||||
{
|
||||
return await _smsClient.FetchAsync(queryParams: new Dictionary<string, object?>()
|
||||
{
|
||||
{ _smsParams.RecipientQueryParamName, recipient },
|
||||
{ _smsParams.MessageQueryParamName, message }
|
||||
})
|
||||
.ThenAsync(res => res.Json<GtxMessagingResponse>())
|
||||
.ThenAsync(_mapper.Map<SmsResponse>);
|
||||
}
|
||||
|
||||
public async Task<SmsResponse> SendSmsCodeAsync(string recipient, string envelopeReceiverId)
|
||||
{
|
||||
var code = await _erCache.GetSmsCodeAsync(envelopeReceiverId);
|
||||
|
||||
if (code is null)
|
||||
{
|
||||
code = _codeGen.GenerateCode(_smsParams.CodeLength);
|
||||
var expiration = await _erCache.SetSmsCodeAsync(envelopeReceiverId, code);
|
||||
var res = await SendSmsAsync(recipient: recipient, message: code);
|
||||
res.Expiration = expiration;
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
var code_expiration = await _erCache.GetSmsCodeExpirationAsync(envelopeReceiverId);
|
||||
return code_expiration is null
|
||||
? new() { Ok = false }
|
||||
: new() { Ok = false, AllowedAt = code_expiration };
|
||||
}
|
||||
}
|
||||
public async Task<SmsResponse> SendSmsCodeAsync(string recipient, string secretKey, string? messageFormat = null)
|
||||
{
|
||||
var code = _codeGen.GenerateTotp(secretKey, _smsParams.SmsTotpStep);
|
||||
var message = string.Format(messageFormat ?? _smsParams.DefaultTotpMessageFormat, code);
|
||||
return await SendSmsAsync(recipient: recipient, message: message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user