UPD: Here is another good solution to this problem if you are running your own IIS.

Last week I was updating one of applications I work on to ASP.NET Identity. For a specific reasons I could not deploy to Azure for a while. But I did run all the tests locally and everything worked just fine.

When I mostly finished with Identity conversion, I finally managed to deploy the application to Azure Web-Sites. And it worked fine.. until I tried registering a user.

At that point I had an exception exploding in my face:

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's user context, which may be the case when the thread is impersonating.

A bit of digging online did not give me any results. Everything single solution in google was talking about Windows Identity Foundation, but I was not using it. I only have ASP.NET Identity.

After a bit of digging turned out that my application could not generate a token for email confirmation. And that was handled like this:

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

I have found this snippet somewhere online, but mostly had no idea what it does – cargo culting. My bad! And this very piece was causing issues when run on Azure Web-Sites.

After a lot of messing about with different available options, I came to conclusion there is a good way to do DI injection in UserManager and not take dependency on IdentityFactoryOptions<ApplicationUserManager> in constructor. For that you’ll need to save the reference to IDataProtector as a static where it is available – Startup.Auth.cs:

public partial class Startup
{
    internal static IDataProtectionProvider DataProtectionProvider { get; private set; }

    public void ConfigureAuth(IAppBuilder app)
    {
        DataProtectionProvider = app.GetDataProtectionProvider();
        // other stuff.
    }
}

Then you can reach this reference within 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
    }
}

I have tried this and it works locally, hosted in IIS and IIS Express. Also just deployed this to Azure and it works in Azure as well, giving nice long user tokens.

After some discussion in the comments, I’ve been shown a better way. Thanks for that, by the way!
The better way is to implement your own IDataProtector, shown in this SO answer

You’ll need to have <machineKey> in your web.config. Usually it looks like this:

<system.web>
    <machineKey validationKey="2EEA416CE..............99E0C" decryptionKey="87..............41A592" validation="SHA1" />
</system.web>

Google for it if you don’t know what it is, there are sites that can generate one for you.

And you’ll need to use MachineKey class in a wrapper:

using System.Web.Security;
using Microsoft.Owin.Security.DataProtection;

public class MachineKeyDataProtector : IDataProtector
{
    private readonly string[] purposes;

    public MachineKeyDataProtector(params string[] purposes)
    {
        this.purposes = purposes;
    }

    public byte[] Protect(byte[] userData)
    {
        return MachineKey.Protect(userData, purposes);
    }

    public byte[] Unprotect(byte[] protectedData)
    {
        return MachineKey.Unprotect(protectedData, purposes);
    }
}

And then in your UserManager specify as following:

public class UserManager : UserManager<ApplicationUser>
{
    public UserManager() : base(new UserStore<ApplicationUser>(new MyDbContext()))
    {
        var machineKeyDataProtector = new MachineKeyDataProtector("ResetPasswordPurpose");
        this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, Guid>(machineKeyDataProtector)
        {
            TokenLifespan = TimeSpan.FromHours(24),
        };
    }
}

I’ve just tried it and works just fine in Azure Web-Site. With a long and scary confirmation code.

  • D

    life saver!

  • Cristian

    YOU SAVE MY LIFE GENIUS!

  • Pingback: AspNet Identity and IoC Container Registration « Trailmax Tech()

  • Derek Flenniken

    This absolutely works and totally helped me, but a few things to note about this:

    1. EmailTokenProvider issues tokens that expire after 5 minutes. Good for 2FA, maybe not so much for password reset nor email confirmation.

    2. With Identity 2.1 it seems they’ve resolved the issue with DPAPI & Azure Websites. Have not dug into what changed. I had exactly this issue a few months ago, but now this works with DataProtectorTokenProvider

  • jakejgordon

    Thanks SO MUCH for posting this. Easy fix. :)

  • Sri

    Thank you! worked perfectly!

  • Nicholas Mitchell

    Dude you are so awesome I don’t even have the words. THANK YOU

  • Jean-Fran├žois Chatelain

    Great post! Worked for me thanks!!!!

  • Mac

    There is no good logic behind such an implementation, not a good example to follow

  • Pingback: GeneratePasswordResetTokenAsync and Data Protection Operation errors | LichtenBytes()

  • Arthur Hylton

    Thank you for this. It is the only solution I could find.

  • Popoola Oluwaseun

    This works but how about a web app deployed to multiple azure compute instances, does that actually means that each instance get to share the static IDataProtectionProvider property. Is it possible to use a distributed cache.

    • Seen your question on stack – left a comment there.
      In theory if all instances have the same machine key, it should work. But I’m not exactly sure and not in the position to test just now.
      You can’t really put an in-memory instance of an object into a distributed cache – it won’t get serialised properly and even if it will, the deserialised object will not be the same as original – yes, it will be the identical copy, but not the same one.

  • tien mai

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

  • Motlalepule Tshakela

    Thanks, I’ve been struggling with this issue for a while.

  • Eric Edelstein

    Seriously, thank you! Worked like a charm

  • Shashi Bhushan

    Worked like a charm!!! Great findings & thanks for making it public.