I am trying to migrate an MVC4 application from ASP.NET membership to the new SimpleMembership and have encountered a password reset problem. In my login mode, I have a "Forgot Password" hyperlink that makes an AJAX message to an action that sends an email with a reset password hyperlink (based on the MSDN Recommendation ):
[HttpPost]
[AllowAnonymous]
public ActionResult ForgotPassword(string userName)
{
var token = WebSecurity.GeneratePasswordResetToken(userName);
var resetLink = "<a href='" + Url.Action("ResetPassword", "Account", new {un = userName, rt = token}, "http") + "'>Reset Password</a>";
return Json(new {result = "Email sent."});
}
This works fine, but for some reason, when I try to use the reset Password link, I get redirected to the login screen, and Fiddler shows 401-Unauthorized. I tried to copy / paste the link into the address bar of the browser, and also create an unwanted HTML file with the anchor tag in it, but the result is still 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 tried to remove the attribute HttpPost, but that also gives 401. What am I missing here?
UPDATE
Thanks to Steve's comment, I fully qualified the attribute HttpPostand now I understand that it is trying to send to the area instead of directly accessing the root controller Account. Using RouteDebugger , I see that the resource was not found:
Matched Route: {controller}/{action}/{id}
Generated URL: /SiteRoot/Reports/Account/ResetPassword?un=whatever&rt=removedForSecurity using the route "Reports/{controller}/{action}/{id}"
, Reports. , ForgotPassword, Area :
var resetLink = "<a href='" + Url.Action("ResetPassword", "Account", new {un = userName, rt = token, Area = ""}, "http") + "'>Reset Password</a>";
Reports. , ?
2
HttpPost, 401.
3
ResetPassword :
@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>
4
, " ". [AllowAnonymous], . 401 .