using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using UnicornStore.Models; namespace UnicornStore.Controllers { [Authorize] public class ManageController : Controller { public ManageController( UserManager userManager, SignInManager signInManager, IAuthenticationSchemeProvider schemes) { UserManager = userManager; SignInManager = signInManager; SchemeProvider = schemes; } public UserManager UserManager { get; } public SignInManager SignInManager { get; } public IAuthenticationSchemeProvider SchemeProvider { get; } // // GET: /Manage/Index public async Task Index(ManageMessageId? message = null) { ViewBag.StatusMessage = message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." : message == ManageMessageId.Error ? "An error has occurred." : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." : ""; var user = await GetCurrentUserAsync(); var model = new IndexViewModel { HasPassword = await UserManager.HasPasswordAsync(user), PhoneNumber = await UserManager.GetPhoneNumberAsync(user), TwoFactor = await UserManager.GetTwoFactorEnabledAsync(user), Logins = await UserManager.GetLoginsAsync(user), BrowserRemembered = await SignInManager.IsTwoFactorClientRememberedAsync(user) }; return View(model); } // // POST: /Manage/RemoveLogin [HttpPost] [ValidateAntiForgeryToken] public async Task RemoveLogin(string loginProvider, string providerKey) { ManageMessageId? message = ManageMessageId.Error; var user = await GetCurrentUserAsync(); if (user != null) { var result = await UserManager.RemoveLoginAsync(user, loginProvider, providerKey); if (result.Succeeded) { await SignInManager.SignInAsync(user, isPersistent: false); message = ManageMessageId.RemoveLoginSuccess; } } return RedirectToAction("ManageLogins", new { Message = message }); } // // GET: /Account/AddPhoneNumber public IActionResult AddPhoneNumber() { return View(); } // // POST: /Account/AddPhoneNumber [HttpPost] [ValidateAntiForgeryToken] public async Task AddPhoneNumber(AddPhoneNumberViewModel model) { if (!ModelState.IsValid) { return View(model); } var user = await GetCurrentUserAsync(); // Generate the token and send it var code = await UserManager.GenerateChangePhoneNumberTokenAsync(user, model.Number); await MessageServices.SendSmsAsync(model.Number, "Your security code is: " + code); return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number }); } // // POST: /Manage/EnableTwoFactorAuthentication [HttpPost] [ValidateAntiForgeryToken] public async Task EnableTwoFactorAuthentication() { var user = await GetCurrentUserAsync(); if (user != null) { await UserManager.SetTwoFactorEnabledAsync(user, true); // TODO: flow remember me somehow? await SignInManager.SignInAsync(user, isPersistent: false); } return RedirectToAction("Index", "Manage"); } // // POST: /Manage/DisableTwoFactorAuthentication [HttpPost] [ValidateAntiForgeryToken] public async Task DisableTwoFactorAuthentication() { var user = await GetCurrentUserAsync(); if (user != null) { await UserManager.SetTwoFactorEnabledAsync(user, false); await SignInManager.SignInAsync(user, isPersistent: false); } return RedirectToAction("Index", "Manage"); } // // GET: /Account/VerifyPhoneNumber public async Task VerifyPhoneNumber(string phoneNumber) { // This code allows you exercise the flow without actually sending codes // For production use please register a SMS provider in IdentityConfig and generate a code here. #if DEMO ViewBag.Code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); #endif return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); } // // POST: /Account/VerifyPhoneNumber [HttpPost] [ValidateAntiForgeryToken] public async Task VerifyPhoneNumber(VerifyPhoneNumberViewModel model) { if (!ModelState.IsValid) { return View(model); } var user = await GetCurrentUserAsync(); if (user != null) { var result = await UserManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); if (result.Succeeded) { await SignInManager.SignInAsync(user, isPersistent: false); return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess }); } } // If we got this far, something failed, redisplay form ModelState.AddModelError("", "Failed to verify phone"); return View(model); } // // GET: /Account/RemovePhoneNumber [HttpPost] [ValidateAntiForgeryToken] public async Task RemovePhoneNumber() { var user = await GetCurrentUserAsync(); if (user != null) { var result = await UserManager.SetPhoneNumberAsync(user, null); if (result.Succeeded) { await SignInManager.SignInAsync(user, isPersistent: false); return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess }); } } return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); } // // GET: /Manage/ChangePassword public IActionResult ChangePassword() { return View(); } // // POST: /Account/Manage [HttpPost] [ValidateAntiForgeryToken] public async Task ChangePassword(ChangePasswordViewModel model) { if (!ModelState.IsValid) { return View(model); } var user = await GetCurrentUserAsync(); if (user != null) { var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); if (result.Succeeded) { await SignInManager.SignInAsync(user, isPersistent: false); return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess }); } AddErrors(result); return View(model); } return RedirectToAction("Index", new { Message = ManageMessageId.Error }); } // // GET: /Manage/SetPassword public IActionResult SetPassword() { return View(); } // // POST: /Manage/SetPassword [HttpPost] [ValidateAntiForgeryToken] public async Task SetPassword(SetPasswordViewModel model) { if (!ModelState.IsValid) { return View(model); } var user = await GetCurrentUserAsync(); if (user != null) { var result = await UserManager.AddPasswordAsync(user, model.NewPassword); if (result.Succeeded) { await SignInManager.SignInAsync(user, isPersistent: false); return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess }); } AddErrors(result); return View(model); } return RedirectToAction("Index", new { Message = ManageMessageId.Error }); } // // POST: /Manage/RememberBrowser [HttpPost] [ValidateAntiForgeryToken] public async Task RememberBrowser() { var user = await GetCurrentUserAsync(); if (user != null) { await SignInManager.RememberTwoFactorClientAsync(user); await SignInManager.SignInAsync(user, isPersistent: false); } return RedirectToAction("Index", "Manage"); } // // POST: /Manage/ForgetBrowser [HttpPost] [ValidateAntiForgeryToken] public async Task ForgetBrowser() { await SignInManager.ForgetTwoFactorClientAsync(); return RedirectToAction("Index", "Manage"); } // // GET: /Account/Manage public async Task ManageLogins(ManageMessageId? message = null) { ViewBag.StatusMessage = message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." : message == ManageMessageId.AddLoginSuccess ? "The external login was added." : message == ManageMessageId.Error ? "An error has occurred." : ""; var user = await GetCurrentUserAsync(); if (user == null) { return View("Error"); } var userLogins = await UserManager.GetLoginsAsync(user); var schemes = await SchemeProvider.GetAllSchemesAsync(); var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList(); ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1; return View(new ManageLoginsViewModel { CurrentLogins = userLogins, OtherLogins = otherLogins }); } // // POST: /Manage/LinkLogin [HttpPost] [ValidateAntiForgeryToken] public ActionResult LinkLogin(string provider) { // Request a redirect to the external login provider to link a login for the current user var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, UserManager.GetUserId(User)); return new ChallengeResult(provider, properties); } // // GET: /Manage/LinkLoginCallback public async Task LinkLoginCallback() { var user = await GetCurrentUserAsync(); if (user == null) { return View("Error"); } var loginInfo = await SignInManager.GetExternalLoginInfoAsync(await UserManager.GetUserIdAsync(user)); if (loginInfo == null) { return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error }); } var result = await UserManager.AddLoginAsync(user, loginInfo); var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; return RedirectToAction("ManageLogins", new { Message = message }); } #region Helpers private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError("", error.Description); } } public enum ManageMessageId { AddPhoneSuccess, AddLoginSuccess, ChangePasswordSuccess, SetTwoFactorSuccess, SetPasswordSuccess, RemoveLoginSuccess, RemovePhoneSuccess, Error } private Task GetCurrentUserAsync() { return UserManager.GetUserAsync(HttpContext.User); } #endregion } }