Enabling account validation, 2FA, Forgot password in the sample

Some identity helpers are still missing. Work arounds are applied to enable them working. Will clean it up as soon as identity helpers are available.
This commit is contained in:
Praburaj 2014-09-24 15:13:28 -07:00
parent 1867d11a43
commit 647a5b6031
11 changed files with 304 additions and 150 deletions

View File

@ -6,6 +6,7 @@ using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using MusicStore.Models;
using System.Threading;
namespace MusicStore.Controllers
{
@ -16,6 +17,11 @@ namespace MusicStore.Controllers
{
UserManager = userManager;
SignInManager = signInManager;
//TODO: Work around - Identity helpers will be available to do this
UserManager.UserTokenProvider = new StaticTokenProvider();
UserManager.RegisterTwoFactorProvider("Phone Code", UserManager.UserTokenProvider);
UserManager.RegisterTwoFactorProvider("Email Code", UserManager.UserTokenProvider);
}
public UserManager<ApplicationUser> UserManager { get; private set; }
@ -64,21 +70,29 @@ namespace MusicStore.Controllers
//
// 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 });
//}
[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
{
//https://github.com/aspnet/Identity/issues/209
// Require that the user has already logged in via username/password or external login
//if (!await SignInManager.HasBeenVerifiedAsync())
if ((await Context.AuthenticateAsync("Microsoft.AspNet.Identity.TwoFactor.UserId")).Identity.GetUserName() == null)
{
return View("Error");
}
//https://github.com/aspnet/Identity/issues/207
//var user = await UserManager.FindByIdAsync(await SignInManager.GetVerifiedUserIdAsync());
var user = await UserManager.FindByIdAsync((await Context.AuthenticateAsync("Microsoft.AspNet.Identity.TwoFactor.UserId")).Identity.GetUserName());
#if DEMO
if (user != null)
{
var code = await UserManager.GenerateTwoFactorTokenAsync(user, provider);
ViewBag.Code = code;
}
#endif
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// POST: /Account/VerifyCode
@ -134,19 +148,21 @@ namespace MusicStore.Controllers
if (result.Succeeded)
{
//Bug: Remember browser option missing?
await SignInManager.SignInAsync(user, isPersistent: false);
//Uncomment this and comment the later part if account verification is not needed.
//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");
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, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
#if !DEMO
return RedirectToAction("Index", "Home");
#else
//To display the email link in a friendly page instead of sending email
ViewBag.Link = callbackUrl;
return View("DemoLinkDisplay");
#endif
}
AddErrors(result);
}
@ -157,7 +173,6 @@ namespace MusicStore.Controllers
//
// GET: /Account/ConfirmEmail
//TODO: This does not work yet due to some missing identity features
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
@ -165,8 +180,8 @@ namespace MusicStore.Controllers
{
return View("Error");
}
var user = new ApplicationUser { Id = userId };
//Bug: Throws NullRefException
var user = await SignInManager.UserManager.FindByIdAsync(userId);
var result = await UserManager.ConfirmEmailAsync(user, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
@ -199,13 +214,14 @@ namespace MusicStore.Controllers
// 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");
await UserManager.SendEmailAsync(user, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
#if !DEMO
return RedirectToAction("ForgotPasswordConfirmation", "Account");
#else
//To display the email link in a friendly page instead of sending email
ViewBag.Link = callbackUrl;
return View("DemoLinkDisplay");
#endif
}
ModelState.AddModelError("", string.Format("We could not locate an account with email : {0}", model.Email));
@ -282,13 +298,14 @@ namespace MusicStore.Controllers
[AllowAnonymous]
public async Task<ActionResult> SendCode(bool rememberMe, string returnUrl = null)
{
// TODO: This currently throws
var userId = await GetCurrentUserAsync();
//https://github.com/aspnet/Identity/issues/207
//var userId = await SignInManager.GetVerifiedUserIdAsync();
var userId = (await Context.AuthenticateAsync("Microsoft.AspNet.Identity.TwoFactor.UserId")).Identity.GetUserName();
if (userId == null)
{
return View("Error");
}
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(new ApplicationUser() { Id = userId });
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
@ -315,74 +332,79 @@ namespace MusicStore.Controllers
//
// 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");
// }
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl = null)
{
//TODO: Helper not available
//var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
var loginInfo = await Context.AuthenticateAsync("External");
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 });
// }
//}
//TODO: Helper not available
// Sign in the user with this external login provider if the user already has a login
//var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
var result = SignInStatus.Failure; //Always redirect to a login confirmation page for now.
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.Identity.AuthenticationType;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Identity.Claims.Single(c => c.Type == System.Security.Claims.ClaimTypes.Email).Value });
}
}
//
// 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");
// }
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
{
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);
// }
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
//var info = await AuthenticationManager.GetExternalLoginInfoAsync();
var info = await Context.AuthenticateAsync("External");
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)
{
var userloginInfo = new UserLoginInfo(info.Description.AuthenticationType, info.Description.AuthenticationType, info.Description.Caption);
result = await UserManager.AddLoginAsync(user, userloginInfo);
if (result.Succeeded)
{
// TODO: rememberBrowser option not being taken in SignInAsync
await SignInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
// ViewBag.ReturnUrl = returnUrl;
// return View(model);
//}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// POST: /Account/LogOff
@ -431,4 +453,37 @@ namespace MusicStore.Controllers
#endregion
}
/// <summary>
/// TODO: Work around until there is a token provider
/// </summary>
internal class StaticTokenProvider : IUserTokenProvider<ApplicationUser>
{
public Task<string> GenerateAsync(string purpose, UserManager<ApplicationUser> manager,
ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(MakeToken(purpose, user));
}
public Task<bool> ValidateAsync(string purpose, string token, UserManager<ApplicationUser> manager,
ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(token == MakeToken(purpose, user));
}
public Task NotifyAsync(string token, UserManager<ApplicationUser> manager, ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
}
public Task<bool> IsValidProviderForUserAsync(UserManager<ApplicationUser> manager, ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(true);
}
private static string MakeToken(string purpose, ApplicationUser user)
{
return string.Join(":", user.Id, purpose, "ImmaToken");
}
}
}

View File

@ -4,6 +4,8 @@ using System.Security.Principal;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using MusicStore.Models;
using System.Linq;
using Microsoft.AspNet.Http.Security;
namespace MusicStore.Controllers
{
@ -138,7 +140,7 @@ namespace MusicStore.Controllers
// 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;
ViewBag.Code = code;
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
}
@ -276,28 +278,60 @@ namespace MusicStore.Controllers
//
// 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
//});
//}
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null)
{
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 = Context.GetAuthenticationTypes().Where(auth => auth.Caption != null && userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
//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
});
}
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
var authenticationProperties = new AuthenticationProperties() { RedirectUri = Url.Action("LinkLoginCallback", "Manage") };
authenticationProperties.Dictionary[XsrfKey] = User.Identity.GetUserId();
return new ChallengeResult(provider, authenticationProperties);
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
//var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
var loginInfo = await Context.AuthenticateAsync("External");
if (loginInfo == null || loginInfo.Properties.Dictionary[XsrfKey] != User.Identity.GetUserId())
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var user = new ApplicationUser() { Id = User.Identity.GetUserId() };
var userloginInfo = new UserLoginInfo(loginInfo.Description.AuthenticationType, loginInfo.Description.AuthenticationType, loginInfo.Description.Caption);
var result = await UserManager.AddLoginAsync(user, userloginInfo);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)

View File

@ -108,27 +108,27 @@ namespace MusicStore
app.UseFacebookAuthentication(new FacebookAuthenticationOptions()
{
AppId = "[AppId]",
AppSecret = "[AppSecret]",
AppId = "550624398330273",
AppSecret = "10e56a291d6b618da61b1e0dae3a8954",
});
app.UseGoogleAuthentication(new GoogleAuthenticationOptions()
{
ClientId = "[ClientId]",
ClientSecret = "[ClientSecret]",
ClientId = "977382855444.apps.googleusercontent.com",
ClientSecret = "NafT482F70Vjj_9q1PU4B0pN",
});
app.UseTwitterAuthentication(new TwitterAuthenticationOptions()
{
ConsumerKey = "[ConsumerKey]",
ConsumerSecret = "[ConsumerSecret]",
ConsumerKey = "9J3j3pSwgbWkgPFH7nAf0Spam",
ConsumerSecret = "jUBYkQuBFyqp7G3CUB9SW3AfflFr9z3oQBiNvumYy87Al0W4h8",
});
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions()
{
Caption = "MicrosoftAccount - Requires project changes",
ClientId = "[ClientId]",
ClientSecret = "[ClientSecret]",
ClientId = "000000004012C08A",
ClientSecret = "GaMQ2hCnqAC6EcDLnXsAeBVIJOLmeutL",
});
// Add MVC to the request pipeline

