15

I need an instance of IDataProtectionProvider to generate email confirmation tokens using the Identity Framework UserManager in an Azure Web Jobs worker:

var confirmToken = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);

This crashes because a null IUserTokenProvider<User, int> was passed to the UserManager<User, int> upon constuction.

In the MVC application an instance is created like this:

public class OWINStartup
{
    public void Configuration(IAppBuilder app)
    {
        var dataProtectionProvider = app.GetDataProtectionProvider();

But of course, Azure Web Jobs doesn't have an OWINStartup hook. Any advice?

Korijn
  • 1,383
  • 9
  • 27
  • You can send the information needed to generate the token in a queue message. And use that message in your WebJob to do what's needed. – lopezbertoni Feb 19 '15 at 13:20
  • Yes, that's what I've settled for in the meantime. I just send the token along. But preferably, I would like to be able to generate it in the webjob. I am generating the token and sending the email in response to an event, after all. – Korijn Feb 19 '15 at 13:24

1 Answers1

21

Taking a look at the Katana source code for the OWIN startup context you can see the default implementation of the DataProtectionProvider is a MachineKeyDataProtectionProvider. Unfortunately this class is not exposed to us, only the DpapiDataProtectionProvider which will not work when hosted in azure.

You can find the implementation of the MachineKeyDataProtectionProvider here. You will need to also implement your own MachineKeyDataProtector as seen here. These are not difficult implmentations and are essentially wrappers around MachineKey.Protect() and MachineKey.Unprotect().

The implementation for MachineKeyDataProtectionProvider and MachineKeyDataProtector from the Katana project source (apache 2.0 license):

internal class MachineKeyProtectionProvider : IDataProtectionProvider
{
    public IDataProtector Create(params string[] purposes)
    {
        return new MachineKeyDataProtector(purposes);
    }
}

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

Once you have that implemented it is easy to plug into the UserManager:

var usermanager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>());
var machineKeyProtectionProvider = new MachineKeyProtectionProvider();
usermanager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(machineKeyProtectionProvider.Create("ASP.NET Identity"));

Hope that helps get you in the right direction.

jsturtevant
  • 2,560
  • 1
  • 23
  • 23
  • 3
    This does work.. kind of. But, since the `MachineKeyDataProtector ` is being used outside of the web app (in the web job), it uses it's own keys (not any that you might speficy in the web.config of the web app). So any token you generate here won't then be accepted as valid by the web app :( Not sure of a solution to this apart from rolling a different IDataProtector.... – Matt Roberts Jan 23 '17 at 14:23
  • @MattRoberts Did you find any solution to this? – Høgsdal Jul 21 '17 at 05:38
  • 1
    Links for [MachineKeyDataProtector](https://github.com/aspnet/AspNetKatana/blob/dev/src/Microsoft.Owin.Host.SystemWeb/DataProtection/MachineKeyDataProtector.cs) and [MachineKeyDataProtectionProvider](https://github.com/aspnet/AspNetKatana/blob/dev/src/Microsoft.Owin.Host.SystemWeb/DataProtection/MachineKeyDataProtectionProvider.cs) have changed. They moved to github. (well, all links have changed, but those are the two i just needed ;) ) – nilsK Dec 20 '18 at 10:28
  • @Høgsdal did either of you find a solution? I have the exact same issue. – Greg Veres Nov 03 '22 at 14:46
  • @GregVeres This is a long time ago so I can't exactly remember what I did. But I have upvoted jsturtevant and Matt Roberts posts so those would have let me to a solution. Hope this helps. – Høgsdal Nov 03 '22 at 21:54