I have an MVC 5 Identity 2 app. I'm trying to change a user's password as follows:
public async Task<string> ChangePassword()
{
var user = await this.UserManager.FindByIdAsync(18);
PasswordHasher hasher = new PasswordHasher();
user.PasswordHash = hasher.HashPassword("NewPassword");
this.UserManager.Update(user);
return string.Empty;
}
this.UserManager is defined as:
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
The method executes successfully, but the password is not getting changed. Am I missing a step?
UserManager has these methods to change user's passwords:
public virtual Task<IdentityResult> ResetPasswordAsync(TKey userId, string token, string newPassword)
public virtual Task<IdentityResult> ChangePasswordAsync(TKey userId, string currentPassword, string newPassword);
protected virtual Task<IdentityResult> UpdatePassword(IUserPasswordStore<TUser, TKey> passwordStore, TUser user, string newPassword);
I believe UserManager has this handy method:
public virtual Task<IdentityResult> ChangePasswordAsync(
TKey userId,
string currentPassword,
string newPassword
)
EDIT:
This seems to be working for me. A UserManager property is defined as such:
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
Then, the password reset method:
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
Related
namespace AspWebAppTest.Controllers
{
public class AccountController : Controller
{
public IActionResult Login()
{
return View();
}
[HttpGet]
public IActionResult Login(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password))
{
return RedirectToAction("Login");
}
ClaimsIdentity identity = null;
bool isAuthenticated = false;
if (userName == "Admin" && password == "pass")
{
identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, "Admin")
}, CookieAuthenticationDefaults.AuthenticationScheme);
isAuthenticated = true;
}
if (isAuthenticated)
{
var principal = new ClaimsPrincipal(identity);
var login = HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return RedirectToAction("Mapping","Setting", "Home");
}
return View();
}
public IActionResult Logout()
{
var login = HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Login");
}
}
}
I have this Cookie Authentication Controller for my tabs(Mapping, and Config). I'am using RedirectToAction() method to redirect my return view to access mapping and config tab once the user entered the correct password and username. My problem is, after I put the password and username nothing is happening. Am I using the wrong method?
Here is my startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
The SignInAsync method returns a Task which will complete when the sign-in operation has succeeded.
Your code does not await this Task, so you're sending the redirection response before the user has been authenticated.
Make your actions async, and await the results of the Sign[In|Out]Async methods:
[HttpGet]
public async Task<IActionResult> Login(string userName, string password)
{
...
if (isAuthenticated)
{
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return RedirectToAction("Mapping", "Setting", "Home");
}
return View();
}
public async Task<IActionResult) Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Login");
}
I receive the following code error when trying to run my MVC application from localhost using Microsoft AD. When debugging the application I noticed that GetExternalLoginInfoAsync returns null. However, the application runs fine on the web. What am I missing? Somebody please help. I have posted my code below the error message.
Server Error in '/' Application.
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 140: {
Line 141: ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
Line 142: ClaimsIdentity claimsIdentity = loginInfo.ExternalIdentity;
Line 143: ApplicationUser applicationUser = new ApplicationUser(claimsIdentity);
Line 144: IdentityResult result = await UserManager.PersistAsync(applicationUser);
My code:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using SampleQuoteTracker.Models;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace SampleQuoteTracker.Controllers
{
/// <summary>
/// Provides methods for accepting and desplaying account authenication and creating new accounts.
/// </summary>
public class AccountController : Controller
{
//
// GET: /Account/Login
public ActionResult Login(string returnUrl)
{
ViewBag.Title = "Log In";
LoginViewModel loginViewModel = new LoginViewModel()
{
ReturnUrl = returnUrl
};
return View(loginViewModel);
}
//
// POST: /Account/Login
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<JsonResult> Login(LoginViewModel model)
{
// seed return object optimistically
AjaxResultModel loginResult = new AjaxResultModel
{
Status = "Valid",
ReturnUrl = GetLocalUrl(model.ReturnUrl),
Message = null
};
if (Request.IsAjaxRequest())
{
if (!ModelState.IsValid)
{
loginResult.Status = "Invalid";
loginResult.Message = Tools.ListModelStateErrors(ModelState);
}
if (loginResult.Status == "Valid")
{
SignInStatus result = await SignInManager.PasswordSignInAsync(
model.Email,
model.Password,
model.RememberMe,
shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
loginResult.Status = "Success";
break;
case SignInStatus.LockedOut:
loginResult.Status = "LockOut";
loginResult.Message = AlertMessages.AccountLockOut;
break;
case SignInStatus.Failure:
default:
loginResult.Status = "Failure";
loginResult.Message = AlertMessages.AuthenticationFailure;
break;
}
}
}
return Json(loginResult);
}
//
// POST: /Account/LogOff
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
//AuthenticationManager.SignOut();
//return RedirectToAction("SignOutCallback");
return null;
}
public void SignIn(string returnUrl = "/")
{
if (returnUrl == "/")
{
returnUrl = Request.ApplicationPath;
}
Uri baseUri = new UriBuilder(Request.Url.Scheme, Request.Url.Host, Request.Url.Port).Uri;
Uri uri = new Uri(baseUri, returnUrl);
// If this action is called and the user is already authenticated,
// it means the user is not a member of the appropriate role for
// the controller/action requested.
if (Request.IsAuthenticated)
{
RouteValueDictionary values = RouteDataContext.RouteValuesFromUri(uri);
string controllerName = (string)values["controller"];
string actionName = (string)values["action"];
string errorUrl = Url.Action("Error",
routeValues: new
{
message = "You are not authorized to view this content",
controllerName,
actionName
});
Response.Redirect(errorUrl, true);
}
else
{
// https://stackoverflow.com/a/21234614
// Activate the session before login to generate the authentication cookie correctly.
Session["Workaround"] = 0;
// Send an OpenID Connect sign-in request.
string externalLoginCallback = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
AuthenticationManager.Challenge(new AuthenticationProperties { RedirectUri = externalLoginCallback },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
//public IAuthenticationManager AuthenticationManager
//{
// get { return HttpContext.GetOwinContext().Authentication; }
//}
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
ClaimsIdentity claimsIdentity = loginInfo.ExternalIdentity;
ApplicationUser applicationUser = new ApplicationUser(claimsIdentity);
IdentityResult result = await UserManager.PersistAsync(applicationUser);
if (result.Succeeded)
{
claimsIdentity = await applicationUser.GenerateUserIdentityAsync(UserManager);
}
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = false }, claimsIdentity);
return Redirect(returnUrl);
}
[Authorize]
public void SignOut()
{
string callbackUrl = Url.Action("SignOutCallback", "Account", null, Request.Url.Scheme);
AuthenticationProperties properties = new AuthenticationProperties { RedirectUri = callbackUrl };
AuthenticationManager.SignOut(
properties,
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType,
Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
}
public ActionResult Error(string message, string controllerName = "Account", string actionName = "SignIn")
{
Exception exception = new Exception(message);
HandleErrorInfo handleErrorInfo = new HandleErrorInfo(exception, controllerName, actionName);
return View("Error", handleErrorInfo);
}
public ActionResult SignOutCallback()
{
if (Request.IsAuthenticated)
{
// Redirect to home page if the user is authenticated.
return RedirectToAction("Index", "Home");
}
return View();
}
protected override void Dispose(bool disposing)
{
if (disposing && UserManager != null)
{
UserManager.Dispose();
UserManager = null;
}
base.Dispose(disposing);
}
#region Helpers
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private IAuthenticationManager _authenticationManager;
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
/// <summary>
/// Gets a reference to the <see cref="ApplicationSignInManager"/>.
/// </summary>
protected ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set { _signInManager = value; }
}
protected ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
protected IAuthenticationManager AuthenticationManager
{
get
{
return _authenticationManager ?? HttpContext.GetOwinContext().Authentication;
}
private set
{
_authenticationManager = value;
}
}
/// <summary>
/// Ensures the <paramref name="returnUrl"/> belongs to this application.
/// <para>We don't want to redirect to a foreign page after authentication.</para>
/// </summary>
/// <param name="returnUrl">a <see cref="System.String"/> containing the page address that required authorization.</param>
/// <returns>a <see cref="System.String"/> containing a local page address.</returns>
private string GetLocalUrl(string returnUrl)
{
if (!Url.IsLocalUrl(returnUrl))
{
return Url.Action("Index", "Home");
}
return returnUrl;
}
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
private class RouteDataContext : HttpContextBase
{
public override HttpRequestBase Request { get; }
private RouteDataContext(Uri uri)
{
string url = uri.GetLeftPart(UriPartial.Path);
string qs = uri.GetComponents(UriComponents.Query, UriFormat.UriEscaped);
Request = new HttpRequestWrapper(new HttpRequest(null, url, qs));
}
public static RouteValueDictionary RouteValuesFromUri(Uri uri)
{
return RouteTable.Routes.GetRouteData(new RouteDataContext(uri)).Values;
}
}
#endregion
}
}
Eventually the ExternalCookie is removed when the Owin middleware inspects the context.
That way AuthenticationManager.GetExternalLoginInfo() returns null after logging in, the cookie holding the info has been removed and replaced by a ApplicationCookie.
So add the following in your Startup.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/LogOn")
});
....
}
For more details, you could refer to this article.
I'm struggling to show FirstName in my shared view. Please see my the code below and let me know where I got it wrong.
The AppUser class in IdentityModels has been extended to include FirstName.
In debug mode, var claim is null and I couldn't figure out why?
IdentityExtensions.cs
public static class IdentityExtensions
{
public static string FirstName(this IPrincipal usr)
{
var claim = ((ClaimsIdentity)usr.Identity).FindFirst("FirstName");
// Test for null to avoid issues during local testing
return (claim != null) ? claim.Value : string.Empty;
}
}
AppUser.cs
public class AppUser : IdentityUser
{
public string FirstName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<AppUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaim(new Claim("FirstName", FirstName));
userIdentity.AddClaim(new Claim("Surname", Surname));
return userIdentity;
}
}
In the view, I could see the method FirstName(), however it returns empty string
_Layout.cshtml
#HttpContext.Current.User.FirstName()
AccountController.cs
public class AccountController : Controller
{
public async Task<ActionResult> SignIn(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
}
CreateUserIdentityAsync was missing. By adding it, I managed to get the FirstName and other properties I added.
public class ApplicationSignInManager : SignInManager<AppUser, string>
{
public override Task<ClaimsIdentity> CreateUserIdentityAsync(AppUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
}
Thanks
simply just edit your Register Action to this
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = "MyFirstName" };
you have to set the value of the FirstName in your Register Action
Background
I have just created an MVC website (as an application) using Visual Studio 2013 Express for Web from the default MVC template using the "Individual user accounts" option. I have created views to fit and added two fields ("Username" and "Birthdate") to the accounts model (using the tutorials by Microsoft). Note: Everything works fine when running on localhost from visual studio.
The Problem
When I upload the site to my server, I get a file not found error (I think it is the accounts database that is not being found). When I upload it (I am doing this via the file system method), there is nothing in the AppData folder but there isn't in VS either, it's in the server explorer. If I try to do anything with the accounts on the live server (eg. register a user), I get the following stack trace.
The Stack Trace
Note: You can see this for yourself at spiderhouse.org if you register a user; I have left the full error details available and in it's current state (at the time of writing) no info will be saved.
The Identity Models
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace Spiderhouse.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public override string UserName { get; set; }
public DateTime BirthDate { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
The Account Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Owin;
using Spiderhouse.Models;
namespace Spiderhouse.Controllers
{
[Authorize]
public class AccountController : Controller
{
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
public ApplicationUserManager UserManager {
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName, Email = model.Email, BirthDate = model.BirthDate };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
// 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.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
IdentityResult result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return View("ConfirmEmail");
}
else
{
AddErrors(result);
return View();
}
}
//
// GET: /Account/ForgotPassword
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
}
//
// POST: /Account/ForgotPassword
[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)))
{
ModelState.AddModelError("", "The user either does not exist or is not confirmed.");
return View();
}
// 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(user.Id);
// var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking here");
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ForgotPasswordConfirmation
[AllowAnonymous]
public ActionResult ForgotPasswordConfirmation()
{
return View();
}
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
if (code == null)
{
return View("Error");
}
return View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
ModelState.AddModelError("", "No user found.");
return View();
}
IdentityResult result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
else
{
AddErrors(result);
return View();
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ResetPasswordConfirmation
[AllowAnonymous]
public ActionResult ResetPasswordConfirmation()
{
return View();
}
//
// POST: /Account/Disassociate
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
{
ManageMessageId? message = null;
IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
await SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("Manage", new { Message = message });
}
//
// GET: /Account/Manage
public ActionResult Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
ViewBag.HasLocalPassword = HasPassword();
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
//
// POST: /Account/Manage
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
bool hasPassword = HasPassword();
ViewBag.HasLocalPassword = hasPassword;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasPassword)
{
if (ModelState.IsValid)
{
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
else
{
// User does not have a password so remove any validation errors caused by a missing OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
else
{
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
else
{
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
//
// POST: /Account/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
}
//
// GET: /Account/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
if (result.Succeeded)
{
return RedirectToAction("Manage");
}
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Manage");
}
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
// 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.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// SendEmail(user.Email, callbackUrl, "Confirm your account", "Please confirm your account by clicking this link");
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
{
return View();
}
[ChildActionOnly]
public ActionResult RemoveAccountList()
{
var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
}
protected override void Dispose(bool disposing)
{
if (disposing && UserManager != null)
{
UserManager.Dispose();
UserManager = null;
}
base.Dispose(disposing);
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, await user.GenerateUserIdentityAsync(UserManager));
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
}
private void SendEmail(string email, string callbackUrl, string subject, string message)
{
// For information on sending mail, please visit http://go.microsoft.com/fwlink/?LinkID=320771
}
public enum ManageMessageId
{
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
Error
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
{
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
#endregion
}
}
The Connection Strings
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-Spiderhouse-20141120093225.mdf;Initial Catalog=aspnet-Spiderhouse-20141120093225;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
This first line of the exception you mentioned i would get is saying that you don't have SQL express installed on the prod server.
SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 52 - Unable to locate a Local Database Runtime installation. Verify that SQL Server Express is properly installed and that the Local Database Runtime feature is enabled.)
Obviously you will need to have that installed in order for your app to work on the server with the .mdf file that you put out. I'm assuming that when you say you "see nothing in the App_Data folder but you do in the server explorer" you are looking through the Visual Studio Solution Explorer into the App_Data folder, correct? If this is true, I also didn't see anything there, but physically going to the directory in file explorer showed the .mdf and .ldf files that the mvc site created. If you do have SQL Express installed on the server, maybe you forgot to put these files in the correct location on the web server.
In my project I opted to use a remote installation of SQL Server full, in which case I needed to change where the connection string was pointing, but because you included that in the information on your post I think you know that.
Totally messed up my last question so posting a new one.
MyTestController:
[HttpPost]
public async Task<ActionResult> Index(MyTestViewModel viewModel)
{
if (ModelState.IsValid)
{
AccountController ac = new AccountController();
var user = new ApplicationUser()
{
UserName = viewModel.Email
};
var result = await ac.UserManager.CreateAsync(user, viewModel.Password);
if (result.Succeeded)
{
await ac.SignInAsync(user, isPersistent: true);
}
else
{
ac.AddErrors(result);
}
The SignInAsync method in AccountController (changed this from private to public):
public async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
While trying to register the user it gives me the following error:
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 411: get
Line 412: {
Line 413: return HttpContext.GetOwinContext().Authentication;
Line 414: }
Line 415: }
Those lines in AccountController:
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
Everything in the AccountController is default MVC 5 app stuff.
It is not possible to call these methods from another controller, like in my example above?
And why am I getting a NullReferenceException on line 413?
Calling a Controller method from another Controller is difficult because of properties like the HttpContext which need to be properly initialized. This is usually done by the MVC framework which creates the controller using a ControllerFactory and at some point during this process the protected method Initialize is called on the controller which ensures that the HttpContext property is set.
This is why you get the exception on line 413, because the Initialize method hasn't been called on the controller you created using the new operator.
I think it would be easier to refactor out the functionality you want to share.
E.g. if both AccountController and your MyTestController holds a reference to something like this
public class AccountManager
{
public UserManager<ApplicationUser> UserManager { get; private set; }
public HttpContextBase HttpContext { get; private set; }
public AccountManager()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountManager(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public void Initialize(HttpContextBase context)
{
HttpContext = context;
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
public async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
}
You would then modify the AccountController like this:
public class AccountController : Controller
{
public AccountController()
: this(new AccountManager())
{
}
public AccountController(AccountManager accountManager)
{
AccountManager = accountManager;
}
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
AccountManager.Initialize(this.HttpContext);
}
public UserManager<ApplicationUser> UserManager
{
get
{
return AccountManager.UserManager;
}
}
public AccountManager AccountManager { get; private set; }
And your MyTestController would be like this:
public class MyTestController : Controller
{
public MyTestController ()
: this(new AccountManager())
{
}
public MyTestController (AccountManager accountManager)
{
AccountManager = accountManager;
}
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
AccountManager.Initialize(this.HttpContext);
}
public AccountManager AccountManager { get; private set; }
[HttpPost]
public async Task<ActionResult> Index(MyTestViewModel viewModel)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser()
{
UserName = viewModel.Email
};
var result = await AccountManager.UserManager.CreateAsync(user, viewModel.Password);
if (result.Succeeded)
{
await AccountManager.SignInAsync(user, isPersistent: true);
}
else
{
AddErrors(result); //don't want to share this a it updates ModelState which belongs to the controller.
}
Update:
Had to make som minor changes:
I had to change the UserManager property since the Dispose method uses the setter method:
private UserManager<ApplicationUser> _userManager;
public UserManager<ApplicationUser> UserManager
{
get { return AccountManager.UserManager; }
private set { _userManager = value; }
}
protected override void Dispose(bool disposing)
{
if (disposing && UserManager != null)
{
UserManager.Dispose();
UserManager = null;
}
base.Dispose(disposing);
}
I had to add the AddErrors method to MyTestController (as you pointed out that we don't want to share that method):
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
I re-added this line to the AccountManager property in the AccountManager class (not really related to the question but I had it in my project)
UserManager.UserValidator = new UserValidator(UserManager) { AllowOnlyAlphanumericUserNames = false };
For me it working like a charm after setting the current ControllerContext to AccountControllerContext. Not sure if there are any drawbacks with this approach.
//This is employee controller class
public ActionResult Create([Bind(Include = "EmployeeId,FirstName,LastName,DOJ,DOB,Address,City,State,Mobile,Landline,ReportsTo,Salary")] Employee employee)
{
if (ModelState.IsValid)
{
AccountController accountController = new AccountController();
accountController.ControllerContext = this.ControllerContext;
//accountController.UserManager;
var userId = accountController.RegisterAccount(new RegisterViewModel { Email = "temp#temp.com", Password = "Pravallika!23" });
if (!string.IsNullOrEmpty(userId))
{
employee.UserId = userId;
employee.CreatedBy = User.Identity.GetUserId();
db.Employees.Add(employee);
db.SaveChanges();
return RedirectToAction("Index");
}
}
//customized method in AccountController
public string RegisterAccount(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = UserManager.Create(user, model.Password);
//to add roles
//UserManager.AddToRole(user.Id, "Admin");
if (result.Succeeded)
{
return user.Id;
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed
return null;
}