View File

@ -1,4 +1,4 @@
{
@{
ViewBag.Title = "Confirm Email";
}

View File

@ -0,0 +1,47 @@
@model MusicStore.Models.VerifyCodeViewModel
@{
ViewBag.Title = "Verify";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("VerifyCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
@Html.Hidden("provider", @Model.Provider)
@Html.Hidden("rememberMe", @Model.RememberMe)
<h4>Enter verification code</h4>
<p class="text-danger">
For DEMO only: You can type in this code in the below text box to proceed: [ @ViewBag.Code ]
<br />
Please change this code to register an SMS/Email service in IdentityConfig to send a message.
</p>
<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">
<div class="checkbox">
@Html.CheckBoxFor(m => m.RememberBrowser)
@Html.LabelFor(m => m.RememberBrowser)
</div>
</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>
}

View File

@ -10,7 +10,11 @@
@Html.AntiForgeryToken()
@Html.Hidden("phoneNumber", @Model.PhoneNumber)
<h4>Enter verification code</h4>
<h5>@ViewBag.Status</h5>
<p class="text-danger">
For DEMO only: You can type in this code in the below text box to proceed: @ViewBag.Code
<br />
Please change this code to register an SMS service in IdentityConfig to send a text message.
</p>
<hr />
@Html.ValidationSummary("", new { @class = "text-danger" })
<div class="form-group">

View File

@ -1,15 +0,0 @@
@{
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>

View File

@ -0,0 +1,21 @@
@{
ViewBag.Title = "Demo link display page - Not for production use";
}
<hgroup class="title">
<h1>@ViewBag.Title.</h1>
</hgroup>
<div>
<p>
Demo link display page - Not for production use.
</p>
@if (ViewBag.Link != null)
{
<p class="text-danger">
For DEMO only: You can click this link to confirm the email: [[<a href="@ViewBag.Link">link</a>]]
<br />
Please change this code to register an email service in IdentityConfig to send an email.
</p>
}
</div>

View File

@ -4,7 +4,7 @@
],
"description": "Music store application on K",
"version": "1.0.0-*",
"compilationOptions": { "warningsAsErrors": true },
"compilationOptions": { "warningsAsErrors": true, "define": [ "DEMO" ] },
"webroot": "wwwroot",
"dependencies": {
"Kestrel": "1.0.0-*",

View File

@ -136,11 +136,17 @@ namespace E2ETests
var content = new FormUrlEncodedContent(formParameters.ToArray());
response = httpClient.PostAsync("Account/Register", content).Result;
responseContent = response.Content.ReadAsStringAsync().Result;
Assert.Contains(string.Format("Hello {0}!", generatedEmail), responseContent, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
//Verify cookie sent
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
Console.WriteLine("Successfully registered user '{0}' and signed in", generatedEmail);
//Account verification
Assert.Equal<string>(ApplicationBaseUrl + "Account/Register", response.RequestMessage.RequestUri.AbsoluteUri);
Assert.Contains("For DEMO only: You can click this link to confirm the email:", responseContent, StringComparison.OrdinalIgnoreCase);
var startIndex = responseContent.IndexOf("[[<a href=\"", 0) + "[[<a href=\"".Length;
var endIndex = responseContent.IndexOf("\">link</a>]]", startIndex);
var confirmUrl = responseContent.Substring(startIndex, endIndex - startIndex);
response = httpClient.GetAsync(confirmUrl).Result;
ThrowIfResponseStatusNotOk(response);
responseContent = response.Content.ReadAsStringAsync().Result;
Assert.Contains("Thank you for confirming your email.", responseContent, StringComparison.OrdinalIgnoreCase);
return generatedEmail;
}

View File

@ -84,6 +84,8 @@ namespace E2ETests
//Register a valid user
var generatedEmail = RegisterValidUser();
SignInWithUser(generatedEmail, "Password~1");
//Register a user - Negative scenario : Trying to register a user name that's already registered.
RegisterExistingUser(generatedEmail);