Compare commits

..
7 changed files with 67 additions and 17 deletions
+13
View File
@@ -122,6 +122,19 @@ namespace Notesnook.API.Hubs
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
if (exception != null)
{
Logger.LogWarning(exception, "Connection {ConnectionId} disconnected with error (server-side drop)", Context.ConnectionId);
}
else
{
Logger.LogInformation("Connection {ConnectionId} disconnected cleanly (client-initiated)", Context.ConnectionId);
}
await base.OnDisconnectedAsync(exception);
}
public async Task<int> PushItems(string deviceId, SyncTransferItemV2 pushItem)
{
+1
View File
@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.3.0" />
<PackageReference Include="AspNetCore.HealthChecks.Aws.S3" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="9.0.0" />
<PackageReference Include="AWSSDK.Core" Version="3.7.304.31" />
<PackageReference Include="DotNetEnv" Version="2.3.0" />
<PackageReference Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="6.2.0" />
+15 -1
View File
@@ -25,6 +25,7 @@ using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Amazon.Runtime;
using StackExchange.Redis;
using IdentityModel.AspNetCore.OAuth2Introspection;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -219,7 +220,20 @@ namespace Notesnook.API
}).AddMessagePackProtocol().AddJsonProtocol();
if (!string.IsNullOrEmpty(Constants.SIGNALR_REDIS_CONNECTION_STRING))
signalR.AddStackExchangeRedis(Constants.SIGNALR_REDIS_CONNECTION_STRING);
{
services.AddHealthChecks()
.AddRedis(Constants.SIGNALR_REDIS_CONNECTION_STRING, tags: ["ready"]);
signalR.AddStackExchangeRedis(options =>
{
options.Configuration = ConfigurationOptions.Parse(Constants.SIGNALR_REDIS_CONNECTION_STRING);
options.Configuration.AbortOnConnectFail = false;
options.Configuration.ConnectRetry = 5;
options.Configuration.ReconnectRetryPolicy = new ExponentialRetry(5000, 30000);
options.Configuration.KeepAlive = 60;
options.Configuration.ConnectTimeout = 5000;
options.Configuration.SyncTimeout = 5000;
});
}
services.AddResponseCompression(options =>
{
@@ -190,8 +190,8 @@ namespace Streetwriters.Identity.Controllers
var client = Clients.FindClientById(form.ClientId);
if (client == null) return BadRequest("Invalid client_id.");
var user = await UserManager.FindByEmailAsync(form.Email) ?? throw new Exception("User not found.");
if (!await UserService.IsUserValidAsync(UserManager, user, form.ClientId)) return Ok();
var user = await UserManager.FindByEmailAsync(form.Email);
if (user == null || !await UserService.IsUserValidAsync(UserManager, user, form.ClientId)) return Ok();
var code = await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "ResetPassword");
var callbackUrl = UrlExtensions.TokenLink(user.Id.ToString(), code, client.Id, TokenType.RESET_PASSWORD);
@@ -24,7 +24,7 @@ namespace Streetwriters.Identity.Interfaces
{
public interface ISMSSender
{
Task<string> SendOTPAsync(string number, IClient client);
Task<string?> SendOTPAsync(string number, IClient client);
Task<bool> VerifyOTPAsync(string id, string code);
}
}
@@ -186,6 +186,8 @@ namespace Streetwriters.Identity.Services
ArgumentNullException.ThrowIfNull(form.PhoneNumber);
await UserManager.SetPhoneNumberAsync(user, form.PhoneNumber);
var id = await SMSSender.SendOTPAsync(form.PhoneNumber, client);
if (string.IsNullOrEmpty(id)) throw new Exception("Failed to send SMS. Please try again.");
logger.LogInformation("SMS OTP sent for user: {UserId}, SMS ID: {SmsId}", user.Id, id);
await this.ReplaceClaimAsync(user, MFAService.SMS_ID_CLAIM, id);
break;
+33 -13
View File
@@ -23,36 +23,56 @@ using Streetwriters.Common;
using Twilio.Rest.Verify.V2.Service;
using Twilio;
using System.Threading.Tasks;
using System;
using Microsoft.Extensions.Logging;
namespace Streetwriters.Identity.Services
{
public class SMSSender : ISMSSender
{
public SMSSender()
private readonly ILogger<SMSSender> Logger;
public SMSSender(ILogger<SMSSender> logger)
{
Logger = logger;
if (!string.IsNullOrEmpty(Constants.TWILIO_ACCOUNT_SID) && !string.IsNullOrEmpty(Constants.TWILIO_AUTH_TOKEN))
{
TwilioClient.Init(Constants.TWILIO_ACCOUNT_SID, Constants.TWILIO_AUTH_TOKEN);
}
}
public async Task<string> SendOTPAsync(string number, IClient app)
public async Task<string?> SendOTPAsync(string number, IClient app)
{
var verification = await VerificationResource.CreateAsync(
to: number,
channel: "sms",
pathServiceSid: Constants.TWILIO_SERVICE_SID
);
return verification.Sid;
try
{
var verification = await VerificationResource.CreateAsync(
to: number,
channel: "sms",
pathServiceSid: Constants.TWILIO_SERVICE_SID
);
return verification.Sid;
}
catch (Exception ex)
{
Logger.LogError(ex, "Error sending OTP with Twilio");
return null;
}
}
public async Task<bool> VerifyOTPAsync(string id, string code)
{
return (await VerificationCheckResource.CreateAsync(
verificationSid: id,
pathServiceSid: Constants.TWILIO_SERVICE_SID,
code: code
)).Status == "approved";
try
{
return (await VerificationCheckResource.CreateAsync(
verificationSid: id,
pathServiceSid: Constants.TWILIO_SERVICE_SID,
code: code
)).Status == "approved";
}
catch (Exception ex)
{
Logger.LogError(ex, "Error verifying OTP with Twilio");
return false;
}
}
}
}