I'm attempting to migrate my MVC4 application from ASP.NET Membership to the new SimpleMembership and have run into a snag with password resets. In my Login view, I have "Forgot Password" hyperlink that does an AJAX post to an action that sends an email with a reset password hyperlink (based loosely on MSDN guidelines):
[HttpPost]
[AllowAnonymous]
public ActionResult ForgotPassword(string userName)
{
    //get the user from database, validate it exists, etc.
    var token = WebSecurity.GeneratePasswordResetToken(userName);
    var resetLink = "<a href='" + Url.Action("ResetPassword", "Account", new {un = userName, rt = token}, "http") + "'>Reset Password</a>";
    //send the email
    return Json(new {result = "Email sent."});
}
This works fine, but for some reason when I attempt to use the Reset Password link, I get redirected to the Login screen and Fiddler shows a 401-Unauthorized. I've tried copy/pasting the link into the browser address bar as well as creating a junk HTML file with the anchor tag in it, but the result is still a 401. Here's the Reset Password action (also in the Account controller from the MVC4 template):
[HttpPost]
[AllowAnonymous]
public ActionResult ResetPassword(string un, string rt)
{
    var model = new ResetPasswordViewModel {UserName = un, ResetToken = rt};
    return View(model);
}
I've tried removing the HttpPost attribute as well, but that also yields a 401.  What am I missing here?
UPDATE
Thanks to Steve's comment, I fully-qualified the HttpPost attribute and have now realized that it's attempting to post to an area instead of straight to the root Account controller.  Using RouteDebugger, I see a resource not found error:
Matched Route: {controller}/{action}/{id}
Generated URL: /SiteRoot/Reports/Account/ResetPassword?un=whatever&rt=removedForSecurity using the route "Reports/{controller}/{action}/{id}"
Note that it is looking in the Reports area.  So, I changed my anchor tag construction in the ForgotPassword action, adding the Area specification in the route values object, as follows:
var resetLink = "<a href='" + Url.Action("ResetPassword", "Account", new {un = userName, rt = token, Area = ""}, "http") + "'>Reset Password</a>";
But it's still attempting to use the Reports area.  How can I assure that the anchor tag in the email will direct to the site root instead of an area?
UPDATE 2
I removed the HttpPost attribute, and am now getting the original 401 redirect.
UPDATE 3
ResetPassword view code:
@model Whatever.Web.Models.ResetPasswordViewModel
@{
    ViewBag.Title = "Reset Password";
}
@using (Html.BeginForm())
{
    <fieldset>
        <legend>Reset Password</legend>
        <table>
            <tr>
                <td>User name:</td>
                <td>@Html.TextBoxFor(m => m.UserName, new { @class="disabled-textbox", @readonly="readonly", autocomplete="off" })</td>
                <td></td>
            </tr>
            <tr>
                <td>New Password:</td>
                <td>@Html.PasswordFor(m => m.NewPassword, new { id = "resetPasswordNewPassword" })</td>
                <td></td>
            </tr>
            <tr>
                <td>Confirm New Password:</td>
                <td>@Html.Password("resetPasswordConfirmPassword", "", new { id = "resetPasswordConfirmPassword"})</td>
                <td>
                    <div id="passwordsMatch"></div>
                </td>
            </tr>
        </table>
        <input type="submit" id="submitButton" value="Submit" disabled="disabled"/>
        <div id="resetPasswordResultDiv"></div>
    </fieldset>
}
<script type="text/javascript">
    $(document).ready(function() {
        $("#resetPasswordNewPassword, #resetPasswordConfirmPassword").keyup(function() {
            if ($("#resetPasswordNewPassword").val().length > 0 && $("#resetPasswordConfirmPassword").val().length > 0 && $("#resetPasswordNewPassword").val() != $("#resetPasswordConfirmPassword").val()) {
                if ($("#resetPasswordNewPassword").val() != $("#resetPasswordConfirmPassword").val()) {
                    $("#passwordsMatch").html('<span style="color: red; font-weight: bold;>Passwords do not match</span>');
                    $("#submitButton").attr('disabled', true);
                } else {
                    $("#submitButton").removeAttr('disabled');
                }
            } else {
                $("#passwordsMatch").html('');
                $("#submitButton").attr('disabled', true);
            }
        });
    });
</script>
UPDATE 4
I tried adding another action method to the Account controller that results in a "hello world" style view.  Added [AllowAnonymous] attribute, of course.  This method also results in a 401 redirect to login.
I don't see any problems with the way you've got this set up. There has to be something else going on here. Check your _Layout.cshmtl file and assure that you don't have ANY calls to other actions that could be causing the 401.
This person had a similar problem: https://stackoverflow.com/a/11046666/199913
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With