Updating the sample to use VS 2013 Update 3 templates for Identity
1. This contains the new manage controller with a bunch of features like 2 factor auth Some of the features are not enabled yet. But this is to set a baseline with update3 templates. Automation to come up in future as more identity extensions are available.
This commit is contained in:
parent
05cd84667a
commit
9f4d46714b
|
|
@ -1,8 +1,11 @@
|
||||||
using Microsoft.AspNet.Identity;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Mvc;
|
|
||||||
using MusicStore.Models;
|
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using MusicStore.Models;
|
||||||
|
|
||||||
namespace MusicStore.Controllers
|
namespace MusicStore.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -35,25 +38,78 @@ namespace MusicStore.Controllers
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
var signInStatus = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
|
return View(model);
|
||||||
switch (signInStatus)
|
|
||||||
{
|
|
||||||
case SignInStatus.Success:
|
|
||||||
return RedirectToLocal(returnUrl);
|
|
||||||
case SignInStatus.LockedOut:
|
|
||||||
ModelState.AddModelError("", "User is locked out, try again later.");
|
|
||||||
return View(model);
|
|
||||||
case SignInStatus.Failure:
|
|
||||||
default:
|
|
||||||
ModelState.AddModelError("", "Invalid username or password.");
|
|
||||||
return View(model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we got this far, something failed, redisplay form
|
// This doesn't count login failures towards account lockout
|
||||||
return View(model);
|
// To enable password failures to trigger account lockout, change to shouldLockout: true
|
||||||
|
var signInStatus = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
|
||||||
|
switch (signInStatus)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/VerifyCode
|
||||||
|
//TODO : Some of the identity helpers not implemented
|
||||||
|
//[AllowAnonymous]
|
||||||
|
//public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
|
||||||
|
//{
|
||||||
|
// // Require that the user has already logged in via username/password or external login
|
||||||
|
// if (!await SignInManager.HasBeenVerifiedAsync())
|
||||||
|
// {
|
||||||
|
// return View("Error");
|
||||||
|
// }
|
||||||
|
// var user = await UserManager.FindByIdAsync(await SignInManager.GetVerifiedUserIdAsync());
|
||||||
|
// if (user != null)
|
||||||
|
// {
|
||||||
|
// var code = await UserManager.GenerateTwoFactorTokenAsync(user.Id, provider);
|
||||||
|
// }
|
||||||
|
// return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||||
|
//}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/VerifyCode
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following code protects for brute force attacks against the two factor codes.
|
||||||
|
// If a user enters incorrect codes for a specified amount of time then the user account
|
||||||
|
// will be locked out for a specified amount of time.
|
||||||
|
// You can configure the account lockout settings in IdentityConfig
|
||||||
|
// TODO : This helper does not take in the remember browser option yet.
|
||||||
|
// var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
|
||||||
|
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe);
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case SignInStatus.Success:
|
||||||
|
return RedirectToLocal(model.ReturnUrl);
|
||||||
|
case SignInStatus.LockedOut:
|
||||||
|
return View("Lockout");
|
||||||
|
case SignInStatus.Failure:
|
||||||
|
default:
|
||||||
|
ModelState.AddModelError("", "Invalid code.");
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -73,17 +129,26 @@ namespace MusicStore.Controllers
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
var user = new ApplicationUser { UserName = model.UserName };
|
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
|
||||||
var result = await UserManager.CreateAsync(user, model.Password);
|
var result = await UserManager.CreateAsync(user, model.Password);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
|
//Bug: Remember browser option missing?
|
||||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
await SignInManager.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);
|
||||||
|
//var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
|
||||||
|
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
|
||||||
|
// return RedirectToAction("Index", "Home");
|
||||||
|
|
||||||
|
// TODO: Email libraries not available on Coreclr yet - Checkout a demo implementation below which displays the code in the browser for testing.
|
||||||
|
//ViewBag.Status = "Navigate to this URL to confirm your account " + callbackUrl;
|
||||||
|
//return View("DemoCodeDisplay");
|
||||||
return RedirectToAction("Index", "Home");
|
return RedirectToAction("Index", "Home");
|
||||||
}
|
}
|
||||||
else
|
AddErrors(result);
|
||||||
{
|
|
||||||
AddErrors(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we got this far, something failed, redisplay form
|
// If we got this far, something failed, redisplay form
|
||||||
|
|
@ -91,42 +156,234 @@ namespace MusicStore.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/Manage
|
// GET: /Account/ConfirmEmail
|
||||||
public IActionResult Manage(ManageMessageId? message = null)
|
//TODO: This does not work yet due to some missing identity features
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<ActionResult> ConfirmEmail(string userId, string code)
|
||||||
|
{
|
||||||
|
if (userId == null || code == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
var user = new ApplicationUser { Id = userId };
|
||||||
|
//Bug: Throws NullRefException
|
||||||
|
var result = await UserManager.ConfirmEmailAsync(user, code);
|
||||||
|
return View(result.Succeeded ? "ConfirmEmail" : "Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ForgotPassword
|
||||||
|
[AllowAnonymous]
|
||||||
|
public ActionResult ForgotPassword()
|
||||||
{
|
{
|
||||||
ViewBag.StatusMessage =
|
|
||||||
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
|
|
||||||
: message == ManageMessageId.Error ? "An error has occurred."
|
|
||||||
: "";
|
|
||||||
ViewBag.ReturnUrl = Url.Action("Manage");
|
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// POST: /Account/Manage
|
// POST: /Account/ForgotPassword
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Manage(ManageUserViewModel model)
|
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
|
||||||
{
|
{
|
||||||
ViewBag.ReturnUrl = Url.Action("Manage");
|
|
||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
var user = await GetCurrentUserAsync();
|
var user = await UserManager.FindByNameAsync(model.Email);
|
||||||
var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
|
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user)))
|
||||||
if (result.Succeeded)
|
|
||||||
{
|
{
|
||||||
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
|
// Don't reveal that the user does not exist or is not confirmed
|
||||||
}
|
return View("ForgotPasswordConfirmation");
|
||||||
else
|
|
||||||
{
|
|
||||||
AddErrors(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
var callbackUrl = Url.Action("ResetPassword", "Account", new { code = code }, protocol: Context.Request.Scheme);
|
||||||
|
// No libraries to send email on CoreCLR yet
|
||||||
|
// await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
|
||||||
|
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
|
||||||
|
|
||||||
|
// TODO: Email libraries not available on Coreclr yet - Checkout a demo implementation below which displays the code in the browser for testing.
|
||||||
|
ViewBag.Status = "Navigate to the URL to reset password " + callbackUrl;
|
||||||
|
return View("DemoCodeDisplay");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelState.AddModelError("", string.Format("We could not locate an account with email : {0}", model.Email));
|
||||||
|
|
||||||
// If we got this far, something failed, redisplay form
|
// If we got this far, something failed, redisplay form
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ForgotPasswordConfirmation
|
||||||
|
[AllowAnonymous]
|
||||||
|
public ActionResult ForgotPasswordConfirmation()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ResetPassword
|
||||||
|
[AllowAnonymous]
|
||||||
|
public ActionResult ResetPassword(string code)
|
||||||
|
{
|
||||||
|
var resetPasswordViewModel = new ResetPasswordViewModel() { Code = code };
|
||||||
|
return code == null ? View("Error") : View(resetPasswordViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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, model.Code, model.Password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return RedirectToAction("ResetPasswordConfirmation", "Account");
|
||||||
|
}
|
||||||
|
AddErrors(result);
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ResetPasswordConfirmation
|
||||||
|
[AllowAnonymous]
|
||||||
|
public ActionResult ResetPasswordConfirmation()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/ExternalLogin
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public ActionResult ExternalLogin(string provider, string returnUrl)
|
||||||
|
{
|
||||||
|
// Request a redirect to the external login provider
|
||||||
|
var redirectUri = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
|
||||||
|
return new ChallengeResult(provider, new AuthenticationProperties() { RedirectUri = redirectUri });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/SendCode
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<ActionResult> SendCode(bool rememberMe, string returnUrl = null)
|
||||||
|
{
|
||||||
|
// TODO: This currently throws
|
||||||
|
var userId = await GetCurrentUserAsync();
|
||||||
|
if (userId == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
|
||||||
|
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
|
||||||
|
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/SendCode
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<ActionResult> SendCode(SendCodeViewModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the token and send it
|
||||||
|
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ExternalLoginCallback
|
||||||
|
// TODO: Some identity helpers on external login does not exist
|
||||||
|
//[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 result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: 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 = false });
|
||||||
|
// case SignInStatus.Failure:
|
||||||
|
// default:
|
||||||
|
// // 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/ExternalLoginConfirmation
|
||||||
|
// TODO: Some of the identity helpers not available
|
||||||
|
//[HttpPost]
|
||||||
|
//[AllowAnonymous]
|
||||||
|
//[ValidateAntiForgeryToken]
|
||||||
|
//public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
|
||||||
|
//{
|
||||||
|
// if (User.Identity.IsAuthenticated)
|
||||||
|
// {
|
||||||
|
// return RedirectToAction("Index", "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 };
|
||||||
|
// var result = await UserManager.CreateAsync(user);
|
||||||
|
// if (result.Succeeded)
|
||||||
|
// {
|
||||||
|
// result = await UserManager.AddLoginAsync(user.Id, info.Login);
|
||||||
|
// if (result.Succeeded)
|
||||||
|
// {
|
||||||
|
// // TODO: rememberBrowser option not being taken in SignInAsync
|
||||||
|
// await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
|
||||||
|
// return RedirectToLocal(returnUrl);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// AddErrors(result);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ViewBag.ReturnUrl = returnUrl;
|
||||||
|
// return View(model);
|
||||||
|
//}
|
||||||
|
|
||||||
//
|
//
|
||||||
// POST: /Account/LogOff
|
// POST: /Account/LogOff
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
|
@ -137,6 +394,14 @@ namespace MusicStore.Controllers
|
||||||
return RedirectToAction("Index", "Home");
|
return RedirectToAction("Index", "Home");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ExternalLoginFailure
|
||||||
|
[AllowAnonymous]
|
||||||
|
public ActionResult ExternalLoginFailure()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
#region Helpers
|
#region Helpers
|
||||||
|
|
||||||
private void AddErrors(IdentityResult result)
|
private void AddErrors(IdentityResult result)
|
||||||
|
|
@ -152,13 +417,7 @@ namespace MusicStore.Controllers
|
||||||
return await UserManager.FindByIdAsync(Context.User.Identity.GetUserId());
|
return await UserManager.FindByIdAsync(Context.User.Identity.GetUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ManageMessageId
|
private ActionResult RedirectToLocal(string returnUrl)
|
||||||
{
|
|
||||||
ChangePasswordSuccess,
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
|
|
||||||
private IActionResult RedirectToLocal(string returnUrl)
|
|
||||||
{
|
{
|
||||||
if (Url.IsLocalUrl(returnUrl))
|
if (Url.IsLocalUrl(returnUrl))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,348 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using MusicStore.Models;
|
||||||
|
|
||||||
|
namespace MusicStore.Controllers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Summary description for ManageController
|
||||||
|
/// </summary>
|
||||||
|
public class ManageController : Controller
|
||||||
|
{
|
||||||
|
public ManageController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||||
|
{
|
||||||
|
UserManager = userManager;
|
||||||
|
SignInManager = signInManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserManager<ApplicationUser> UserManager { get; private set; }
|
||||||
|
|
||||||
|
public SignInManager<ApplicationUser> SignInManager { get; private set; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Manage/Index
|
||||||
|
public async Task<ActionResult> 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<IActionResult> 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<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
// Generate the token and send it
|
||||||
|
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), model.Number);
|
||||||
|
if (UserManager.SmsService != null)
|
||||||
|
{
|
||||||
|
var message = new IdentityMessage
|
||||||
|
{
|
||||||
|
Destination = model.Number,
|
||||||
|
Body = "Your security code is: " + code
|
||||||
|
};
|
||||||
|
await UserManager.SmsService.SendAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Manage/EnableTwoFactorAuthentication
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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.
|
||||||
|
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
|
||||||
|
ViewBag.Status = "For DEMO purposes only, the current code is " + code;
|
||||||
|
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/VerifyPhoneNumber
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> 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
|
||||||
|
public async Task<IActionResult> 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("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Manage/ChangePassword
|
||||||
|
public IActionResult ChangePassword()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/Manage
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> RememberBrowser()
|
||||||
|
{
|
||||||
|
var user = await GetCurrentUserAsync();
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
await SignInManager.RememberTwoFactorClient(user);
|
||||||
|
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
}
|
||||||
|
return RedirectToAction("Index", "Manage");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Manage/ForgetBrowser
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> ForgetBrowser()
|
||||||
|
{
|
||||||
|
await SignInManager.ForgetTwoFactorClientAsync();
|
||||||
|
return RedirectToAction("Index", "Manage");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/Manage
|
||||||
|
//public async Task<IActionResult> ManageLogins(ManageMessageId? message)
|
||||||
|
//{
|
||||||
|
// ViewBag.StatusMessage =
|
||||||
|
// message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
|
||||||
|
// : message == ManageMessageId.Error ? "An error has occurred."
|
||||||
|
// : "";
|
||||||
|
// var user = await GetCurrentUserAsync();
|
||||||
|
// if (user == null)
|
||||||
|
// {
|
||||||
|
// return View("Error");
|
||||||
|
// }
|
||||||
|
// var userLogins = await UserManager.GetLoginsAsync(user);
|
||||||
|
//var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
|
||||||
|
//ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
|
||||||
|
//return View(new ManageLoginsViewModel
|
||||||
|
//{
|
||||||
|
// CurrentLogins = userLogins,
|
||||||
|
// OtherLogins = otherLogins
|
||||||
|
//});
|
||||||
|
//}
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
private void AddErrors(IdentityResult result)
|
||||||
|
{
|
||||||
|
foreach (var error in result.Errors)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> HasPhoneNumber()
|
||||||
|
{
|
||||||
|
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
return user.PhoneNumber != null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ManageMessageId
|
||||||
|
{
|
||||||
|
AddPhoneSuccess,
|
||||||
|
ChangePasswordSuccess,
|
||||||
|
SetTwoFactorSuccess,
|
||||||
|
SetPasswordSuccess,
|
||||||
|
RemoveLoginSuccess,
|
||||||
|
RemovePhoneSuccess,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ApplicationUser> GetCurrentUserAsync()
|
||||||
|
{
|
||||||
|
return await UserManager.FindByIdAsync(Context.User.Identity.GetUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActionResult RedirectToLocal(string returnUrl)
|
||||||
|
{
|
||||||
|
if (Url.IsLocalUrl(returnUrl))
|
||||||
|
{
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RedirectToAction("Index", "Home");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"DefaultAdminUsername": "Administrator",
|
"DefaultAdminUsername": "Administrator@test.com",
|
||||||
"DefaultAdminPassword": "YouShouldChangeThisPassword1!",
|
"DefaultAdminPassword": "YouShouldChangeThisPassword1!",
|
||||||
"Data": {
|
"Data": {
|
||||||
"DefaultConnection": {
|
"DefaultConnection": {
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,58 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace MusicStore.Models
|
namespace MusicStore.Models
|
||||||
{
|
{
|
||||||
public class ExternalLoginConfirmationViewModel
|
public class ExternalLoginConfirmationViewModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[Display(Name = "User name")]
|
[Display(Name = "Email")]
|
||||||
public string UserName { get; set; }
|
public string Email { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ManageUserViewModel
|
public class ExternalLoginListViewModel
|
||||||
|
{
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SendCodeViewModel
|
||||||
|
{
|
||||||
|
public string SelectedProvider { get; set; }
|
||||||
|
public ICollection<SelectListItem> Providers { get; set; }
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VerifyCodeViewModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[DataType(DataType.Password)]
|
public string Provider { get; set; }
|
||||||
[Display(Name = "Current password")]
|
|
||||||
public string OldPassword { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
[Display(Name = "Code")]
|
||||||
[DataType(DataType.Password)]
|
public string Code { get; set; }
|
||||||
[Display(Name = "New password")]
|
public string ReturnUrl { get; set; }
|
||||||
public string NewPassword { get; set; }
|
|
||||||
|
|
||||||
[DataType(DataType.Password)]
|
[Display(Name = "Remember this browser?")]
|
||||||
[Display(Name = "Confirm new password")]
|
public bool RememberBrowser { get; set; }
|
||||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
|
||||||
public string ConfirmPassword { get; set; }
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ForgotViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LoginViewModel
|
public class LoginViewModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[Display(Name = "User name")]
|
[Display(Name = "Email")]
|
||||||
public string UserName { get; set; }
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[DataType(DataType.Password)]
|
[DataType(DataType.Password)]
|
||||||
|
|
@ -46,8 +66,9 @@ namespace MusicStore.Models
|
||||||
public class RegisterViewModel
|
public class RegisterViewModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[Display(Name = "User name")]
|
[EmailAddress]
|
||||||
public string UserName { get; set; }
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
|
@ -60,4 +81,33 @@ namespace MusicStore.Models
|
||||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||||
public string ConfirmPassword { get; set; }
|
public string ConfirmPassword { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ResetPasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Password")]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm password")]
|
||||||
|
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
|
||||||
|
public string Code { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ForgotPasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
||||||
|
namespace MusicStore.Models
|
||||||
|
{
|
||||||
|
public class IndexViewModel
|
||||||
|
{
|
||||||
|
public bool HasPassword { get; set; }
|
||||||
|
public IList<UserLoginInfo> Logins { get; set; }
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
public bool TwoFactor { get; set; }
|
||||||
|
public bool BrowserRemembered { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ManageLoginsViewModel
|
||||||
|
{
|
||||||
|
public IList<UserLoginInfo> CurrentLogins { get; set; }
|
||||||
|
public IList<AuthenticationDescription> OtherLogins { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FactorViewModel
|
||||||
|
{
|
||||||
|
public string Purpose { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SetPasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "New password")]
|
||||||
|
public string NewPassword { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm new password")]
|
||||||
|
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangePasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Current password")]
|
||||||
|
public string OldPassword { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "New password")]
|
||||||
|
public string NewPassword { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm new password")]
|
||||||
|
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddPhoneNumberViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Phone]
|
||||||
|
[Display(Name = "Phone Number")]
|
||||||
|
public string Number { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VerifyPhoneNumberViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Code")]
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[Phone]
|
||||||
|
[Display(Name = "Phone Number")]
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConfigureTwoFactorViewModel
|
||||||
|
{
|
||||||
|
public string SelectedProvider { get; set; }
|
||||||
|
public ICollection<SelectListItem> Providers { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,9 +53,7 @@
|
||||||
<Content Include="Scripts\respond.min.js" />
|
<Content Include="Scripts\respond.min.js" />
|
||||||
<Content Include="Scripts\_references.js" />
|
<Content Include="Scripts\_references.js" />
|
||||||
<Content Include="Views\Account\Login.cshtml" />
|
<Content Include="Views\Account\Login.cshtml" />
|
||||||
<Content Include="Views\Account\Manage.cshtml" />
|
|
||||||
<Content Include="Views\Account\Register.cshtml" />
|
<Content Include="Views\Account\Register.cshtml" />
|
||||||
<Content Include="Views\Account\_ChangePasswordPartial.cshtml" />
|
|
||||||
<Content Include="Views\Checkout\AddressAndPayment.cshtml" />
|
<Content Include="Views\Checkout\AddressAndPayment.cshtml" />
|
||||||
<Content Include="Views\Checkout\Complete.cshtml" />
|
<Content Include="Views\Checkout\Complete.cshtml" />
|
||||||
<Content Include="Views\Home\Index.cshtml" />
|
<Content Include="Views\Home\Index.cshtml" />
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ namespace MusicStore
|
||||||
configuration.AddJsonFile("LocalConfig.json");
|
configuration.AddJsonFile("LocalConfig.json");
|
||||||
configuration.AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.
|
configuration.AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.
|
||||||
|
|
||||||
/* Error page middleware displays a nice formatted HTML page for any unhandled exceptions in the request pipeline.
|
/* Error page middleware displays a nice formatted HTML page for any unhandled exceptions in the request pipeline.
|
||||||
* Note: ErrorPageOptions.ShowAll to be used only at development time. Not recommended for production.
|
* Note: ErrorPageOptions.ShowAll to be used only at development time. Not recommended for production.
|
||||||
*/
|
*/
|
||||||
app.UseErrorPage(ErrorPageOptions.ShowAll);
|
app.UseErrorPage(ErrorPageOptions.ShowAll);
|
||||||
|
|
||||||
app.UseServices(services =>
|
app.UseServices(services =>
|
||||||
|
|
@ -78,9 +78,11 @@ namespace MusicStore
|
||||||
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||||
{
|
{
|
||||||
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
|
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
|
||||||
LoginPath = new PathString("/Account/Login"),
|
LoginPath = new PathString("/Account/Login")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.UseTwoFactorSignInCookies();
|
||||||
|
|
||||||
// Add MVC to the request pipeline
|
// Add MVC to the request pipeline
|
||||||
app.UseMvc(routes =>
|
app.UseMvc(routes =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
ViewBag.Title = "Confirm Email";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Thank you for confirming your email. Please @Html.ActionLink("Click here to Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
@model MusicStore.Models.ExternalLoginConfirmationViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Register";
|
||||||
|
}
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<h3>Associate your @ViewBag.LoginProvider account.</h3>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("ExternalLoginConfirmation", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
|
||||||
|
<h4>Association Form</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
|
||||||
|
<p class="text-info">
|
||||||
|
You've successfully authenticated with <strong>@ViewBag.LoginProvider</strong>.
|
||||||
|
Please enter a user name for this site below and click the Register button to finish
|
||||||
|
logging in.
|
||||||
|
</p>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
|
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Register" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Login Failure";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<h3 class="text-danger">Unsuccessful login with service.</h3>
|
||||||
|
</hgroup>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
@model MusicStore.Models.ForgotPasswordViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Forgot your password?";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<h4>Enter your email.</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Email Link" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Forgot Password Confirmation";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup class="title">
|
||||||
|
<h1>@ViewBag.Title.</h1>
|
||||||
|
</hgroup>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Please check your email to reset your password.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For demo purpose only: @Html.ActionLink("Click here to reset the password: ", "ResetPassword", new { code = ViewBag.Code });
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
@model MusicStore.Models.LoginViewModel
|
@using MusicStore.Models
|
||||||
|
@model LoginViewModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewBag.Title = "Log in";
|
ViewBag.Title = "Log in";
|
||||||
|
|
@ -13,19 +14,19 @@
|
||||||
@Html.AntiForgeryToken()
|
@Html.AntiForgeryToken()
|
||||||
<h4>Use a local account to log in.</h4>
|
<h4>Use a local account to log in.</h4>
|
||||||
<hr />
|
<hr />
|
||||||
@Html.ValidationSummary(true)
|
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
@Html.ValidationMessageFor(m => m.UserName)
|
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
|
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
|
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
|
||||||
@Html.ValidationMessageFor(m => m.Password)
|
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -42,11 +43,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
@Html.ActionLink("Register", "Register") if you don't have a local account.
|
@Html.ActionLink("Register as a new user", "Register")
|
||||||
|
</p>
|
||||||
|
@*Enable this once you have account confirmation enabled for password reset functionality*@
|
||||||
|
<p>
|
||||||
|
@Html.ActionLink("Forgot your password?", "ForgotPassword")
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@*<div class="col-md-4">
|
||||||
|
<section id="socialLoginForm">
|
||||||
|
@Html.PartialAsync("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl })
|
||||||
|
</section>
|
||||||
|
</div>*@
|
||||||
</div>
|
</div>
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
@*TODO : Until script helpers are available, adding script references manually*@
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
@{
|
|
||||||
ViewBag.Title = "Manage Account";
|
|
||||||
}
|
|
||||||
|
|
||||||
<h2>@ViewBag.Title.</h2>
|
|
||||||
<p class="text-success">@ViewBag.StatusMessage</p>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
@await Html.PartialAsync("_ChangePasswordPartial")
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@section Scripts {
|
|
||||||
@*TODO : Until script helpers are available, adding script references manually*@
|
|
||||||
@*@Scripts.Render("~/bundles/jqueryval")*@
|
|
||||||
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
|
||||||
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
|
||||||
}
|
|
||||||
|
|
@ -10,11 +10,11 @@
|
||||||
@Html.AntiForgeryToken()
|
@Html.AntiForgeryToken()
|
||||||
<h4>Create a new account.</h4>
|
<h4>Create a new account.</h4>
|
||||||
<hr />
|
<hr />
|
||||||
@Html.ValidationSummary()
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Register Confirmation";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup class="title">
|
||||||
|
<h1>@ViewBag.Title.</h1>
|
||||||
|
</hgroup>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Please check your email to activate your account.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Demo/testing purposes only: The sample displays the code and user id in the page: @Html.ActionLink("Click here to confirm your email: ", "ConfirmEmail", new { code = ViewBag.Code, userId = ViewBag.UserId })
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
@model MusicStore.Models.ResetPasswordViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Reset password";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<h4>Reset your password.</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
@Html.HiddenFor(model => model.Code)
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Reset" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Reset password confirmation";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup class="title">
|
||||||
|
<h1>@ViewBag.Title.</h1>
|
||||||
|
</hgroup>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Your password has been reset. Please @Html.ActionLink("click here to log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
@model MusicStore.Models.SendCodeViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Send";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("SendCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) {
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
@Html.Hidden("rememberMe", @Model.RememberMe)
|
||||||
|
<h4>Send verification code</h4>
|
||||||
|
<hr />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
Select Two-Factor Authentication Provider:
|
||||||
|
@Html.DropDownListFor(model => model.SelectedProvider, Model.Providers)
|
||||||
|
<input type="submit" value="Submit" class="btn btn-default" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
@model MusicStore.Models.ExternalLoginListViewModel
|
||||||
|
@using Microsoft.AspNet.Http.Security;
|
||||||
|
|
||||||
|
<h4>Use another service to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
@{
|
||||||
|
var loginProviders = Context.GetAuthenticationTypes();
|
||||||
|
if (loginProviders.Count() == 0) {
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkId=313242">this article</a>
|
||||||
|
for details on setting up this ASP.NET application to support logging in via external services.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl })) {
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<div id="socialLoginList">
|
||||||
|
<p>
|
||||||
|
@foreach (AuthenticationDescription p in loginProviders) {
|
||||||
|
<button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Log in using your @p.Caption account">@p.AuthenticationType</button>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
@model MusicStore.Models.AddPhoneNumberViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Phone Number";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("AddPhoneNumber", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<h4>Add a phone number</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Number, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Number, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Submit" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
@using System.Security.Principal
|
@model MusicStore.Models.ChangePasswordViewModel
|
||||||
@model MusicStore.Models.ManageUserViewModel
|
@{
|
||||||
|
ViewBag.Title = "Change Password";
|
||||||
|
}
|
||||||
|
|
||||||
<p>You're logged in as <strong>@User.Identity.GetUserName()</strong>.</p>
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
@using (Html.BeginForm("Manage", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
@using (Html.BeginForm("ChangePassword", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
{
|
{
|
||||||
@Html.AntiForgeryToken()
|
@Html.AntiForgeryToken()
|
||||||
<h4>Change Password Form</h4>
|
<h4>Change Password Form</h4>
|
||||||
<hr />
|
<hr />
|
||||||
@Html.ValidationSummary()
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@Html.LabelFor(m => m.OldPassword, new { @class = "col-md-2 control-label" })
|
@Html.LabelFor(m => m.OldPassword, new { @class = "col-md-2 control-label" })
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
|
|
@ -27,10 +29,15 @@
|
||||||
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
|
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-md-offset-2 col-md-10">
|
<div class="col-md-offset-2 col-md-10">
|
||||||
<input type="submit" value="Change password" class="btn btn-default" />
|
<input type="submit" value="Change password" class="btn btn-default" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
@model MusicStore.Models.IndexViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Manage";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
<p class="text-success">@ViewBag.StatusMessage</p>
|
||||||
|
<div>
|
||||||
|
<h4>Change your account settings</h4>
|
||||||
|
<hr />
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Password:</dt>
|
||||||
|
<dd>
|
||||||
|
[
|
||||||
|
@if (Model.HasPassword)
|
||||||
|
{
|
||||||
|
@Html.ActionLink("Change your password", "ChangePassword")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Html.ActionLink("Create", "SetPassword")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</dd>
|
||||||
|
<dt>External Logins:</dt>
|
||||||
|
<dd>
|
||||||
|
@Model.Logins.Count [
|
||||||
|
@Html.ActionLink("Manage", "ManageLogins") ]
|
||||||
|
</dd>
|
||||||
|
@*
|
||||||
|
Phone Numbers can used as a second factor of verification in a two-factor authentication system.
|
||||||
|
|
||||||
|
See <a href="http://go.microsoft.com/fwlink/?LinkId=313242">this article</a>
|
||||||
|
for details on setting up this ASP.NET application to support two-factor authentication using SMS.
|
||||||
|
|
||||||
|
Uncomment the following block after you have set up two-factor authentication
|
||||||
|
*@
|
||||||
|
|
||||||
|
<dt>Phone Number:</dt>
|
||||||
|
<dd>
|
||||||
|
@(Model.PhoneNumber ?? "None") [
|
||||||
|
@if (Model.PhoneNumber != null)
|
||||||
|
{
|
||||||
|
@Html.ActionLink("Change", "AddPhoneNumber")
|
||||||
|
@: |
|
||||||
|
@Html.ActionLink("Remove", "RemovePhoneNumber")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Html.ActionLink("Add", "AddPhoneNumber")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</dd>
|
||||||
|
<dt>Two-Factor Authentication:</dt>
|
||||||
|
<dd>
|
||||||
|
@if (Model.TwoFactor)
|
||||||
|
{
|
||||||
|
using (Html.BeginForm("DisableTwoFactorAuthentication", "Manage", FormMethod.Post))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<p>
|
||||||
|
Enabled
|
||||||
|
<input type="submit" value="Disable" class="btn btn-link" />
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (Html.BeginForm("EnableTwoFactorAuthentication", "Manage", FormMethod.Post))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<p>
|
||||||
|
Disabled
|
||||||
|
<input type="submit" value="Enable" class="btn btn-link" />
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
<dt>Browser remembered:</dt>
|
||||||
|
<dd>
|
||||||
|
@if (Model.BrowserRemembered)
|
||||||
|
{
|
||||||
|
using (Html.BeginForm("ForgetBrowser", "Manage", FormMethod.Post))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<p>
|
||||||
|
Browser is curently remembered for two factor:
|
||||||
|
<input type="submit" value="Forget Browser" class="btn btn-default" />
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (Html.BeginForm("RememberBrowser", "Manage", FormMethod.Post))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<p>
|
||||||
|
Browser is curently not remembered for two factor:
|
||||||
|
<input type="submit" value="Remember Browser" class="btn btn-default" />
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
@using Microsoft.AspNet.Http.Security;
|
||||||
|
@model MusicStore.Models.ManageLoginsViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Manage your external logins";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
<p class="text-success">@ViewBag.StatusMessage</p>
|
||||||
|
@if (Model.CurrentLogins.Count > 0)
|
||||||
|
{
|
||||||
|
<h4>Registered Logins</h4>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
@foreach (var account in Model.CurrentLogins)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@account.LoginProvider</td>
|
||||||
|
<td>
|
||||||
|
@if (ViewBag.ShowRemoveButton)
|
||||||
|
{
|
||||||
|
using (Html.BeginForm("RemoveLogin", "Manage"))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<div>
|
||||||
|
@Html.Hidden("loginProvider", account.LoginProvider)
|
||||||
|
@Html.Hidden("providerKey", account.ProviderKey)
|
||||||
|
<input type="submit" class="btn btn-default" value="Remove" title="Remove this @account.LoginProvider login from your account" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
@if (Model.OtherLogins.Count > 0)
|
||||||
|
{
|
||||||
|
<h4>Add another service to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
using (Html.BeginForm("LinkLogin", "Manage"))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<div id="socialLoginList">
|
||||||
|
<p>
|
||||||
|
@foreach (AuthenticationDescription p in Model.OtherLogins)
|
||||||
|
{
|
||||||
|
<button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Log in using your @p.Caption account">@p.AuthenticationType</button>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
@model MusicStore.Models.SetPasswordViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Create Password";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<p class="text-info">
|
||||||
|
You do not have a local username/password for this site. Add a local
|
||||||
|
account so you can log in without an external login.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("SetPassword", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
|
||||||
|
<h4>Create Local Login</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" value="Set password" class="btn btn-default" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
@model MusicStore.Models.VerifyPhoneNumberViewModel
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Verify Phone Number";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("VerifyPhoneNumber", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
@Html.Hidden("phoneNumber", @Model.PhoneNumber)
|
||||||
|
<h4>Enter verification code</h4>
|
||||||
|
<h5>@ViewBag.Status</h5>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Code, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Submit" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Demo code display page - Not for production use";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup class="title">
|
||||||
|
<h1>@ViewBag.Title.</h1>
|
||||||
|
</hgroup>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Demo code display page - Not for production use.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
[Demo]: @ViewBag.Status
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
@{
|
||||||
|
ViewBag.Title = "Locked Out";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h1 class="text-danger">Locked out.</h1>
|
||||||
|
<h2 class="text-danger">This account has been locked out, please try again later.</h2>
|
||||||
|
</hgroup>
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li>
|
<li>
|
||||||
@Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" })
|
@Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
|
||||||
</li>
|
</li>
|
||||||
<li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
|
<li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -97,36 +97,36 @@ namespace E2ETests
|
||||||
RegisterUserWithNonMatchingPasswords();
|
RegisterUserWithNonMatchingPasswords();
|
||||||
|
|
||||||
//Register a valid user
|
//Register a valid user
|
||||||
var generatedUserName = RegisterValidUser();
|
var generatedEmail = RegisterValidUser();
|
||||||
|
|
||||||
//Register a user - Negative scenario : Trying to register a user name that's already registered.
|
//Register a user - Negative scenario : Trying to register a user name that's already registered.
|
||||||
RegisterExistingUser(generatedUserName);
|
RegisterExistingUser(generatedEmail);
|
||||||
|
|
||||||
//Logout from this user session - This should take back to the home page
|
//Logout from this user session - This should take back to the home page
|
||||||
SignOutUser(generatedUserName);
|
SignOutUser(generatedEmail);
|
||||||
|
|
||||||
//Sign in scenarios: Invalid password - Expected an invalid user name password error.
|
//Sign in scenarios: Invalid password - Expected an invalid user name password error.
|
||||||
SignInWithInvalidPassword(generatedUserName, "InvalidPassword~1");
|
SignInWithInvalidPassword(generatedEmail, "InvalidPassword~1");
|
||||||
|
|
||||||
//Sign in scenarios: Valid user name & password.
|
//Sign in scenarios: Valid user name & password.
|
||||||
SignInWithUser(generatedUserName, "Password~1");
|
SignInWithUser(generatedEmail, "Password~1");
|
||||||
|
|
||||||
//Change password scenario
|
//Change password scenario
|
||||||
ChangePassword(generatedUserName);
|
ChangePassword(generatedEmail);
|
||||||
|
|
||||||
//SignIn with old password and verify old password is not allowed and new password is allowed
|
//SignIn with old password and verify old password is not allowed and new password is allowed
|
||||||
SignOutUser(generatedUserName);
|
SignOutUser(generatedEmail);
|
||||||
SignInWithInvalidPassword(generatedUserName, "Password~1");
|
SignInWithInvalidPassword(generatedEmail, "Password~1");
|
||||||
SignInWithUser(generatedUserName, "Password~2");
|
SignInWithUser(generatedEmail, "Password~2");
|
||||||
|
|
||||||
//Making a request to a protected resource that this user does not have access to - should automatically redirect to login page again
|
//Making a request to a protected resource that this user does not have access to - should automatically redirect to login page again
|
||||||
AccessStoreWithoutPermissions(generatedUserName);
|
AccessStoreWithoutPermissions(generatedEmail);
|
||||||
|
|
||||||
//Logout from this user session - This should take back to the home page
|
//Logout from this user session - This should take back to the home page
|
||||||
SignOutUser(generatedUserName);
|
SignOutUser(generatedEmail);
|
||||||
|
|
||||||
//Login as an admin user
|
//Login as an admin user
|
||||||
SignInWithUser("Administrator", "YouShouldChangeThisPassword1!");
|
SignInWithUser("Administrator@test.com", "YouShouldChangeThisPassword1!");
|
||||||
|
|
||||||
//Now navigating to the store manager should work fine as this user has the necessary permission to administer the store.
|
//Now navigating to the store manager should work fine as this user has the necessary permission to administer the store.
|
||||||
AccessStoreWithPermissions();
|
AccessStoreWithPermissions();
|
||||||
|
|
@ -222,9 +222,9 @@ namespace E2ETests
|
||||||
Assert.Contains("<li class=\"divider\"></li>", responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("<li class=\"divider\"></li>", responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AccessStoreWithoutPermissions(string userName = null)
|
private void AccessStoreWithoutPermissions(string email = null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Trying to access StoreManager that needs ManageStore claim with the current user : {0}", userName ?? "Anonymous");
|
Console.WriteLine("Trying to access StoreManager that needs ManageStore claim with the current user : {0}", email ?? "Anonymous");
|
||||||
var response = httpClient.GetAsync("StoreManager/").Result;
|
var response = httpClient.GetAsync("StoreManager/").Result;
|
||||||
ThrowIfResponseStatusNotOk(response);
|
ThrowIfResponseStatusNotOk(response);
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
|
|
@ -259,11 +259,11 @@ namespace E2ETests
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
ValidateLayoutPage(responseContent);
|
ValidateLayoutPage(responseContent);
|
||||||
|
|
||||||
var generatedUserName = Guid.NewGuid().ToString().Replace("-", string.Empty);
|
var generatedEmail = Guid.NewGuid().ToString().Replace("-", string.Empty) + "@test.com";
|
||||||
Console.WriteLine("Creating a new user with name '{0}'", generatedUserName);
|
Console.WriteLine("Creating a new user with name '{0}'", generatedEmail);
|
||||||
var formParameters = new List<KeyValuePair<string, string>>
|
var formParameters = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("UserName", generatedUserName),
|
new KeyValuePair<string, string>("Email", generatedEmail),
|
||||||
new KeyValuePair<string, string>("Password", "Password~1"),
|
new KeyValuePair<string, string>("Password", "Password~1"),
|
||||||
new KeyValuePair<string, string>("ConfirmPassword", "Password~2"),
|
new KeyValuePair<string, string>("ConfirmPassword", "Password~2"),
|
||||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Register")),
|
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Register")),
|
||||||
|
|
@ -273,8 +273,8 @@ namespace E2ETests
|
||||||
response = httpClient.PostAsync("Account/Register", content).Result;
|
response = httpClient.PostAsync("Account/Register", content).Result;
|
||||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
||||||
Assert.Contains("<div class=\"validation-summary-errors\" data-valmsg-summary=\"true\"><ul><li>The password and confirmation password do not match.</li>", responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("<div class=\"validation-summary-errors text-danger\" data-valmsg-summary=\"true\"><ul><li>The password and confirmation password do not match.</li>", responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
Console.WriteLine("Server side model validator rejected the user '{0}''s registration as passwords do not match.", generatedUserName);
|
Console.WriteLine("Server side model validator rejected the user '{0}''s registration as passwords do not match.", generatedEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string RegisterValidUser()
|
private string RegisterValidUser()
|
||||||
|
|
@ -284,11 +284,11 @@ namespace E2ETests
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
ValidateLayoutPage(responseContent);
|
ValidateLayoutPage(responseContent);
|
||||||
|
|
||||||
var generatedUserName = Guid.NewGuid().ToString().Replace("-", string.Empty);
|
var generatedEmail = Guid.NewGuid().ToString().Replace("-", string.Empty) + "@test.com";
|
||||||
Console.WriteLine("Creating a new user with name '{0}'", generatedUserName);
|
Console.WriteLine("Creating a new user with name '{0}'", generatedEmail);
|
||||||
var formParameters = new List<KeyValuePair<string, string>>
|
var formParameters = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("UserName", generatedUserName),
|
new KeyValuePair<string, string>("Email", generatedEmail),
|
||||||
new KeyValuePair<string, string>("Password", "Password~1"),
|
new KeyValuePair<string, string>("Password", "Password~1"),
|
||||||
new KeyValuePair<string, string>("ConfirmPassword", "Password~1"),
|
new KeyValuePair<string, string>("ConfirmPassword", "Password~1"),
|
||||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Register")),
|
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Register")),
|
||||||
|
|
@ -297,24 +297,24 @@ namespace E2ETests
|
||||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||||
response = httpClient.PostAsync("Account/Register", content).Result;
|
response = httpClient.PostAsync("Account/Register", content).Result;
|
||||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Assert.Contains(string.Format("Hello {0}!", generatedUserName), responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains(string.Format("Hello {0}!", generatedEmail), responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
//Verify cookie sent
|
//Verify cookie sent
|
||||||
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
||||||
Console.WriteLine("Successfully registered user '{0}' and signed in", generatedUserName);
|
Console.WriteLine("Successfully registered user '{0}' and signed in", generatedEmail);
|
||||||
return generatedUserName;
|
return generatedEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterExistingUser(string userName)
|
private void RegisterExistingUser(string email)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Trying to register a user with name '{0}' again", userName);
|
Console.WriteLine("Trying to register a user with name '{0}' again", email);
|
||||||
var response = httpClient.GetAsync("Account/Register").Result;
|
var response = httpClient.GetAsync("Account/Register").Result;
|
||||||
ThrowIfResponseStatusNotOk(response);
|
ThrowIfResponseStatusNotOk(response);
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Console.WriteLine("Creating a new user with name '{0}'", userName);
|
Console.WriteLine("Creating a new user with name '{0}'", email);
|
||||||
var formParameters = new List<KeyValuePair<string, string>>
|
var formParameters = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("UserName", userName),
|
new KeyValuePair<string, string>("Email", email),
|
||||||
new KeyValuePair<string, string>("Password", "Password~1"),
|
new KeyValuePair<string, string>("Password", "Password~1"),
|
||||||
new KeyValuePair<string, string>("ConfirmPassword", "Password~1"),
|
new KeyValuePair<string, string>("ConfirmPassword", "Password~1"),
|
||||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Register")),
|
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Register")),
|
||||||
|
|
@ -323,13 +323,13 @@ namespace E2ETests
|
||||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||||
response = httpClient.PostAsync("Account/Register", content).Result;
|
response = httpClient.PostAsync("Account/Register", content).Result;
|
||||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Assert.Contains(string.Format("Name {0} is already taken.", userName), responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains(string.Format("Name {0} is already taken.", email), responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
Console.WriteLine("Identity threw a valid exception that user '{0}' already exists in the system", userName);
|
Console.WriteLine("Identity threw a valid exception that user '{0}' already exists in the system", email);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignOutUser(string userName)
|
private void SignOutUser(string email)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Signing out from '{0}''s session", userName);
|
Console.WriteLine("Signing out from '{0}''s session", email);
|
||||||
var response = httpClient.GetAsync(string.Empty).Result;
|
var response = httpClient.GetAsync(string.Empty).Result;
|
||||||
ThrowIfResponseStatusNotOk(response);
|
ThrowIfResponseStatusNotOk(response);
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
|
|
@ -352,7 +352,7 @@ namespace E2ETests
|
||||||
Assert.Contains("/Images/home-showcase.png", responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("/Images/home-showcase.png", responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
//Verify cookie cleared on logout
|
//Verify cookie cleared on logout
|
||||||
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
||||||
Console.WriteLine("Successfully signed out of '{0}''s session", userName);
|
Console.WriteLine("Successfully signed out of '{0}''s session", email);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -362,15 +362,15 @@ namespace E2ETests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignInWithInvalidPassword(string userName, string invalidPassword)
|
private void SignInWithInvalidPassword(string email, string invalidPassword)
|
||||||
{
|
{
|
||||||
var response = httpClient.GetAsync("Account/Login").Result;
|
var response = httpClient.GetAsync("Account/Login").Result;
|
||||||
ThrowIfResponseStatusNotOk(response);
|
ThrowIfResponseStatusNotOk(response);
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Console.WriteLine("Signing in with user '{0}'", userName);
|
Console.WriteLine("Signing in with user '{0}'", email);
|
||||||
var formParameters = new List<KeyValuePair<string, string>>
|
var formParameters = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("UserName", userName),
|
new KeyValuePair<string, string>("Email", email),
|
||||||
new KeyValuePair<string, string>("Password", invalidPassword),
|
new KeyValuePair<string, string>("Password", invalidPassword),
|
||||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Login")),
|
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Login")),
|
||||||
};
|
};
|
||||||
|
|
@ -378,21 +378,21 @@ namespace E2ETests
|
||||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||||
response = httpClient.PostAsync("Account/Login", content).Result;
|
response = httpClient.PostAsync("Account/Login", content).Result;
|
||||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Assert.Contains("<div class=\"validation-summary-errors\"><ul><li>Invalid username or password.</li>", responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("<div class=\"validation-summary-errors text-danger\"><ul><li>Invalid login attempt.</li>", responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
//Verify cookie not sent
|
//Verify cookie not sent
|
||||||
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
||||||
Console.WriteLine("Identity successfully prevented an invalid user login.");
|
Console.WriteLine("Identity successfully prevented an invalid user login.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignInWithUser(string userName, string password)
|
private void SignInWithUser(string email, string password)
|
||||||
{
|
{
|
||||||
var response = httpClient.GetAsync("Account/Login").Result;
|
var response = httpClient.GetAsync("Account/Login").Result;
|
||||||
ThrowIfResponseStatusNotOk(response);
|
ThrowIfResponseStatusNotOk(response);
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Console.WriteLine("Signing in with user '{0}'", userName);
|
Console.WriteLine("Signing in with user '{0}'", email);
|
||||||
var formParameters = new List<KeyValuePair<string, string>>
|
var formParameters = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("UserName", userName),
|
new KeyValuePair<string, string>("Email", email),
|
||||||
new KeyValuePair<string, string>("Password", password),
|
new KeyValuePair<string, string>("Password", password),
|
||||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Login")),
|
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Login")),
|
||||||
};
|
};
|
||||||
|
|
@ -400,16 +400,16 @@ namespace E2ETests
|
||||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||||
response = httpClient.PostAsync("Account/Login", content).Result;
|
response = httpClient.PostAsync("Account/Login", content).Result;
|
||||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Assert.Contains(string.Format("Hello {0}!", userName), responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains(string.Format("Hello {0}!", email), responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
//Verify cookie sent
|
//Verify cookie sent
|
||||||
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
||||||
Console.WriteLine("Successfully signed in with user '{0}'", userName);
|
Console.WriteLine("Successfully signed in with user '{0}'", email);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangePassword(string userName)
|
private void ChangePassword(string email)
|
||||||
{
|
{
|
||||||
var response = httpClient.GetAsync("Account/Manage").Result;
|
var response = httpClient.GetAsync("Manage/ChangePassword").Result;
|
||||||
ThrowIfResponseStatusNotOk(response);
|
ThrowIfResponseStatusNotOk(response);
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
var formParameters = new List<KeyValuePair<string, string>>
|
var formParameters = new List<KeyValuePair<string, string>>
|
||||||
|
|
@ -417,15 +417,15 @@ namespace E2ETests
|
||||||
new KeyValuePair<string, string>("OldPassword", "Password~1"),
|
new KeyValuePair<string, string>("OldPassword", "Password~1"),
|
||||||
new KeyValuePair<string, string>("NewPassword", "Password~2"),
|
new KeyValuePair<string, string>("NewPassword", "Password~2"),
|
||||||
new KeyValuePair<string, string>("ConfirmPassword", "Password~2"),
|
new KeyValuePair<string, string>("ConfirmPassword", "Password~2"),
|
||||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/Manage")),
|
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Manage/ChangePassword")),
|
||||||
};
|
};
|
||||||
|
|
||||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||||
response = httpClient.PostAsync("Account/Manage", content).Result;
|
response = httpClient.PostAsync("Manage/ChangePassword", content).Result;
|
||||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
Assert.Contains("Your password has been changed.", responseContent, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("Your password has been changed.", responseContent, StringComparison.OrdinalIgnoreCase);
|
||||||
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
|
||||||
Console.WriteLine("Successfully changed the password for user '{0}'", userName);
|
Console.WriteLine("Successfully changed the password for user '{0}'", email);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateAlbum()
|
private string CreateAlbum()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue