I have a windows service, a MVC application and a Webforms web application in the same solution. From Webforms I am trying to send a password confirm e-mail to a user. The Webforms app sends a message through MSMQ to the Windows Service (message consumers). The Windows Service then creates a user, their confirmation token and sends the e-mail.
The user when clicking the link then confirms their e-mail on the MVC application. All this works fine on me development machine.
Whenever I deploy on production I start getting invalid token issues.
Some relevant code in my setup:
In the Windows Service:
Autofac config
builder.Register(c => new UserManager<User, long>(new UserStore(new DbEntities()))).InstancePerLifetimeScope();
Inside the method that creates the token and e-mail
Constructor
private readonly UserManager<User, long> _usermanager;
private readonly IUnitOfWork _uow;
public ConfiguratorUserService(IUnitOfWork uow)
{
_uow = uow;
_usermanager = new UserManager<User, long>(new UserStore((DeronEntities)_uow.Context));
var provider = new DpapiDataProtectionProvider("DeronConfigurator");
_usermanager.UserTokenProvider = new DataProtectorTokenProvider<User, long>(provider.Create("Passwords"));
}
Method
var user = new User
{
... more props setting
UserName = contact.Emailadres,
};
_usermanager.Create(user);
var provider = new DpapiDataProtectionProvider("Configurator");
_usermanager.UserTokenProvider = new DataProtectorTokenProvider<User, long>(provider.Create("Passwords"));
var token = _usermanager.GenerateEmailConfirmationToken(user.Id);
var data = Encoding.UTF8.GetBytes(token);
var base64EncodedToken = System.Convert.ToBase64String(data);
// And sending the e-mail here
Then on the MVC side I have
Autofac
builder.Register(c => new UserManager<User, long>(new UserStore(new DeronEntities()))).InstancePerLifetimeScope();
Auth setup (startup)
var provider = new DpapiDataProtectionProvider("Configurator");
app.CreatePerOwinContext(() => new UserManager(new UserStore(new DeronEntities()))
{
UserTokenProvider = new DataProtectorTokenProvider<User, long>(provider.Create("Passwords"))
});
AccountController
var data = Convert.FromBase64String(code);
var base64Decoded = Encoding.UTF8.GetString(data);
var result = await UserManager.ConfirmEmailAsync(userId, base64Decoded);
What I have tried and found out":
Tried solutions: http://www.gunaatita.com/blog/Invalid-Token-Error-on-Email-Confirmation-in-Aspnet-Identity/1056
MachineKeyDataProtector - Invalid link when confirmation email sent through background job
I am unsure how to setup machine key usage for the Windows Service
I have resolved the issue. It is by using machine key setup.
Relevant posts that helped me solve it:
MachineKeyDataProtector - Invalid link when confirmation email sent through background job
Generating reset password token does not work in Azure Website
So my setup now looks like this:
In Windows Service
Autofac config:
builder.Register<IUserTokenProvider<User, long>>(c => DataProtector.TokenProvider).InstancePerLifetimeScope();
builder.RegisterType<UserManager<User, long>>()
.As<UserManager<User, long>>().InstancePerLifetimeScope();
And in the constructor:
private readonly IUnitOfWork _uow;
private readonly UserManager<User, long> _usermanager;
public ConfiguratorUserService(UserManager<User, long> userManager, IUnitOfWork uow)
{
_uow = uow;
_usermanager = userManager;
}
In MVC
Autofac
builder.Register(c => new UserManager<User, long>(new UserStore(new DeronEntities()))).InstancePerLifetimeScope();
builder.Register<IUserTokenProvider<User, long>>(c => DataProtector.TokenProvider).InstancePerRequest();
I have taken the MachineKeyProtectionProvider from the linked posts
Add in your web.config the machine key:
<system.web>
<machineKey decryption="AES" decryptionKey="dec key here" validation="HMACSHA256" validationKey="key here"/>
</system.web>
And add the same in the app.config of the widows service