diff --git a/src/MusicStore/Controllers/AccountController.cs b/src/MusicStore/Controllers/AccountController.cs index e6eb8690ae..14e80a7338 100644 --- a/src/MusicStore/Controllers/AccountController.cs +++ b/src/MusicStore/Controllers/AccountController.cs @@ -1,11 +1,12 @@ using System.Linq; using System.Security.Principal; 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; +using Microsoft.AspNet.Http; +using System.Security.Claims; namespace MusicStore.Controllers { @@ -63,26 +64,20 @@ namespace MusicStore.Controllers // // GET: /Account/VerifyCode - //TODO : Some of the identity helpers not implemented [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) + var user = await SignInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == 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()); + // Remove before production #if DEMO if (user != null) { - var code = await UserManager.GenerateTwoFactorTokenAsync(user, provider); - ViewBag.Code = code; + ViewBag.Code = await UserManager.GenerateTwoFactorTokenAsync(user, provider); } #endif return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); @@ -104,7 +99,7 @@ namespace MusicStore.Controllers // 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 - var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberClient: model.RememberBrowser); + var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); switch (result) { case SignInStatus.Success: @@ -174,6 +169,10 @@ namespace MusicStore.Controllers } var user = await SignInManager.UserManager.FindByIdAsync(userId); + if (user == null) + { + return View("Error"); + } var result = await UserManager.ConfirmEmailAsync(user, code); return View(result.Succeeded ? "ConfirmEmail" : "Error"); } @@ -208,7 +207,7 @@ namespace MusicStore.Controllers var callbackUrl = Url.Action("ResetPassword", "Account", new { code = code }, protocol: Context.Request.Scheme); await UserManager.SendEmailAsync(user, "Reset Password", "Please reset your password by clicking here"); #if !DEMO - return RedirectToAction("ForgotPasswordConfirmation", "Account"); + return RedirectToAction("ForgotPasswordConfirmation"); #else //To display the email link in a friendly page instead of sending email ViewBag.Link = callbackUrl; @@ -235,6 +234,7 @@ namespace MusicStore.Controllers [AllowAnonymous] public ActionResult ResetPassword(string code) { + //TODO: Fix this? var resetPasswordViewModel = new ResetPasswordViewModel() { Code = code }; return code == null ? View("Error") : View(resetPasswordViewModel); } @@ -281,8 +281,9 @@ namespace MusicStore.Controllers public ActionResult ExternalLogin(string provider, string returnUrl = null) { // 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 }); + var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); + var properties = Context.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return new ChallengeResult(provider, properties); } // @@ -290,14 +291,13 @@ namespace MusicStore.Controllers [AllowAnonymous] public async Task SendCode(bool rememberMe, string returnUrl = null) { - //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) + //TODO : Default rememberMe as well? + var user = await SignInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) { return View("Error"); } - var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(new ApplicationUser() { Id = userId }); + var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(user); var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); } @@ -327,18 +327,14 @@ namespace MusicStore.Controllers [AllowAnonymous] public async Task ExternalLoginCallback(string returnUrl = null) { - //TODO: Helper not available - //var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); - var loginInfo = await Context.AuthenticateAsync("External"); + var loginInfo = await Context.GetExternalLoginInfo(); if (loginInfo == null) { return RedirectToAction("Login"); } - //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. + var result = await SignInManager.ExternalLoginSignInAsync(loginInfo.LoginProvider, loginInfo.ProviderKey, isPersistent: false); switch (result) { case SignInStatus.Success: @@ -351,14 +347,15 @@ namespace MusicStore.Controllers 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 }); + ViewBag.LoginProvider = loginInfo.LoginProvider; + // REVIEW: handle case where email not in claims? + var email = loginInfo.ExternalIdentity.FindFirstValue(ClaimTypes.Email); + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); } } // // POST: /Account/ExternalLoginConfirmation - // TODO: Some of the identity helpers not available [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] @@ -372,8 +369,7 @@ namespace MusicStore.Controllers 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"); + var info = await Context.GetExternalLoginInfo(); if (info == null) { return View("ExternalLoginFailure"); @@ -382,11 +378,9 @@ namespace MusicStore.Controllers 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); + result = await UserManager.AddLoginAsync(user, info); if (result.Succeeded) { - // TODO: rememberBrowser option not being taken in SignInAsync await SignInManager.SignInAsync(user, isPersistent: false); return RedirectToLocal(returnUrl); } diff --git a/src/MusicStore/Controllers/ManageController.cs b/src/MusicStore/Controllers/ManageController.cs index a122240b60..62b59dece9 100644 --- a/src/MusicStore/Controllers/ManageController.cs +++ b/src/MusicStore/Controllers/ManageController.cs @@ -1,17 +1,17 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using System.Security.Principal; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Mvc; using MusicStore.Models; using System.Linq; -using Microsoft.AspNet.Http.Security; +using Microsoft.AspNet.Http; namespace MusicStore.Controllers { /// /// Summary description for ManageController /// + [Authorize] public class ManageController : Controller { public ManageController(UserManager userManager, SignInManager signInManager) @@ -139,8 +139,9 @@ 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.Code = code; +#if DEMO + ViewBag.Code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); +#endif return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); } @@ -282,6 +283,7 @@ namespace MusicStore.Controllers { ViewBag.StatusMessage = message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." + : message == ManageMessageId.AddLoginSuccess ? "The external login was added." : message == ManageMessageId.Error ? "An error has occurred." : ""; var user = await GetCurrentUserAsync(); @@ -290,8 +292,7 @@ namespace MusicStore.Controllers 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(); + var otherLogins = Context.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList(); ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1; return View(new ManageLoginsViewModel { @@ -307,30 +308,31 @@ namespace MusicStore.Controllers 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); + var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); + var properties = Context.ConfigureExternalAuthenticationProperties(provider, redirectUrl, User.Identity.GetUserId()); + return new ChallengeResult(provider, properties); } // // 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()) + var user = await GetCurrentUserAsync(); + if(user == null) + { + return View("Error"); + } + var loginInfo = await Context.GetExternalLoginInfo(User.Identity.GetUserId()); + if (loginInfo == null) { 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 }); + var result = await UserManager.AddLoginAsync(user, loginInfo); + var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; + return RedirectToAction("ManageLogins", new { Message = message }); } #region Helpers - // Used for XSRF protection when adding external logins - private const string XsrfKey = "XsrfId"; private void AddErrors(IdentityResult result) { @@ -340,6 +342,7 @@ namespace MusicStore.Controllers } } + //TODO: No caller - do we need this? private async Task HasPhoneNumber() { var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); @@ -353,6 +356,7 @@ namespace MusicStore.Controllers public enum ManageMessageId { AddPhoneSuccess, + AddLoginSuccess, ChangePasswordSuccess, SetTwoFactorSuccess, SetPasswordSuccess, diff --git a/src/MusicStore/Views/Account/_ExternalLoginsListPartial.cshtml b/src/MusicStore/Views/Account/_ExternalLoginsListPartial.cshtml index d830b9446c..b03637a756 100644 --- a/src/MusicStore/Views/Account/_ExternalLoginsListPartial.cshtml +++ b/src/MusicStore/Views/Account/_ExternalLoginsListPartial.cshtml @@ -1,11 +1,11 @@ @model MusicStore.Models.ExternalLoginListViewModel +@using Microsoft.AspNet.Http; @using Microsoft.AspNet.Http.Security;

Use another service to log in.


@{ - //TODO: Need to replace with the helper that filters non external ones. GetExternalAuthenticationTypes() - var loginProviders = Context.GetAuthenticationTypes(); + var loginProviders = Context.GetExternalAuthenticationTypes(); if (loginProviders.Count() == 0) {
@@ -22,7 +22,6 @@ @Html.AntiForgeryToken()

- @*TODO: Temporary hack for the above issue. Needs to be removed once GetExternalAuthenticationTypes() is available*@ @foreach (AuthenticationDescription p in loginProviders.Where(a => a.Caption != null)) {