diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props
index 89faa3f3c7..30ebb40fdd 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props
@@ -9,6 +9,7 @@
+
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj
index 2762b17f1c..ce557d156f 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs
index a888a3af38..eb8bdcff8a 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.PhoneConfirmation.cs
@@ -8,11 +8,12 @@ namespace Boilerplate.Server.Api.Controllers.Identity;
public partial class IdentityController
{
- [AutoInject] private SmsService smsService = default!;
+ [AutoInject] private PhoneService phoneService = default!;
[HttpPost]
public async Task SendConfirmPhoneToken(SendPhoneTokenRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindByPhoneNumber(request.PhoneNumber!)
?? throw new BadRequestException(Localizer[nameof(AppStrings.UserNotFound)]);
@@ -25,6 +26,7 @@ public async Task SendConfirmPhoneToken(SendPhoneTokenRequestDto request, Cancel
[HttpPost, Produces()]
public async Task ConfirmPhone(ConfirmPhoneRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindByPhoneNumber(request.PhoneNumber!)
?? throw new BadRequestException(Localizer[nameof(AppStrings.UserNotFound)]);
@@ -77,6 +79,6 @@ private async Task SendConfirmPhoneToken(User user, CancellationToken cancellati
var token = await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"VerifyPhoneNumber:{phoneNumber},{user.PhoneNumberTokenRequestedOn?.ToUniversalTime()}"));
var link = new Uri(HttpContext.Request.GetWebClientUrl(), $"{Urls.ConfirmPage}?phoneNumber={Uri.EscapeDataString(phoneNumber!)}&phoneToken={Uri.EscapeDataString(token)}&culture={CultureInfo.CurrentUICulture.Name}");
- await smsService.SendSms(Localizer[nameof(AppStrings.ConfirmPhoneTokenSmsText), token], phoneNumber, cancellationToken);
+ await phoneService.SendSms(Localizer[nameof(AppStrings.ConfirmPhoneTokenSmsText), token], phoneNumber, cancellationToken);
}
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs
index 0224a83a05..24846da43f 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.ResetPassword.cs
@@ -13,6 +13,7 @@ public partial class IdentityController
[HttpPost]
public async Task SendResetPasswordToken(SendResetPasswordTokenRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindUserAsync(request)
?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]);
@@ -48,7 +49,7 @@ public async Task SendResetPasswordToken(SendResetPasswordTokenRequestDto reques
if (await userManager.IsPhoneNumberConfirmedAsync(user))
{
- sendMessagesTasks.Add(smsService.SendSms(message, user.PhoneNumber!, cancellationToken));
+ sendMessagesTasks.Add(phoneService.SendSms(message, user.PhoneNumber!, cancellationToken));
}
//#if (signalr == true)
@@ -65,6 +66,7 @@ public async Task SendResetPasswordToken(SendResetPasswordTokenRequestDto reques
[HttpPost]
public async Task ResetPassword(ResetPasswordRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindUserAsync(request) ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]);
var expired = (DateTimeOffset.Now - user.ResetPasswordTokenRequestedOn) > AppSettings.Identity.ResetPasswordTokenLifetime;
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs
index 5d570e3e7b..abbaa6f0a4 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.SocialSignIn.cs
@@ -34,7 +34,7 @@ public async Task SocialSignInCallback(string? returnUrl = null, i
try
{
var email = info.Principal.GetEmail();
- var phoneNumber = info.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.HomePhone or ClaimTypes.MobilePhone or ClaimTypes.OtherPhone)?.Value;
+ var phoneNumber = phoneService.NormalizePhoneNumber(info.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.HomePhone or ClaimTypes.MobilePhone or ClaimTypes.OtherPhone)?.Value);
var user = await userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs
index 190e8faf9f..5c86ee8042 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/IdentityController.cs
@@ -9,6 +9,7 @@
using Boilerplate.Shared.Dtos.Identity;
using Boilerplate.Server.Api.Models.Identity;
using Boilerplate.Shared.Controllers.Identity;
+using Boilerplate.Server.Api.Services.Identity;
namespace Boilerplate.Server.Api.Controllers.Identity;
@@ -43,6 +44,7 @@ public partial class IdentityController : AppControllerBase, IIdentityController
[HttpPost]
public async Task SignUp(SignUpRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
//#if (captcha == "reCaptcha")
if (await googleRecaptchaHttpClient.Verify(request.GoogleRecaptchaResponse, cancellationToken) is false)
throw new BadRequestException(Localizer[nameof(AppStrings.InvalidGoogleRecaptchaResponse)]);
@@ -88,6 +90,7 @@ public async Task SignUp(SignUpRequestDto request, CancellationToken cancellatio
[HttpPost, Produces()]
public async Task SignIn(SignInRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
signInManager.AuthenticationScheme = IdentityConstants.BearerScheme;
var user = await userManager.FindUserAsync(request) ?? throw new UnauthorizedException(Localizer[nameof(AppStrings.InvalidUserCredentials)]);
@@ -239,6 +242,7 @@ await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not U
[HttpPost]
public async Task SendOtp(IdentityRequestDto request, string? returnUrl = null, CancellationToken cancellationToken = default)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindUserAsync(request)
?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]);
@@ -264,7 +268,7 @@ public async Task SendOtp(IdentityRequestDto request, string? returnUrl = null,
if (await userManager.IsPhoneNumberConfirmedAsync(user))
{
var smsMessage = Localizer[nameof(AppStrings.OtpShortText), await userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, FormattableString.Invariant($"Otp_Sms,{user.OtpRequestedOn?.ToUniversalTime()}"))].ToString();
- sendMessagesTasks.Add(smsService.SendSms(smsMessage, user.PhoneNumber!, cancellationToken));
+ sendMessagesTasks.Add(phoneService.SendSms(smsMessage, user.PhoneNumber!, cancellationToken));
}
//#if (signalr == true)
@@ -283,6 +287,7 @@ public async Task SendOtp(IdentityRequestDto request, string? returnUrl = null,
[HttpPost]
public async Task SendTwoFactorToken(SignInRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindUserAsync(request) ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.UserNotFound)]);
if (user.TwoFactorEnabled is false)
@@ -320,7 +325,7 @@ public async Task SendTwoFactorToken(SignInRequestDto request, CancellationToken
if (firstStepAuthenticationMethod != "Sms" && await userManager.IsPhoneNumberConfirmedAsync(user))
{
- sendMessagesTasks.Add(smsService.SendSms(message, user.PhoneNumber!, cancellationToken));
+ sendMessagesTasks.Add(phoneService.SendSms(message, user.PhoneNumber!, cancellationToken));
}
//#if (signalr == true)
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs
index 9a4318feac..986753a196 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs
@@ -1,7 +1,7 @@
using System.Text;
using System.Text.Encodings.Web;
-using Humanizer;
using QRCoder;
+using Humanizer;
using Boilerplate.Server.Api.Services;
using Boilerplate.Shared.Dtos.Identity;
using Boilerplate.Server.Api.Models.Identity;
@@ -18,7 +18,7 @@ public partial class UserController : AppControllerBase, IUserController
[AutoInject] private IUserEmailStore userEmailStore = default!;
- [AutoInject] private SmsService smsService = default!;
+ [AutoInject] private PhoneService phoneService = default!;
[AutoInject] private EmailService emailService = default!;
@@ -218,6 +218,7 @@ public async Task ChangeEmail(ChangeEmailRequestDto request, CancellationToken c
[HttpPost]
public async Task SendChangePhoneNumberToken(SendPhoneTokenRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindByIdAsync(User.GetUserId().ToString());
var resendDelay = (DateTimeOffset.Now - user!.PhoneNumberTokenRequestedOn) - AppSettings.Identity.PhoneNumberTokenLifetime;
@@ -233,12 +234,13 @@ public async Task SendChangePhoneNumberToken(SendPhoneTokenRequestDto request, C
var token = await userManager.GenerateChangePhoneNumberTokenAsync(user!, request.PhoneNumber!);
- await smsService.SendSms(Localizer[nameof(AppStrings.ChangePhoneNumberTokenSmsText), token], request.PhoneNumber!, cancellationToken);
+ await phoneService.SendSms(Localizer[nameof(AppStrings.ChangePhoneNumberTokenSmsText), token], request.PhoneNumber!, cancellationToken);
}
[HttpPost]
public async Task ChangePhoneNumber(ChangePhoneNumberRequestDto request, CancellationToken cancellationToken)
{
+ request.PhoneNumber = phoneService.NormalizePhoneNumber(request.PhoneNumber);
var user = await userManager.FindByIdAsync(User.GetUserId().ToString());
var expired = (DateTimeOffset.Now - user!.PhoneNumberTokenRequestedOn) > AppSettings.Identity.PhoneNumberTokenLifetime;
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
index 29376e3c18..e33ac8a191 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
@@ -11,6 +11,7 @@
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.ResponseCompression;
using Twilio;
+using PhoneNumbers;
using FluentStorage;
using FluentStorage.Blobs;
//#if (notification == true)
@@ -19,6 +20,7 @@
using Boilerplate.Server.Api.Services;
using Boilerplate.Server.Api.Controllers;
using Boilerplate.Server.Api.Models.Identity;
+using Boilerplate.Server.Api.Services.Identity;
namespace Boilerplate.Server.Api;
@@ -195,7 +197,7 @@ void AddDbContext(DbContextOptionsBuilder options)
}
services.AddTransient();
- services.AddTransient();
+ services.AddTransient();
if (appSettings.Sms.Configured)
{
TwilioClient.Init(appSettings.Sms.TwilioAccountSid, appSettings.Sms.TwilioAutoToken);
@@ -230,6 +232,8 @@ void AddDbContext(DbContextOptionsBuilder options)
services.AddAdsPush(configuration);
services.AddTransient();
//#endif
+
+ services.AddSingleton(_ => PhoneNumberUtil.GetInstance());
}
private static void AddIdentity(WebApplicationBuilder builder)
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppIdentityErrorDescriber.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppIdentityErrorDescriber.cs
similarity index 98%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppIdentityErrorDescriber.cs
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppIdentityErrorDescriber.cs
index 9792e886e1..653f1e4f7b 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppIdentityErrorDescriber.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppIdentityErrorDescriber.cs
@@ -1,6 +1,6 @@
using System.Data;
-namespace Boilerplate.Server.Api.Services;
+namespace Boilerplate.Server.Api.Services.Identity;
public partial class AppIdentityErrorDescriber : IdentityErrorDescriber
{
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppSecureJwtDataFormat.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppSecureJwtDataFormat.cs
similarity index 97%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppSecureJwtDataFormat.cs
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppSecureJwtDataFormat.cs
index 56fe5ecdd7..77f23f832e 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppSecureJwtDataFormat.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppSecureJwtDataFormat.cs
@@ -2,7 +2,7 @@
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authentication;
-namespace Boilerplate.Server.Api.Services;
+namespace Boilerplate.Server.Api.Services.Identity;
///
/// Stores bearer token in jwt format
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserClaimsPrincipalFactory.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserClaimsPrincipalFactory.cs
similarity index 93%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserClaimsPrincipalFactory.cs
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserClaimsPrincipalFactory.cs
index fb8ddb7f98..1b9a6f5146 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserClaimsPrincipalFactory.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserClaimsPrincipalFactory.cs
@@ -1,6 +1,6 @@
using Boilerplate.Server.Api.Models.Identity;
-namespace Boilerplate.Server.Api.Services;
+namespace Boilerplate.Server.Api.Services.Identity;
public partial class AppUserClaimsPrincipalFactory(UserManager userManager, IOptions optionsAccessor)
: UserClaimsPrincipalFactory(userManager, optionsAccessor)
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserConfirmation.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserConfirmation.cs
similarity index 84%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserConfirmation.cs
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserConfirmation.cs
index 14a4f64f87..7113b30613 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/AppUserConfirmation.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/Identity/AppUserConfirmation.cs
@@ -1,6 +1,6 @@
using Boilerplate.Server.Api.Models.Identity;
-namespace Boilerplate.Server.Api.Services;
+namespace Boilerplate.Server.Api.Services.Identity;
public partial class AppUserConfirmation : IUserConfirmation
{
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/SmsService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PhoneService.cs
similarity index 51%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/SmsService.cs
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PhoneService.cs
index f992c9896c..52fe8e48c7 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/SmsService.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/PhoneService.cs
@@ -1,12 +1,33 @@
-using Twilio.Rest.Api.V2010.Account;
+using PhoneNumbers;
+using Twilio.Rest.Api.V2010.Account;
namespace Boilerplate.Server.Api.Services;
-public partial class SmsService
+public partial class PhoneService
{
[AutoInject] private readonly AppSettings appSettings = default!;
- [AutoInject] private readonly ILogger logger = default!;
+ [AutoInject] private readonly ILogger logger = default!;
[AutoInject] private readonly IHostEnvironment hostEnvironment = default!;
+ [AutoInject] private readonly IHttpContextAccessor httpContextAccessor = default!;
+ [AutoInject] private readonly PhoneNumberUtil phoneNumberUtil = default!;
+ private const string APP_DEFAULT_REGION = "US" /*Two letter ISO region name*/;
+
+ public string? NormalizePhoneNumber(string? phoneNumber)
+ {
+ if (string.IsNullOrEmpty(phoneNumber))
+ return null;
+
+ // Get region from Cloudflare "CF-IPCountry" header if available, otherwise use UI culture's region if multilingual is enabled, or fallback to the default region.
+ var region = httpContextAccessor.HttpContext!.Request.Headers.TryGetValue("CF-IPCountry", out var value)
+ ? value.ToString()
+ : CultureInfoManager.MultilingualEnabled
+ ? new RegionInfo(CultureInfo.CurrentUICulture.Name).TwoLetterISORegionName
+ : APP_DEFAULT_REGION;
+
+ var parsedPhoneNumber = phoneNumberUtil.Parse(phoneNumber, region);
+
+ return phoneNumberUtil.Format(parsedPhoneNumber, PhoneNumberFormat.E164);
+ }
public async Task SendSms(string messageText, string phoneNumber, CancellationToken cancellationToken)
{