diff --git a/src/MusicStore/Controllers/AccountController.cs b/src/MusicStore/Controllers/AccountController.cs index 9e3443a3f3..ce4e8ead61 100644 --- a/src/MusicStore/Controllers/AccountController.cs +++ b/src/MusicStore/Controllers/AccountController.cs @@ -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 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 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 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 here"); - // 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 here"); +#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 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 here"); - // 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 here"); +#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 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 ExternalLoginCallback(string returnUrl) - //{ - // var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); - // if (loginInfo == null) - // { - // return RedirectToAction("Login"); - // } + [AllowAnonymous] + public async Task 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 ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) - //{ - // if (User.Identity.IsAuthenticated) - // { - // return RedirectToAction("Index", "Manage"); - // } + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task 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 } + + /// + /// TODO: Work around until there is a token provider + /// + internal class StaticTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, + ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(MakeToken(purpose, user)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(token == MakeToken(purpose, user)); + } + + public Task NotifyAsync(string token, UserManager manager, ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(0); + } + + public Task IsValidProviderForUserAsync(UserManager 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"); + } + } } \ No newline at end of file diff --git a/src/MusicStore/Controllers/ManageController.cs b/src/MusicStore/Controllers/ManageController.cs index 74a054ae02..94d283dc4b 100644 --- a/src/MusicStore/Controllers/ManageController.cs +++ b/src/MusicStore/Controllers/ManageController.cs @@ -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 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 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 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) diff --git a/src/MusicStore/Startup.cs b/src/MusicStore/Startup.cs index 1b9dcd69e0..6d1767f5d5 100644 --- a/src/MusicStore/Startup.cs +++ b/src/MusicStore/Startup.cs @@ -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 diff --git a/src/MusicStore/Views/Account/ConfirmEmail.cshtml b/src/MusicStore/Views/Account/ConfirmEmail.cshtml index 98c66ede3a..5608c5460a 100644 --- a/src/MusicStore/Views/Account/ConfirmEmail.cshtml +++ b/src/MusicStore/Views/Account/ConfirmEmail.cshtml @@ -1,4 +1,4 @@ -{ +@{ ViewBag.Title = "Confirm Email"; } diff --git a/src/MusicStore/Views/Account/VerifyCode.cshtml b/src/MusicStore/Views/Account/VerifyCode.cshtml new file mode 100644 index 0000000000..36871d3dfd --- /dev/null +++ b/src/MusicStore/Views/Account/VerifyCode.cshtml @@ -0,0 +1,47 @@ +@model MusicStore.Models.VerifyCodeViewModel +@{ + ViewBag.Title = "Verify"; +} + +

@ViewBag.Title.

+ +@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) +

Enter verification code

+

+ For DEMO only: You can type in this code in the below text box to proceed: [ @ViewBag.Code ] +
+ Please change this code to register an SMS/Email service in IdentityConfig to send a message. +

+
+ @Html.ValidationSummary("", new { @class = "text-danger" }) +
+ @Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" }) +
+ @Html.TextBoxFor(m => m.Code, new { @class = "form-control" }) +
+
+
+
+
+ @Html.CheckBoxFor(m => m.RememberBrowser) + @Html.LabelFor(m => m.RememberBrowser) +
+
+
+
+
+ +
+
+} + +@section Scripts { + @*TODO : Until script helpers are available, adding script references manually*@ + @*@Scripts.Render("~/bundles/jqueryval")*@ + + +} diff --git a/src/MusicStore/Views/Manage/VerifyPhoneNumber.cshtml b/src/MusicStore/Views/Manage/VerifyPhoneNumber.cshtml index 6ab6abe5cc..d7d1c6508f 100644 --- a/src/MusicStore/Views/Manage/VerifyPhoneNumber.cshtml +++ b/src/MusicStore/Views/Manage/VerifyPhoneNumber.cshtml @@ -10,7 +10,11 @@ @Html.AntiForgeryToken() @Html.Hidden("phoneNumber", @Model.PhoneNumber)

Enter verification code

-
@ViewBag.Status
+

+ For DEMO only: You can type in this code in the below text box to proceed: @ViewBag.Code +
+ Please change this code to register an SMS service in IdentityConfig to send a text message. +


@Html.ValidationSummary("", new { @class = "text-danger" })
diff --git a/src/MusicStore/Views/Shared/DemoCodeDisplay.cshtml b/src/MusicStore/Views/Shared/DemoCodeDisplay.cshtml deleted file mode 100644 index 1b188da8c5..0000000000 --- a/src/MusicStore/Views/Shared/DemoCodeDisplay.cshtml +++ /dev/null @@ -1,15 +0,0 @@ -@{ - ViewBag.Title = "Demo code display page - Not for production use"; -} - -
-

@ViewBag.Title.

-
-
-

- Demo code display page - Not for production use. -

-

- [Demo]: @ViewBag.Status -

-
\ No newline at end of file diff --git a/src/MusicStore/Views/Shared/DemoLinkDisplay.cshtml b/src/MusicStore/Views/Shared/DemoLinkDisplay.cshtml new file mode 100644 index 0000000000..d4ff51bb29 --- /dev/null +++ b/src/MusicStore/Views/Shared/DemoLinkDisplay.cshtml @@ -0,0 +1,21 @@ +@{ + ViewBag.Title = "Demo link display page - Not for production use"; +} + +
+

@ViewBag.Title.

+
+
+

+ Demo link display page - Not for production use. +

+ + @if (ViewBag.Link != null) + { +

+ For DEMO only: You can click this link to confirm the email: [[link]] +
+ Please change this code to register an email service in IdentityConfig to send an email. +

+ } +
\ No newline at end of file diff --git a/src/MusicStore/project.json b/src/MusicStore/project.json index b83b639fc8..a1c3d8c75e 100644 --- a/src/MusicStore/project.json +++ b/src/MusicStore/project.json @@ -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-*", diff --git a/test/E2ETests/Scenarios.cs b/test/E2ETests/Scenarios.cs index 4eb5c11cad..aa111b377c 100644 --- a/test/E2ETests/Scenarios.cs +++ b/test/E2ETests/Scenarios.cs @@ -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(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("[[link]]", 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; } diff --git a/test/E2ETests/SmokeTests.cs b/test/E2ETests/SmokeTests.cs index 7767598398..07c9bea34b 100644 --- a/test/E2ETests/SmokeTests.cs +++ b/test/E2ETests/SmokeTests.cs @@ -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);