Skip to content

Commit

Permalink
Added two factor token providers which send tokens using INotificatio…
Browse files Browse the repository at this point in the history
…nManager
  • Loading branch information
artem-dudarev committed Mar 15, 2017
1 parent 05bb62d commit bd24c66
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using VirtoCommerce.Platform.Core.Notifications;
using VirtoCommerce.Platform.Data.Notifications;

namespace VirtoCommerce.Platform.Data.Security.Identity
{
public class ApplicationEmailTokenProvider : EmailTokenProvider<ApplicationUser>
{
private readonly INotificationManager _notificationManager;

public ApplicationEmailTokenProvider(INotificationManager notificationManager)
{
_notificationManager = notificationManager;
}

public override async Task NotifyAsync(string token, UserManager<ApplicationUser, string> manager, ApplicationUser user)
{
if (manager == null)
throw new ArgumentNullException(nameof(manager));

var notification = _notificationManager.GetNewNotification<TwoFactorSmsNotification>();

notification.Recipient = await manager.GetPhoneNumberAsync(user.Id);
notification.Token = token;

_notificationManager.SendNotification(notification);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using VirtoCommerce.Platform.Core.Notifications;
using VirtoCommerce.Platform.Data.Notifications;

namespace VirtoCommerce.Platform.Data.Security.Identity
{
public class ApplicationPhoneNumberTokenProvider : PhoneNumberTokenProvider<ApplicationUser>
{
private readonly INotificationManager _notificationManager;

public ApplicationPhoneNumberTokenProvider(INotificationManager notificationManager)
{
_notificationManager = notificationManager;
}

public override async Task NotifyAsync(string token, UserManager<ApplicationUser, string> manager, ApplicationUser user)
{
if (manager == null)
throw new ArgumentNullException(nameof(manager));

var notification = _notificationManager.GetNewNotification<TwoFactorEmailNotification>();

notification.Recipient = await manager.GetEmailAsync(user.Id);
notification.Token = token;

_notificationManager.SendNotification(notification);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.DataProtection;
using VirtoCommerce.Platform.Core.Notifications;
using VirtoCommerce.Platform.Data.Notifications;

namespace VirtoCommerce.Platform.Data.Security.Identity
{
Expand All @@ -13,13 +11,9 @@ namespace VirtoCommerce.Platform.Data.Security.Identity
/// </summary>
public class ApplicationUserManager : UserManager<ApplicationUser>
{
private readonly INotificationManager _notificationManager;

public ApplicationUserManager(IUserStore<ApplicationUser> store, IDataProtectionProvider dataProtectionProvider, INotificationManager notificationManager)
: base(store)
{
_notificationManager = notificationManager;

// Configure validation logic for usernames
UserValidator = new UserValidator<ApplicationUser>(this)
{
Expand All @@ -43,41 +37,15 @@ public ApplicationUserManager(IUserStore<ApplicationUser> store, IDataProtection
MaxFailedAccessAttemptsBeforeLockout = 5;

// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug in here.
RegisterTwoFactorProvider("PhoneNumberTokenProvider", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "{0}" // Token
});
RegisterTwoFactorProvider("EmailTokenProvider", new EmailTokenProvider<ApplicationUser>
{
BodyFormat = "{0}" // Token
});
// You can write your own provider and plug it in here.
RegisterTwoFactorProvider("PhoneCode", new ApplicationPhoneNumberTokenProvider(notificationManager));
RegisterTwoFactorProvider("EmailCode", new ApplicationEmailTokenProvider(notificationManager));

if (dataProtectionProvider != null)
{
UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
}

public override async Task SendEmailAsync(string userId, string subject, string body)
{
var notification = _notificationManager.GetNewNotification<TwoFactorEmailNotification>();

notification.Recipient = await GetEmailAsync(userId);
notification.Token = body;

_notificationManager.SendNotification(notification);
}

public override async Task SendSmsAsync(string userId, string message)
{
var notification = _notificationManager.GetNewNotification<TwoFactorSmsNotification>();

notification.Recipient = await GetPhoneNumberAsync(userId);
notification.Token = message;

_notificationManager.SendNotification(notification);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@
<Compile Include="CurrentUserOwinMiddleware.cs" />
<Compile Include="IApiAccountProvider.cs" />
<Compile Include="Identity\ApplicationClaimsIdentityProvider.cs" />
<Compile Include="Identity\ApplicationEmailTokenProvider.cs" />
<Compile Include="Identity\ApplicationSignInManager.cs" />
<Compile Include="Identity\ApplicationPhoneNumberTokenProvider.cs" />
<Compile Include="Identity\ApplicationUser.cs" />
<Compile Include="Identity\ApplicationUserManager.cs" />
<Compile Include="Identity\ApplicationUserStore.cs" />
Expand Down
18 changes: 16 additions & 2 deletions VirtoCommerce.Platform.Web/Controllers/Api/SecurityController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,30 @@ public SecurityController(Func<ApplicationSignInManager> signInManagerFactory, F
[AllowAnonymous]
public async Task<IHttpActionResult> Login(UserLogin model)
{
if (await _signInManagerFactory().PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, true) == SignInStatus.Success)
var signInManager = _signInManagerFactory();
var signInStatus = await signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, true);

if (signInStatus == SignInStatus.Success)
{
var retVal = await _securityService.FindByNameAsync(model.UserName, UserDetails.Full);
//Do not allow login to admin customers and rejected users
if (retVal.UserState != AccountState.Rejected && !String.Equals(retVal.UserType, AccountType.Customer.ToString(), StringComparison.InvariantCultureIgnoreCase))
if (retVal.UserState != AccountState.Rejected && !string.Equals(retVal.UserType, AccountType.Customer.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
return Ok(retVal);
}
}

if (signInStatus == SignInStatus.RequiresVerification)
{
// TODO: Add UI for choosing a two factor provider, sending the code and verifying the entered code.
// var userId = await signInManager.GetVerifiedUserIdAsync();
// var providers = await signInManager.UserManager.GetValidTwoFactorProvidersAsync(userId);
// var provider = providers.FirstOrDefault(); // User should choose a provider
// var sendCodeResult = await signInManager.SendTwoFactorCodeAsync(provider);
// var code = "123456"; // Get code from user
// var twoFactorSignInStatus = await signInManager.TwoFactorSignInAsync(provider, code, false, false);
}

return StatusCode(HttpStatusCode.Unauthorized);
}

Expand Down
18 changes: 14 additions & 4 deletions VirtoCommerce.Platform.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public void Configuration(IAppBuilder app, string virtualRoot, string routPrefix
{
Subject = PlatformNotificationResource.RegistrationNotificationSubject,
Body = PlatformNotificationResource.RegistrationNotificationBody,
Language = "en-US",
}
});

Expand All @@ -213,23 +214,32 @@ public void Configuration(IAppBuilder app, string virtualRoot, string routPrefix
{
Subject = PlatformNotificationResource.ResetPasswordNotificationSubject,
Body = PlatformNotificationResource.ResetPasswordNotificationBody,
Language = "en-US",
}
});

notificationManager.RegisterNotificationType(() => new TwoFactorEmailNotification(container.Resolve<IEmailNotificationSendingGateway>())
{
DisplayName = "Two factor authentication",
Description = "This notification contains a security token for two factor authentication",
Subject = PlatformNotificationResource.TwoFactorNotificationSubject,
Body = PlatformNotificationResource.TwoFactorNotificationBody,
NotificationTemplate = new NotificationTemplate
{
Subject = PlatformNotificationResource.TwoFactorNotificationSubject,
Body = PlatformNotificationResource.TwoFactorNotificationBody,
Language = "en-US",
}
});

notificationManager.RegisterNotificationType(() => new TwoFactorSmsNotification(container.Resolve<ISmsNotificationSendingGateway>())
{
DisplayName = "Two factor authentication",
Description = "This notification contains a security token for two factor authentication",
Subject = PlatformNotificationResource.TwoFactorNotificationSubject,
Body = PlatformNotificationResource.TwoFactorNotificationBody,
NotificationTemplate = new NotificationTemplate
{
Subject = PlatformNotificationResource.TwoFactorNotificationSubject,
Body = PlatformNotificationResource.TwoFactorNotificationBody,
Language = "en-US",
}
});

//Get initialized modules list sorted by dependency order
Expand Down

0 comments on commit bd24c66

Please sign in to comment.