0

I am attempting to allow password reset for my users and I cannot figure out why code and callbackUrl are returning false. Here is my forgot password method:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.Email);
        if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
        {
            // Don't reveal that the user does not exist or is not confirmed
            return View("ForgotPasswordConfirmation");
        }

        // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
        // Send an email with this link
        string callbackUrl = await ResetPasswordConfirmationTokenAsync(user.Id, "Password Reset"); 
        return RedirectToAction("ForgotPasswordConfirmation", "Account");
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

see breakpoint info

then I create the password reset task

private async Task<string> ResetPasswordConfirmationTokenAsync(string userID, string subject)
{
    // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
    // Send an email with this link:
    string code = await UserManager.GeneratePasswordResetTokenAsync(userID);
    var callbackUrl = Url.Action("ForgotPassword", "Account", new { userId = userID, code = code }, protocol: Request.Url.Scheme); 
    await UserManager.SendEmailAsync(userID, subject, "Please confirm your account by <a href=\"" + callbackUrl + "\">clicking here</a>");


    return callbackUrl;
}

see breakpoint info for this

Since both values return null it generates the error No IUserTokenProvider is registered. Why is this happening when user is not null?

user6225
  • 35
  • 1
  • 9

1 Answers1

2

I had this exact same issue. If you have not already created a email service class you need to do so, then you need to reconfigure your identity class. I suggest you use something like this:

public async Task SendAsync(IdentityMessage message)
{
    MailMessage email = new MailMessage(new 
    MailAddress("youremailadress@domain.com", "(any subject here)"),
    new MailAddress(message.Destination));
    email.Subject = message.Subject;
    email.Body = message.Body;

    email.IsBodyHtml = true;

    GmailEmailService mailClient = new GmailEmailService();
    await mailClient.SendMailAsync(email);
}

Next, add your credentials to the webconfig

<appSettings>
     <add key="webpages:Version" value="3.0.0.0" />
     <add key="webpages:Enabled" value="false" />
     <add key="ClientValidationEnabled" value="true" />
     <add key="UnobtrusiveJavaScriptEnabled" value="true" />

     <add key="GmailUserName" value="youremail@yourdomain.com"/>
     <add key="GmailPassword" value="yourPassword"/>
     <add key="GmailHost" value="yourServer"/>
     <add key="GmailPort" value="yourPort"/>
     <add key="GmailSsl" value="chooseTrueOrFalse"/>    
</appSettings>

Finally there are several changes you need to add to your account controller. Please see below:


  1. Create a new folder called MyClasses and create and add the following class

    public class GmailEmailService:SmtpClient
    {
        // Gmail user-name
        public string UserName { get; set; }
    
    
    public GmailEmailService() :
        base(ConfigurationManager.AppSettings["GmailHost"], Int32.Parse(ConfigurationManager.AppSettings["GmailPort"]))
    {
        //Get values from web.config file:
        this.UserName = ConfigurationManager.AppSettings["GmailUserName"];
        this.EnableSsl = Boolean.Parse(ConfigurationManager.AppSettings["GmailSsl"]);
        this.UseDefaultCredentials = false;
        this.Credentials = new System.Net.NetworkCredential(this.UserName, ConfigurationManager.AppSettings["GmailPassword"]);
    }
    
    }
  2. Configure your Identity Class

    public async Task SendAsync(IdentityMessage message)
    {
        MailMessage email = new MailMessage(new MailAddress("youremailadress@domain.com", "(any subject here)"),
        new MailAddress(message.Destination));
        email.Subject = message.Subject;
        email.Body = message.Body;
    
    
    email.IsBodyHtml = true;
    
    GmailEmailService mailClient = new GmailEmailService();
    await mailClient.SendMailAsync(email);
    
    }
  3. Add your credentials to the web.config. I did not use gmail in this portion because the use of gmail is blocked in my workplace and it still works perfectly.

    <add key="GmailUserName" value="youremail@yourdomain.com"/>
    <add key="GmailPassword" value="yourPassword"/>
    <add key="GmailHost" value="yourServer"/>
    <add key="GmailPort" value="yourPort"/>
    <add key="GmailSsl" value="chooseTrueOrFalse"/>
    <!--Smptp Server (confirmations emails)-->
    

  4. Make necessary changes to your Account Controller. Add the following highlighted code.

First do this

Then This

Compile then run. Cheers!

Graham
  • 7,431
  • 18
  • 59
  • 84
Skullomania
  • 2,225
  • 2
  • 29
  • 65
  • A link to a solution is welcome, but please ensure your answer is useful without it: [add context around the link](//meta.stackexchange.com/a/8259) so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. [Answers that are little more than a link may be deleted.](//stackoverflow.com/help/deleted-answers) – NobodyNada Jul 21 '17 at 19:13
  • I was close, I worked on this quite a bit since then. I was missing some the `DpapiDataProtectionProvider` bit you pointed out in the account controller. Thanks! – user6225 Jul 21 '17 at 19:24
  • @NobodyNada, is that better? – Skullomania Jul 21 '17 at 19:26
  • @Skullomania It's better, thanks! I'd suggest including *at least* a summary of the "several changes you need to add to your account controller." – NobodyNada Jul 21 '17 at 19:28
  • I was trying not to copy and paste the documentation itself. There are several changes in the account controller that needs to be made. At your request I will update the answer again – Skullomania Jul 21 '17 at 19:30