Generar un token de contraseña de restablecimiento no funciona en el sitio web de Azure

Estoy implementando la funcionalidad de restablecer la contraseña en mi sitio mediante el uso de la clase de UserManager incorporada que viene con ASP.NET 5.

Todo funciona bien en mi entorno de desarrollo. Sin embargo, una vez que lo bash en el sitio de producción que se ejecuta como un sitio web de Azure, obtengo la siguiente excepción:

System.Security.Cryptography.CryptographicException : la operación de protección de datos no tuvo éxito. Esto puede deberse a que no se cargó el perfil de usuario para el contexto de usuario del subproceso actual, que puede ser el caso cuando el subproceso se hace pasar por otro.

Así es como configuro la instancia de UserManager :

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

Luego, genero el token de este modo (para ser enviado al usuario en un correo electrónico para que puedan verificar que realmente desean restablecer su contraseña):

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

Lamentablemente, cuando esto se ejecuta en Azure, obtengo la excepción anterior.

Busqué en Google y encontré esta posible solución . Sin embargo, no funcionó en absoluto y todavía recibo la misma excepción.

Según el enlace, tiene algo que ver con los tokens de sesión que no funcionan en una granja de servidores web como Azure.

DpapiDataProtectionProvider utiliza DPAPI, que no funcionará correctamente en un entorno de granja de servidores web / nube, ya que los datos cifrados solo pueden ser descifrados por la máquina que lo cifró. Lo que necesita es una forma de encriptar datos de manera que cualquier máquina en su entorno pueda descifrarlos. Desafortunadamente, ASP.NET Identity 2.0 no incluye ninguna otra implementación de IProtectionProvider que no sea DpapiDataProtectionProvider. Sin embargo, no es demasiado difícil hacer el suyo.

Una opción es utilizar la clase MachineKey de la siguiente manera:

 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); } } 

Para usar esta opción, hay un par de pasos que necesitarías seguir.

Paso 1

Modifique su código para usar MachineKeyProtectionProvider.

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

Paso 2

Sincronice el valor de MachineKey en todas las máquinas de su entorno de granja / nube web. Esto suena aterrador, pero es el mismo paso que hemos realizado en innumerables ocasiones para que la validación de ViewState funcione correctamente en una granja de servidores web (también usa DPAPI).

Considere usar IAppBuilder.GetDataProtectionProvider() lugar de declarar un nuevo DpapiDataProtectionProvider .

De forma similar a usted, presenté este problema configurando mi UserManager de esta manera, a partir de una muestra de código que encontré:

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

El problema de CodePlex vinculado anteriormente hace referencia a una publicación de blog que se ha actualizado con una solución más simple al problema. Se recomienda guardar una referencia estática al IDataProtector

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

… y luego hacer referencia a él desde el UserManager

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

La respuesta de johnso también proporciona un buen ejemplo de cómo conectar esto usando Autofac.

Tenía los mismos problemas, excepto que estaba alojando en Amazon EC2.
Pude resolverlo accediendo al grupo de aplicaciones en IIS y (en configuraciones avanzadas después de hacer clic con el botón derecho) al modelo de proceso de configuración – cargar perfil de usuario = verdadero.

Estaba teniendo el mismo problema ( Owin.Security.DataProtection.DpapiDataProtectionProvider falló cuando se ejecutó en Azure), y Staley está en lo cierto, no puede usar DpapiDataProtectionProvider .

Si está utilizando OWIN Startup Classes , puede evitar rodar su propio IDataProtectionProvider , en su lugar utilice el método IAppBuilder de IAppBuilder .

Por ejemplo, con Autofac:

 internal static IDataProtectionProvider DataProtectionProvider; public void ConfigureAuth(IAppBuilder app) { // ... DataProtectionProvider = app.GetDataProtectionProvider(); builder.Register(c => DataProtectionProvider) .InstancePerLifetimeScope(); // ... }