using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.Owin.Security; using MvcMusicStore.Models; namespace MvcMusicStore.Controllers { [Authorize] public class AccountController : Controller { public enum ManageMessageId { ChangePasswordSuccess, SetPasswordSuccess, RemoveLoginSuccess, Error } private const string XsrfKey = "XsrfId"; private UserManager _userManager; public AccountController() : this(new UserManager(new UserStore(new ApplicationDbContext()))) { } public AccountController(UserManager userManager) { _userManager = userManager; } private IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } } private async Task MigrateShoppingCart(string userName) { using (var storeContext = new MusicStoreEntities()) { var cart = ShoppingCart.GetCart(storeContext, this); await cart.MigrateCart(userName); Session[ShoppingCart.CartSessionKey] = userName; } } // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task 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); } ModelState.AddModelError("", "Invalid username or password."); } return View(model); } // GET: /Account/Register [AllowAnonymous] public ActionResult Register() { return View(); } // POST: /Account/Register [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser { UserName = model.UserName }; var result = await _userManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInAsync(user, false); return RedirectToAction("Index", "Home"); } AddErrors(result); } return View(model); } // POST: /Account/Disassociate [HttpPost] [ValidateAntiForgeryToken] public async Task Disassociate(string loginProvider, string providerKey) { var result = await _userManager.RemoveLoginAsync( User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey)); return RedirectToAction( "Manage", new { Message = result.Succeeded ? ManageMessageId.RemoveLoginSuccess : ManageMessageId.Error }); } // GET: /Account/Manage public async Task Manage(ManageMessageId? message) { switch (message) { case ManageMessageId.ChangePasswordSuccess: ViewBag.StatusMessage = "Your password has been changed."; break; case ManageMessageId.SetPasswordSuccess: ViewBag.StatusMessage = "Your password has been set."; break; case ManageMessageId.RemoveLoginSuccess: ViewBag.StatusMessage = "The external login was removed."; break; case ManageMessageId.Error: ViewBag.StatusMessage = "An error has occurred."; break; default: ViewBag.StatusMessage = ""; break; } ViewBag.HasLocalPassword = await HasPasswordAsync(); ViewBag.ReturnUrl = Url.Action("Manage"); return View(); } // POST: /Account/Manage [HttpPost] [ValidateAntiForgeryToken] public async Task Manage(ManageUserViewModel model) { bool hasPassword = await HasPasswordAsync(); ViewBag.HasLocalPassword = hasPassword; ViewBag.ReturnUrl = Url.Action("Manage"); if (hasPassword) { if (ModelState.IsValid) { var result = await _userManager.ChangePasswordAsync( User.Identity.GetUserId(), model.OldPassword, model.NewPassword); if (result.Succeeded) { return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess }); } AddErrors(result); } } else { var state = ModelState["OldPassword"]; if (state != null) { state.Errors.Clear(); } if (ModelState.IsValid) { var result = await _userManager.AddPasswordAsync( User.Identity.GetUserId(), model.NewPassword); if (result.Succeeded) { return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess }); } AddErrors(result); } } return View(model); } // POST: /Account/ExternalLogin [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult ExternalLogin(string provider, string returnUrl) { return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); } // GET: /Account/ExternalLoginCallback [AllowAnonymous] public async Task ExternalLoginCallback(string returnUrl) { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { return RedirectToAction("Login"); } var user = await _userManager.FindAsync(loginInfo.Login); if (user != null) { await SignInAsync(user, false); return RedirectToLocal(returnUrl); } ViewBag.ReturnUrl = returnUrl; ViewBag.LoginProvider = loginInfo.Login.LoginProvider; return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName }); } // POST: /Account/LinkLogin [HttpPost] [ValidateAntiForgeryToken] public ActionResult LinkLogin(string provider) { return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId()); } // GET: /Account/LinkLoginCallback public async Task LinkLoginCallback() { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); if (loginInfo == null) { return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } var 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 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.UserName }; var result = await _userManager.CreateAsync(user); if (result.Succeeded) { result = await _userManager.AddLoginAsync(user.Id, info.Login); if (result.Succeeded) { await SignInAsync(user, false); 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 PartialView("_RemoveAccountPartial", linkedAccounts); } protected override void Dispose(bool disposing) { if (disposing && _userManager != null) { _userManager.Dispose(); _userManager = null; } base.Dispose(disposing); } private 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); await MigrateShoppingCart(user.UserName); } private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError("", error); } } private bool HasPassword() { var user = _userManager.FindById(User.Identity.GetUserId()); return user != null && user.PasswordHash != null; } private async Task HasPasswordAsync() { var user = await _userManager.FindByIdAsync(User.Identity.GetUserId()); return user != null && user.PasswordHash != null; } private ActionResult RedirectToLocal(string returnUrl) { return Url.IsLocalUrl(returnUrl) ? (ActionResult)Redirect(returnUrl) : RedirectToAction("Index", "Home"); } private class ChallengeResult : HttpUnauthorizedResult { private readonly string _loginProvider; private readonly string _redirectUri; private readonly string _userId; public ChallengeResult(string provider, string redirectUri, string userId = null) { _loginProvider = provider; _redirectUri = redirectUri; _userId = userId; } 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); } } } }