Password reset token reset not working on Azure Website

I implement the reset password functionality on my site using the built-in UserManager class that ships with ASP.NET 5.

Everything works fine in my dev environment. However, as soon as I try it on a production site that runs as an Azure site, I get the following exception:

System.Security.Cryptography.CryptographicException: The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread user context, which may be the case when the thread is impersonating. 

This is how I configure the UserManager instance:

 var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider(SiteConfig.SiteName); UserManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<User>(provider.Create(ResetPasswordPurpose)); 

Then I generate the token in this way (to send the user an email so they can make sure that they really want to reset their password):

 string token = UserManager.GeneratePasswordResetToken(user.Id); 

Unfortunately, when it works on Azure, I get the exception above.

I searched Google and found this possible solution . However, this did not work, and I still get the same exception.

According to the link, this has something to do with session tokens that do not work on a web farm such as Azure.

+46
asp.net-mvc azure
May 4 '14 at 11:16
source share
4 answers

DpapiDataProtectionProvider uses DPAPI , which will not work properly in a web farm / cloud environment, since the encrypted data can only be decrypted by the machine that encrypted it. You need a way to encrypt data so that it can be decrypted by any machine in your environment. Unfortunately, ASP.NET Identity 2.0 does not include another implementation of IProtectionProvider other than DpapiDataProtectionProvider. However, it is not too difficult to roll.

One option is to use the MachineKey class as follows:

 public class MachineKeyProtectionProvider : IDataProtectionProvider { public IDataProtector Create(params string[] purposes) { return new MachineKeyDataProtector(purposes); } } public class MachineKeyDataProtector : IDataProtector { private readonly string[] _purposes; public MachineKeyDataProtector(string[] purposes) { _purposes = purposes; } public byte[] Protect(byte[] userData) { return MachineKey.Protect(userData, _purposes); } public byte[] Unprotect(byte[] protectedData) { return MachineKey.Unprotect(protectedData, _purposes); } } 

To use this option, you need to follow a few steps.

Step 1

Modify your code to use MachineKeyProtectionProvider.

 using Microsoft.AspNet.Identity.Owin; // ... var provider = new MachineKeyProtectionProvider(); UserManager.UserTokenProvider = new DataProtectorTokenProvider<User>( provider.Create("ResetPasswordPurpose")); 

Step 2

Synchronize MachineKey value on all web farm / cloud machines. This sounds scary, but it's the same step that we have done countless times to get the correct ViewState validation in the web farm (it also uses DPAPI).

+76
May 14 '14 at 18:01
source share

Consider using IAppBuilder.GetDataProtectionProvider() instead of declaring a new DpapiDataProtectionProvider .

Like you, I introduced this problem by setting up my UserManager like this from the found code:

 public class UserManager : UserManager<ApplicationUser> { public UserManager() : base(new UserStore<ApplicationUser>(new MyDbContext())) { // this does not work on azure!!! var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ASP.NET IDENTITY"); this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("EmailConfirmation")) { TokenLifespan = TimeSpan.FromHours(24), }; } } 

The CodePlex issue associated with the above actually refers to a blog post that has been updated with a simpler solution to the problem. He recommends keeping a static link to IDataProtector ...

 public partial class Startup { internal static IDataProtectionProvider DataProtectionProvider { get; private set; } public void ConfigureAuth(IAppBuilder app) { DataProtectionProvider = app.GetDataProtectionProvider(); // other stuff. } } 

... and then referring to it from the UserManager

 public class UserManager : UserManager<ApplicationUser> { public UserManager() : base(new UserStore<ApplicationUser>(new MyDbContext())) { var dataProtectionProvider = Startup.DataProtectionProvider; this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity")); // do other configuration } } 

The answer from johnso also serves as a good example of how to connect this with Autofac.

+28
Jun 05 '15 at 22:53
source share

I had the same problems besides what I was taking on amazon ec2. I was able to solve it by going to the application pool in iis and (under additional settings after right-clicking), setting the process model - load user profile = true.

+17
Jan 19 '15 at 20:57
source share

I had the same problem ( Owin.Security.DataProtection.DpapiDataProtectionProvider crashes when starting on Azure) and Staley is correct, you cannot use DpapiDataProtectionProvider .

If you use the OWIN Startup Classes , you can not use your own IDataProtectionProvider , use the GetDataProtectionProvider IAppBuilder method IAppBuilder .

For example, with Autofac:

 internal static IDataProtectionProvider DataProtectionProvider; public void ConfigureAuth(IAppBuilder app) { // ... DataProtectionProvider = app.GetDataProtectionProvider(); builder.Register<IDataProtectionProvider>(c => DataProtectionProvider) .InstancePerLifetimeScope(); // ... } 
+6
Jun 02 '15 at 1:35
source share



All Articles