diff --git a/.gitignore b/.gitignore index 40e0db6460d0..831a9e4eafc8 100644 --- a/.gitignore +++ b/.gitignore @@ -200,3 +200,4 @@ FakesAssemblies/ samples/MigratingFromMvc5/NewMvc6Project/src/NewMvc6Project/wwwroot/lib/ samples/AngularSample/src/AngularSample/wwwroot/lib project.lock.json +samples/WebApplication1/src/WebApplication1/wwwroot/lib/ diff --git a/samples/WebApplication1/WebApplication1.sln b/samples/WebApplication1/WebApplication1.sln new file mode 100644 index 000000000000..e6e55ad19f44 --- /dev/null +++ b/samples/WebApplication1/WebApplication1.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.22823.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A31DD569-39CD-4AA5-9157-01F60D2A1D5E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4DBCC235-9928-46CA-9DA2-5A70D277B5D8}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WebApplication1", "src\WebApplication1\WebApplication1.xproj", "{75FF878B-38CA-457F-8024-4E3791AC469B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {75FF878B-38CA-457F-8024-4E3791AC469B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75FF878B-38CA-457F-8024-4E3791AC469B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75FF878B-38CA-457F-8024-4E3791AC469B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75FF878B-38CA-457F-8024-4E3791AC469B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {75FF878B-38CA-457F-8024-4E3791AC469B} = {A31DD569-39CD-4AA5-9157-01F60D2A1D5E} + EndGlobalSection +EndGlobal diff --git a/samples/WebApplication1/global.json b/samples/WebApplication1/global.json new file mode 100644 index 000000000000..2b5eb2f04d06 --- /dev/null +++ b/samples/WebApplication1/global.json @@ -0,0 +1,6 @@ +{ + "projects": [ "src", "test" ], + "sdk": { + "version": "1.0.0-beta4-11566" + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Compiler/Preprocess/RazorPreCompilation.cs b/samples/WebApplication1/src/WebApplication1/Compiler/Preprocess/RazorPreCompilation.cs new file mode 100644 index 000000000000..d9c5f888be1f --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Compiler/Preprocess/RazorPreCompilation.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace WebApplication1.Compiler.Preprocess +{ + // Uncomment the following class to enable pre-compilation of Razor views. + // Pre-compilation may reduce the time it takes to build and launch your project. + // Please note, in this pre-release of Visual Studio 2015, enabling pre-compilation may cause IntelliSense and build errors in views using Tag Helpers. + + //public class RazorPreCompilation : RazorPreCompileModule + //{ + // public RazorPreCompilation(IServiceProvider provider) : base(provider) + // { + // GenerateSymbols = true; + // } + //} +} diff --git a/samples/WebApplication1/src/WebApplication1/Controllers/AccountController.cs b/samples/WebApplication1/src/WebApplication1/Controllers/AccountController.cs new file mode 100644 index 000000000000..a6eb04fd2022 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Controllers/AccountController.cs @@ -0,0 +1,449 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.AspNet.Authentication; +using Microsoft.AspNet.Authorization; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.Mvc.Rendering; +using WebApplication1; +using WebApplication1.Models; + +namespace WebApplication1.Controllers +{ + [Authorize] + public class AccountController : Controller + { + public AccountController(UserManager userManager, SignInManager signInManager) + { + UserManager = userManager; + SignInManager = signInManager; + } + + public UserManager UserManager { get; private set; } + + public SignInManager SignInManager { get; private set; } + + // + // GET: /Account/Login + [HttpGet] + [AllowAnonymous] + public IActionResult Login(string returnUrl = null) + { + ViewBag.ReturnUrl = returnUrl; + return View(); + } + + // + // POST: /Account/Login + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task Login(LoginViewModel model, string returnUrl = null) + { + ViewBag.ReturnUrl = returnUrl; + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set shouldLockout: true + var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); + if (result.Succeeded) + { + return RedirectToLocal(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); + } + if (result.IsLockedOut) + { + return View("Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return View(model); + } + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/Register + [HttpGet] + [AllowAnonymous] + public IActionResult Register() + { + return View(); + } + + // + // POST: /Account/Register + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task Register(RegisterViewModel model) + { + if (ModelState.IsValid) + { + var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; + var result = await UserManager.CreateAsync(user, model.Password); + if (result.Succeeded) + { + // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713 + // Send an email with this link + //var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme); + //await MessageServices.SendEmailAsync(model.Email, "Confirm your account", + // "Please confirm your account by clicking this link: link"); + await SignInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction("Index", "Home"); + } + AddErrors(result); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // POST: /Account/LogOff + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult LogOff() + { + SignInManager.SignOut(); + return RedirectToAction("Index", "Home"); + } + + // + // POST: /Account/ExternalLogin + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public IActionResult ExternalLogin(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); + var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return new ChallengeResult(provider, properties); + } + + // + // GET: /Account/ExternalLoginCallback + [HttpGet] + [AllowAnonymous] + public async Task ExternalLoginCallback(string returnUrl = null) + { + var info = await SignInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return RedirectToAction("Login"); + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await SignInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false); + if (result.Succeeded) + { + return RedirectToLocal(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToAction("SendCode", new { ReturnUrl = returnUrl }); + } + if (result.IsLockedOut) + { + return View("Lockout"); + } + else + { + // If the user does not have an account, then ask the user to create an account. + ViewBag.ReturnUrl = returnUrl; + ViewBag.LoginProvider = info.LoginProvider; + var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email); + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); + } + } + + // + // POST: /Account/ExternalLoginConfirmation + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null) + { + if (User.IsSignedIn()) + { + return RedirectToAction("Index", "Manage"); + } + + if (ModelState.IsValid) + { + // Get the information about the user from the external login provider + var info = await SignInManager.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, info); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent: false); + return RedirectToLocal(returnUrl); + } + } + AddErrors(result); + } + + ViewBag.ReturnUrl = returnUrl; + return View(model); + } + + // GET: /Account/ConfirmEmail + [HttpGet] + [AllowAnonymous] + public async Task ConfirmEmail(string userId, string code) + { + if (userId == null || code == null) + { + return View("Error"); + } + var user = await UserManager.FindByIdAsync(userId); + if (user == null) + { + return View("Error"); + } + var result = await UserManager.ConfirmEmailAsync(user, code); + return View(result.Succeeded ? "ConfirmEmail" : "Error"); + } + + // + // GET: /Account/ForgotPassword + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPassword() + { + return View(); + } + + // + // POST: /Account/ForgotPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ForgotPassword(ForgotPasswordViewModel model) + { + if (ModelState.IsValid) + { + var user = await UserManager.FindByNameAsync(model.Email); + if (user == null || !(await UserManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + return View("ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713 + // Send an email with this link + // var code = await UserManager.GeneratePasswordResetTokenAsync(user); + // var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme); + // await MessageServices.SendEmailAsync(model.Email, "Reset Password", + // "Please reset your password by clicking here: link"); + // return View("ForgotPasswordConfirmation"); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/ForgotPasswordConfirmation + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPasswordConfirmation() + { + return View(); + } + + // + // GET: /Account/ResetPassword + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPassword(string code = null) + { + return code == null ? View("Error") : View(); + } + + // + // POST: /Account/ResetPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task 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 + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPasswordConfirmation() + { + return View(); + } + + // + // GET: /Account/SendCode + [HttpGet] + [AllowAnonymous] + public async Task SendCode(string returnUrl = null, bool rememberMe = false) + { + var user = await SignInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + 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 }); + } + + // + // POST: /Account/SendCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task SendCode(SendCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(); + } + + var user = await SignInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + + // Generate the token and send it + var code = await UserManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); + if (string.IsNullOrWhiteSpace(code)) + { + return View("Error"); + } + + var message = "Your security code is: " + code; + if (model.SelectedProvider == "Email") + { + await MessageServices.SendEmailAsync(await UserManager.GetEmailAsync(user), "Security Code", message); + } + else if (model.SelectedProvider == "Phone") + { + await MessageServices.SendSmsAsync(await UserManager.GetPhoneNumberAsync(user), message); + } + + return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); + } + + // + // GET: /Account/VerifyCode + [HttpGet] + [AllowAnonymous] + public async Task VerifyCode(string provider, bool rememberMe, string returnUrl = null) + { + // Require that the user has already logged in via username/password or external login + var user = await SignInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/VerifyCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task 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. + var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); + if (result.Succeeded) + { + return RedirectToLocal(model.ReturnUrl); + } + if (result.IsLockedOut) + { + return View("Lockout"); + } + else + { + ModelState.AddModelError("", "Invalid code."); + return View(model); + } + } + + #region Helpers + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + private async Task GetCurrentUserAsync() + { + return await UserManager.FindByIdAsync(Context.User.GetUserId()); + } + + private IActionResult RedirectToLocal(string returnUrl) + { + if (Url.IsLocalUrl(returnUrl)) + { + return Redirect(returnUrl); + } + else + { + return RedirectToAction("Index", "Home"); + } + } + + #endregion + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Controllers/HomeController.cs b/samples/WebApplication1/src/WebApplication1/Controllers/HomeController.cs new file mode 100644 index 000000000000..450333b7a77e --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Controllers/HomeController.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace WebApplication1.Controllers +{ + public class HomeController : Controller + { + public IActionResult Index() + { + return View(); + } + + public IActionResult About() + { + ViewBag.Message = "Your application description page."; + + return View(); + } + + public IActionResult Contact() + { + ViewBag.Message = "Your contact page."; + + return View(); + } + + public IActionResult Error() + { + return View("~/Views/Shared/Error.cshtml"); + } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Controllers/ManageController.cs b/samples/WebApplication1/src/WebApplication1/Controllers/ManageController.cs new file mode 100644 index 000000000000..31f80c195faf --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Controllers/ManageController.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Security.Claims; +using System.Security.Principal; +using Microsoft.AspNet.Authorization; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Mvc; +using WebApplication1; +using WebApplication1.Models; + +namespace WebApplication1.Controllers +{ + [Authorize] + public class ManageController : Controller + { + public ManageController(UserManager userManager, SignInManager signInManager) + { + UserManager = userManager; + SignInManager = signInManager; + } + + public UserManager UserManager { get; private set; } + + public SignInManager SignInManager { get; private set; } + + // + // GET: /Account/Index + [HttpGet] + public async Task Index(ManageMessageId? message = null) + { + ViewBag.StatusMessage = + message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." + : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." + : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." + : message == ManageMessageId.Error ? "An error has occurred." + : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." + : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." + : ""; + + var user = await GetCurrentUserAsync(); + var model = new IndexViewModel + { + HasPassword = await UserManager.HasPasswordAsync(user), + PhoneNumber = await UserManager.GetPhoneNumberAsync(user), + TwoFactor = await UserManager.GetTwoFactorEnabledAsync(user), + Logins = await UserManager.GetLoginsAsync(user), + BrowserRemembered = await SignInManager.IsTwoFactorClientRememberedAsync(user) + }; + return View(model); + } + + // + // GET: /Account/RemoveLogin + [HttpGet] + public async Task RemoveLogin() + { + var user = await GetCurrentUserAsync(); + var linkedAccounts = await UserManager.GetLoginsAsync(user); + ViewBag.ShowRemoveButton = await UserManager.HasPasswordAsync(user) || linkedAccounts.Count > 1; + return View(linkedAccounts); + } + + // + // POST: /Manage/RemoveLogin + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RemoveLogin(string loginProvider, string providerKey) + { + ManageMessageId? message = ManageMessageId.Error; + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await UserManager.RemoveLoginAsync(user, loginProvider, providerKey); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent: false); + message = ManageMessageId.RemoveLoginSuccess; + } + } + return RedirectToAction("ManageLogins", new { Message = message }); + } + + // + // GET: /Account/AddPhoneNumber + public IActionResult AddPhoneNumber() + { + return View(); + } + + // + // POST: /Account/AddPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task AddPhoneNumber(AddPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + // Generate the token and send it + var user = await GetCurrentUserAsync(); + var code = await UserManager.GenerateChangePhoneNumberTokenAsync(user, model.Number); + await MessageServices.SendSmsAsync(model.Number, "Your security code is: " + code); + return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number }); + } + + // + // POST: /Manage/EnableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EnableTwoFactorAuthentication() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + await UserManager.SetTwoFactorEnabledAsync(user, true); + await SignInManager.SignInAsync(user, isPersistent: false); + } + return RedirectToAction("Index", "Manage"); + } + + // + // POST: /Manage/DisableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DisableTwoFactorAuthentication() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + await UserManager.SetTwoFactorEnabledAsync(user, false); + await SignInManager.SignInAsync(user, isPersistent: false); + } + return RedirectToAction("Index", "Manage"); + } + + // + // GET: /Account/VerifyPhoneNumber + [HttpGet] + public async Task VerifyPhoneNumber(string phoneNumber) + { + var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); + // Send an SMS to verify the phone number + return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); + } + + // + // POST: /Account/VerifyPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task VerifyPhoneNumber(VerifyPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await UserManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess }); + } + } + // If we got this far, something failed, redisplay the form + ModelState.AddModelError(string.Empty, "Failed to verify phone number"); + return View(model); + } + + // + // GET: /Account/RemovePhoneNumber + [HttpGet] + public async Task RemovePhoneNumber() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await UserManager.SetPhoneNumberAsync(user, null); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess }); + } + } + return RedirectToAction("Index", new { Message = ManageMessageId.Error }); + } + + // + // GET: /Manage/ChangePassword + [HttpGet] + public IActionResult ChangePassword() + { + return View(); + } + + // + // POST: /Account/Manage + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ChangePassword(ChangePasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess }); + } + AddErrors(result); + return View(model); + } + return RedirectToAction("Index", new { Message = ManageMessageId.Error }); + } + + // + // GET: /Manage/SetPassword + [HttpGet] + public IActionResult SetPassword() + { + return View(); + } + + // + // POST: /Manage/SetPassword + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SetPassword(SetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await UserManager.AddPasswordAsync(user, model.NewPassword); + if (result.Succeeded) + { + await SignInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess }); + } + AddErrors(result); + return View(model); + } + return RedirectToAction("Index", new { Message = ManageMessageId.Error }); + } + + //GET: /Account/Manage + [HttpGet] + public async Task ManageLogins(ManageMessageId? message = null) + { + ViewBag.StatusMessage = + message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." + : message == ManageMessageId.AddLoginSuccess ? "The external login was added." + : message == ManageMessageId.Error ? "An error has occurred." + : ""; + var user = await GetCurrentUserAsync(); + if (user == null) + { + return View("Error"); + } + var userLogins = await UserManager.GetLoginsAsync(user); + var otherLogins = SignInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != 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 IActionResult LinkLogin(string provider) + { + // Request a redirect to the external login provider to link a login for the current user + var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); + var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, User.GetUserId()); + return new ChallengeResult(provider, properties); + } + + // + // GET: /Manage/LinkLoginCallback + [HttpGet] + public async Task LinkLoginCallback() + { + var user = await GetCurrentUserAsync(); + if (user == null) + { + return View("Error"); + } + var info = await SignInManager.GetExternalLoginInfoAsync(User.GetUserId()); + if (info == null) + { + return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error }); + } + var result = await UserManager.AddLoginAsync(user, info); + var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; + return RedirectToAction("ManageLogins", new { Message = message }); + } + + #region Helpers + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + private async Task HasPhoneNumber() + { + var user = await UserManager.FindByIdAsync(User.GetUserId()); + if (user != null) + { + return user.PhoneNumber != null; + } + return false; + } + + public enum ManageMessageId + { + AddPhoneSuccess, + AddLoginSuccess, + ChangePasswordSuccess, + SetTwoFactorSuccess, + SetPasswordSuccess, + RemoveLoginSuccess, + RemovePhoneSuccess, + Error + } + + private async Task GetCurrentUserAsync() + { + return await UserManager.FindByIdAsync(Context.User.GetUserId()); + } + + private IActionResult RedirectToLocal(string returnUrl) + { + if (Url.IsLocalUrl(returnUrl)) + { + return Redirect(returnUrl); + } + else + { + return RedirectToAction("Index", "Home"); + } + } + + #endregion + } +} diff --git a/samples/WebApplication1/src/WebApplication1/MessageServices.cs b/samples/WebApplication1/src/WebApplication1/MessageServices.cs new file mode 100644 index 000000000000..65e034f4af02 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/MessageServices.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication1 +{ + // This class is used by the application to send Email and SMS when you turn on two-factor authentication in ASP.NET Identity. + // For more details see this link http://go.microsoft.com/fwlink/?LinkID=532713 + public static class MessageServices + { + public static Task SendEmailAsync(string email, string subject, string message) + { + // Plug in your email service here to send an email. + return Task.FromResult(0); + } + public static Task SendSmsAsync(string number, string message) + { + // Plug in your SMS service here to send a text message. + return Task.FromResult(0); + } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Migrations/00000000000000_CreateIdentitySchema.cs b/samples/WebApplication1/src/WebApplication1/Migrations/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 000000000000..5a541f0be049 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Migrations/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Data.Entity; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Data.Entity.Metadata.Builders; +using Microsoft.Data.Entity.Relational.Migrations; +using Microsoft.Data.Entity.Relational.Migrations.Builders; +using Microsoft.Data.Entity.Relational.Migrations.Infrastructure; +using Microsoft.Data.Entity.Relational.Migrations.Operations; +using WebApplication1.Models; + +namespace WebApplication1.Migrations +{ + public partial class CreateIdentitySchema : Migration + { + public override void Up(MigrationBuilder migration) + { + migration.CreateTable( + name: "AspNetUsers", + columns: table => new + { + AccessFailedCount = table.Column(type: "int", nullable: false), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + Email = table.Column(type: "nvarchar(max)", nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + Id = table.Column(type: "nvarchar(450)", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(max)", nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(max)", nullable: true), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + UserName = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + migration.CreateTable( + name: "AspNetRoles", + columns: table => new + { + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + Id = table.Column(type: "nvarchar(450)", nullable: true), + Name = table.Column(type: "nvarchar(max)", nullable: true), + NormalizedName = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + migration.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true), + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:ValueGeneration", "Identity"), + UserId = table.Column(type: "nvarchar(450)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + columns: x => x.UserId, + referencedTable: "AspNetUsers", + referencedColumn: "Id"); + }); + migration.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: true), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + columns: x => x.UserId, + referencedTable: "AspNetUsers", + referencedColumn: "Id"); + }); + migration.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true), + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:ValueGeneration", "Identity"), + RoleId = table.Column(type: "nvarchar(450)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + columns: x => x.RoleId, + referencedTable: "AspNetRoles", + referencedColumn: "Id"); + }); + migration.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + RoleId = table.Column(type: "nvarchar(450)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + columns: x => x.RoleId, + referencedTable: "AspNetRoles", + referencedColumn: "Id"); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + columns: x => x.UserId, + referencedTable: "AspNetUsers", + referencedColumn: "Id"); + }); + } + + public override void Down(MigrationBuilder migration) + { + migration.DropTable("AspNetUserRoles"); + migration.DropTable("AspNetRoleClaims"); + migration.DropTable("AspNetUserLogins"); + migration.DropTable("AspNetUserClaims"); + migration.DropTable("AspNetRoles"); + migration.DropTable("AspNetUsers"); + } + } + + [ContextType(typeof(ApplicationDbContext))] + partial class CreateIdentitySchema + { + public override string Id + { + get { return "00000000000000_CreateIdentitySchema"; } + } + + public override string ProductVersion + { + get { return "7.0.0-beta4"; } + } + + public override IModel Target + { + get + { + var builder = new BasicModelBuilder() + .Annotation("SqlServer:ValueGeneration", "Identity"); + + builder.Entity("WebApplication1.Models.ApplicationUser", b => + { + b.Property("AccessFailedCount") + .Annotation("OriginalValueIndex", 0); + b.Property("ConcurrencyStamp") + .ConcurrencyToken() + .Annotation("OriginalValueIndex", 1); + b.Property("Email") + .Annotation("OriginalValueIndex", 2); + b.Property("EmailConfirmed") + .Annotation("OriginalValueIndex", 3); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 4); + b.Property("LockoutEnabled") + .Annotation("OriginalValueIndex", 5); + b.Property("LockoutEnd") + .Annotation("OriginalValueIndex", 6); + b.Property("NormalizedEmail") + .Annotation("OriginalValueIndex", 7); + b.Property("NormalizedUserName") + .Annotation("OriginalValueIndex", 8); + b.Property("PasswordHash") + .Annotation("OriginalValueIndex", 9); + b.Property("PhoneNumber") + .Annotation("OriginalValueIndex", 10); + b.Property("PhoneNumberConfirmed") + .Annotation("OriginalValueIndex", 11); + b.Property("SecurityStamp") + .Annotation("OriginalValueIndex", 12); + b.Property("TwoFactorEnabled") + .Annotation("OriginalValueIndex", 13); + b.Property("UserName") + .Annotation("OriginalValueIndex", 14); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetUsers"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", b => + { + b.Property("ConcurrencyStamp") + .ConcurrencyToken() + .Annotation("OriginalValueIndex", 0); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 1); + b.Property("Name") + .Annotation("OriginalValueIndex", 2); + b.Property("NormalizedName") + .Annotation("OriginalValueIndex", 3); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetRoles"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("ClaimType") + .Annotation("OriginalValueIndex", 0); + b.Property("ClaimValue") + .Annotation("OriginalValueIndex", 1); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 2) + .Annotation("SqlServer:ValueGeneration", "Default"); + b.Property("RoleId") + .Annotation("OriginalValueIndex", 3); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetRoleClaims"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("ClaimType") + .Annotation("OriginalValueIndex", 0); + b.Property("ClaimValue") + .Annotation("OriginalValueIndex", 1); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 2) + .Annotation("SqlServer:ValueGeneration", "Default"); + b.Property("UserId") + .Annotation("OriginalValueIndex", 3); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetUserClaims"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("LoginProvider") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 0); + b.Property("ProviderDisplayName") + .Annotation("OriginalValueIndex", 1); + b.Property("ProviderKey") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 2); + b.Property("UserId") + .Annotation("OriginalValueIndex", 3); + b.Key("LoginProvider", "ProviderKey"); + b.Annotation("Relational:TableName", "AspNetUserLogins"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("RoleId") + .Annotation("OriginalValueIndex", 0); + b.Property("UserId") + .Annotation("OriginalValueIndex", 1); + b.Key("UserId", "RoleId"); + b.Annotation("Relational:TableName", "AspNetUserRoles"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", "RoleId"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("WebApplication1.Models.ApplicationUser", "UserId"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("WebApplication1.Models.ApplicationUser", "UserId"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", "RoleId"); + b.ForeignKey("WebApplication1.Models.ApplicationUser", "UserId"); + }); + + return builder.Model; + } + } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Migrations/ApplicationDbContextModelSnapshot.cs b/samples/WebApplication1/src/WebApplication1/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 000000000000..5a772c8fd023 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Data.Entity; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Data.Entity.Metadata.Builders; +using Microsoft.Data.Entity.Relational.Migrations.Infrastructure; +using WebApplication1.Models; + +namespace WebApplication1.Migrations +{ + [ContextType(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + public override IModel Model + { + get + { + var builder = new BasicModelBuilder() + .Annotation("SqlServer:ValueGeneration", "Identity"); + + builder.Entity("WebApplication1.Models.ApplicationUser", b => + { + b.Property("AccessFailedCount") + .Annotation("OriginalValueIndex", 0); + b.Property("ConcurrencyStamp") + .ConcurrencyToken() + .Annotation("OriginalValueIndex", 1); + b.Property("Email") + .Annotation("OriginalValueIndex", 2); + b.Property("EmailConfirmed") + .Annotation("OriginalValueIndex", 3); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 4); + b.Property("LockoutEnabled") + .Annotation("OriginalValueIndex", 5); + b.Property("LockoutEnd") + .Annotation("OriginalValueIndex", 6); + b.Property("NormalizedEmail") + .Annotation("OriginalValueIndex", 7); + b.Property("NormalizedUserName") + .Annotation("OriginalValueIndex", 8); + b.Property("PasswordHash") + .Annotation("OriginalValueIndex", 9); + b.Property("PhoneNumber") + .Annotation("OriginalValueIndex", 10); + b.Property("PhoneNumberConfirmed") + .Annotation("OriginalValueIndex", 11); + b.Property("SecurityStamp") + .Annotation("OriginalValueIndex", 12); + b.Property("TwoFactorEnabled") + .Annotation("OriginalValueIndex", 13); + b.Property("UserName") + .Annotation("OriginalValueIndex", 14); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetUsers"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", b => + { + b.Property("ConcurrencyStamp") + .ConcurrencyToken() + .Annotation("OriginalValueIndex", 0); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 1); + b.Property("Name") + .Annotation("OriginalValueIndex", 2); + b.Property("NormalizedName") + .Annotation("OriginalValueIndex", 3); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetRoles"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("ClaimType") + .Annotation("OriginalValueIndex", 0); + b.Property("ClaimValue") + .Annotation("OriginalValueIndex", 1); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 2) + .Annotation("SqlServer:ValueGeneration", "Default"); + b.Property("RoleId") + .Annotation("OriginalValueIndex", 3); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetRoleClaims"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("ClaimType") + .Annotation("OriginalValueIndex", 0); + b.Property("ClaimValue") + .Annotation("OriginalValueIndex", 1); + b.Property("Id") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 2) + .Annotation("SqlServer:ValueGeneration", "Default"); + b.Property("UserId") + .Annotation("OriginalValueIndex", 3); + b.Key("Id"); + b.Annotation("Relational:TableName", "AspNetUserClaims"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("LoginProvider") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 0); + b.Property("ProviderDisplayName") + .Annotation("OriginalValueIndex", 1); + b.Property("ProviderKey") + .GenerateValueOnAdd() + .Annotation("OriginalValueIndex", 2); + b.Property("UserId") + .Annotation("OriginalValueIndex", 3); + b.Key("LoginProvider", "ProviderKey"); + b.Annotation("Relational:TableName", "AspNetUserLogins"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.Property("RoleId") + .Annotation("OriginalValueIndex", 0); + b.Property("UserId") + .Annotation("OriginalValueIndex", 1); + b.Key("UserId", "RoleId"); + b.Annotation("Relational:TableName", "AspNetUserRoles"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", "RoleId"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("WebApplication1.Models.ApplicationUser", "UserId"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("WebApplication1.Models.ApplicationUser", "UserId"); + }); + + builder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", b => + { + b.ForeignKey("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", "RoleId"); + b.ForeignKey("WebApplication1.Models.ApplicationUser", "UserId"); + }); + + return builder.Model; + } + } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Models/AccountViewModels.cs b/samples/WebApplication1/src/WebApplication1/Models/AccountViewModels.cs new file mode 100644 index 000000000000..7c02c7c712a5 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Models/AccountViewModels.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Mvc.Rendering; + +namespace WebApplication1.Models +{ + public class ExternalLoginConfirmationViewModel + { + [Required] + [Display(Name = "Email")] + public string Email { get; set; } + } + + public class SendCodeViewModel + { + public string SelectedProvider { get; set; } + public ICollection Providers { get; set; } + public string ReturnUrl { get; set; } + public bool RememberMe { get; set; } + } + + public class VerifyCodeViewModel + { + [Required] + public string Provider { get; set; } + + [Required] + [Display(Name = "Code")] + public string Code { get; set; } + public string ReturnUrl { get; set; } + + [Display(Name = "Remember this browser?")] + public bool RememberBrowser { get; set; } + + public bool RememberMe { 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] + [Display(Name = "Email")] + public string Email { get; set; } + } + + public class LoginViewModel + { + [Required] + [Display(Name = "Email")] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } + + public class RegisterViewModel + { + [Required] + [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; } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Models/IdentityModels.cs b/samples/WebApplication1/src/WebApplication1/Models/IdentityModels.cs new file mode 100644 index 000000000000..2b1ef9359d04 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Models/IdentityModels.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.EntityFramework; +using Microsoft.Data.Entity; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Framework.OptionsModel; + +namespace WebApplication1.Models +{ + // Add profile data for application users by adding properties to the ApplicationUser class + public class ApplicationUser : IdentityUser + { + } + + public class ApplicationDbContext : IdentityDbContext + { + private static bool _created; + + public ApplicationDbContext() + { + // Create the database and schema if it doesn't exist + if (!_created) + { + Database.AsRelational().ApplyMigrations(); + _created = true; + } + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + // Customize the ASP.NET Identity model and override the defaults if needed. + // For example, you can rename the ASP.NET Identity table names and more. + // Add your customizations after calling base.OnModelCreating(builder); + } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Models/ManageViewModels.cs b/samples/WebApplication1/src/WebApplication1/Models/ManageViewModels.cs new file mode 100644 index 000000000000..c332ad0fcab1 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Models/ManageViewModels.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Http.Authentication; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Mvc.Rendering; + +namespace WebApplication1.Models +{ + public class IndexViewModel + { + public bool HasPassword { get; set; } + public IList Logins { get; set; } + public string PhoneNumber { get; set; } + public bool TwoFactor { get; set; } + public bool BrowserRemembered { get; set; } + } + + public class ManageLoginsViewModel + { + public IList CurrentLogins { get; set; } + public IList 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 Providers { get; set; } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Project_Readme.html b/samples/WebApplication1/src/WebApplication1/Project_Readme.html new file mode 100644 index 000000000000..bc2318453033 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Project_Readme.html @@ -0,0 +1,203 @@ + + + + + Welcome to ASP.NET 5 + + + + + + + + + + diff --git a/samples/WebApplication1/src/WebApplication1/Properties/AppSettings.cs b/samples/WebApplication1/src/WebApplication1/Properties/AppSettings.cs new file mode 100644 index 000000000000..d5c297e68001 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Properties/AppSettings.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebApplication1 +{ + public class AppSettings + { + public string SiteTitle { get; set; } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Startup.cs b/samples/WebApplication1/src/WebApplication1/Startup.cs new file mode 100644 index 000000000000..72c1ab893866 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Startup.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Authentication.Facebook; +using Microsoft.AspNet.Authentication.Google; +using Microsoft.AspNet.Authentication.MicrosoftAccount; +using Microsoft.AspNet.Authentication.Twitter; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Diagnostics; +using Microsoft.AspNet.Diagnostics.Entity; +using Microsoft.AspNet.Hosting; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.EntityFramework; +using Microsoft.AspNet.Routing; +using Microsoft.Data.Entity; +using Microsoft.Framework.ConfigurationModel; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.Logging; +using Microsoft.Framework.Logging.Console; +using Microsoft.Framework.Runtime; +using WebApplication1.Models; + +namespace WebApplication1 +{ + public class Startup + { + public Startup(IHostingEnvironment env) + { + // Setup configuration sources. + var configuration = new Configuration() + .AddJsonFile("config.json") + .AddJsonFile($"config.{env.EnvironmentName}.json", optional: true); + + if (env.IsEnvironment("Development")) + { + // This reads the configuration keys from the secret store. + // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709 + configuration.AddUserSecrets(); + } + configuration.AddEnvironmentVariables(); + Configuration = configuration; + } + + public IConfiguration Configuration { get; set; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add Application settings to the services container. + services.Configure(Configuration.GetSubKey("AppSettings")); + + // Add EF services to the services container. + services.AddEntityFramework() + .AddSqlServer() + .AddDbContext(options => + options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"])); + + // Add Identity services to the services container. + services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + + // Configure the options for the authentication middleware. + // You can add options for Google, Twitter and other middleware as shown below. + // For more information see http://go.microsoft.com/fwlink/?LinkID=532715 + services.Configure(options => + { + options.AppId = Configuration["Authentication:Facebook:AppId"]; + options.AppSecret = Configuration["Authentication:Facebook:AppSecret"]; + }); + + services.Configure(options => + { + options.ClientId = Configuration["Authentication:MicrosoftAccount:ClientId"]; + options.ClientSecret = Configuration["Authentication:MicrosoftAccount:ClientSecret"]; + }); + + // Add MVC services to the services container. + services.AddMvc(); + + // Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers. + // You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json. + // services.AddWebApiConventions(); + } + + // Configure is called after ConfigureServices is called. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) + { + // Configure the HTTP request pipeline. + + // Add the console logger. + loggerfactory.AddConsole(minLevel: LogLevel.Warning); + + // Add the following to the request pipeline only in development environment. + if (env.IsEnvironment("Development")) + { + app.UseBrowserLink(); + app.UseErrorPage(ErrorPageOptions.ShowAll); + app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll); + } + else + { + // Add Error handling middleware which catches all application specific errors and + // sends the request to the following path or controller action. + app.UseErrorHandler("/Home/Error"); + } + + // Add static files to the request pipeline. + app.UseStaticFiles(); + + // Add cookie-based authentication to the request pipeline. + app.UseIdentity(); + + // Add authentication middleware to the request pipeline. You can configure options such as Id and Secret in the ConfigureServices method. + // For more information see http://go.microsoft.com/fwlink/?LinkID=532715 + // app.UseFacebookAuthentication(); + // app.UseGoogleAuthentication(); + // app.UseMicrosoftAccountAuthentication(); + // app.UseTwitterAuthentication(); + + // Add MVC to the request pipeline. + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller}/{action}/{id?}", + defaults: new { controller = "Home", action = "Index" }); + + // Uncomment the following line to add a route for porting Web API 2 controllers. + // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}"); + }); + } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/ConfirmEmail.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/ConfirmEmail.cshtml new file mode 100644 index 000000000000..f68e3a80f5fa --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/ConfirmEmail.cshtml @@ -0,0 +1,10 @@ +@{ + ViewBag.Title = "Confirm Email"; +} + +

@ViewBag.Title.

+
+

+ Thank you for confirming your email. Please Click here to Log in. +

+
diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginConfirmation.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginConfirmation.cshtml new file mode 100644 index 000000000000..424f01994620 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginConfirmation.cshtml @@ -0,0 +1,35 @@ +@model ExternalLoginConfirmationViewModel +@{ + ViewBag.Title = "Register"; +} + +

@ViewBag.Title.

+

Associate your @ViewBag.LoginProvider account.

+ +
+

Association Form

+
+
+ +

+ You've successfully authenticated with @ViewBag.LoginProvider. + Please enter a user name for this site below and click the Register button to finish + logging in. +

+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginFailure.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginFailure.cshtml new file mode 100644 index 000000000000..3be4ab37d49b --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginFailure.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Login Failure"; +} + +
+

@ViewBag.Title.

+

Unsuccessful login with service.

+
diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPassword.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPassword.cshtml new file mode 100644 index 000000000000..228834f5a201 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPassword.cshtml @@ -0,0 +1,31 @@ +@model ForgotPasswordViewModel +@{ + ViewBag.Title = "Forgot your password?"; +} + +

@ViewBag.Title.

+

+ For more information on how to enable reset password please see this article. +

+ +@*
+

Enter your email.

+
+
+
+ +
+ + +
+
+
+
+ +
+
+
*@ + +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPasswordConfirmation.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPasswordConfirmation.cshtml new file mode 100644 index 000000000000..85496eafcbe6 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPasswordConfirmation.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Forgot Password Confirmation"; +} + +

@ViewBag.Title.

+

+ Please check your email to reset your password. +

diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/Login.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/Login.cshtml new file mode 100644 index 000000000000..0e0e185d3412 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/Login.cshtml @@ -0,0 +1,91 @@ +@model LoginViewModel +@using System.Collections.Generic +@using Microsoft.AspNet.Http +@using Microsoft.AspNet.Http.Authentication +@inject SignInManager SignInManager + +@{ + ViewBag.Title = "Log in"; +} + +

@ViewBag.Title.

+
+
+
+
+

Use a local account to log in.

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ + +
+
+
+
+
+ +
+
+

+ Register as a new user? +

+

+ Forgot your password? +

+
+
+
+
+
+

Use another service to log in.

+
+ @{ + var LoginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList(); + if (LoginProviders.Count == 0) + { +
+

+ There are no external authentication services configured. See this article + for details on setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+ +
+

+ @foreach (AuthenticationDescription p in LoginProviders) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/Register.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/Register.cshtml new file mode 100644 index 000000000000..36d9d9623e88 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/Register.cshtml @@ -0,0 +1,42 @@ +@model RegisterViewModel +@{ + ViewBag.Title = "Register"; +} + +

@ViewBag.Title.

+ +
+

Create a new account.

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/ResetPassword.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/ResetPassword.cshtml new file mode 100644 index 000000000000..2d1fa9513f80 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/ResetPassword.cshtml @@ -0,0 +1,43 @@ +@model ResetPasswordViewModel +@{ + ViewBag.Title = "Reset password"; +} + +

@ViewBag.Title.

+ +
+

Reset your password.

+
+
+ +
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/ResetPasswordConfirmation.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/ResetPasswordConfirmation.cshtml new file mode 100644 index 000000000000..16a918f4b25d --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/ResetPasswordConfirmation.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "Reset password confirmation"; +} + +

@ViewBag.Title.

+

+ Your password has been reset. Please Click here to log in. +

diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/SendCode.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/SendCode.cshtml new file mode 100644 index 000000000000..b16de7225d40 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/SendCode.cshtml @@ -0,0 +1,21 @@ +@model SendCodeViewModel +@{ + ViewBag.Title = "Send Verification Code"; +} + +

@ViewBag.Title.

+ +
+ +
+
+ Select Two-Factor Authentication Provider: + + +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Account/VerifyCode.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Account/VerifyCode.cshtml new file mode 100644 index 000000000000..43210308141c --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Account/VerifyCode.cshtml @@ -0,0 +1,38 @@ +@model VerifyCodeViewModel +@{ + ViewBag.Title = "Verify"; +} + +

@ViewBag.Title.

+ +
+
+ + +

@ViewBag.Status

+
+
+ +
+ + +
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Home/About.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Home/About.cshtml new file mode 100644 index 000000000000..4b2d9e8440b9 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Home/About.cshtml @@ -0,0 +1,7 @@ +@{ + ViewBag.Title = "About"; +} +

@ViewBag.Title.

+

@ViewBag.Message

+ +

Use this area to provide additional information.

diff --git a/samples/WebApplication1/src/WebApplication1/Views/Home/Contact.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Home/Contact.cshtml new file mode 100644 index 000000000000..01436993cf9b --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Home/Contact.cshtml @@ -0,0 +1,17 @@ +@{ + ViewBag.Title = "Contact"; +} +

@ViewBag.Title.

+

@ViewBag.Message

+ +
+ One Microsoft Way
+ Redmond, WA 98052-6399
+ P: + 425.555.0100 +
+ +
+ Support: Support@example.com
+ Marketing: Marketing@example.com +
diff --git a/samples/WebApplication1/src/WebApplication1/Views/Home/Index.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Home/Index.cshtml new file mode 100644 index 000000000000..84847d7769e2 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Home/Index.cshtml @@ -0,0 +1,98 @@ +@{ + ViewBag.Title = "Home Page"; +} + + + + diff --git a/samples/WebApplication1/src/WebApplication1/Views/Manage/AddPhoneNumber.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Manage/AddPhoneNumber.cshtml new file mode 100644 index 000000000000..d8708eaf1abe --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Manage/AddPhoneNumber.cshtml @@ -0,0 +1,27 @@ +@model AddPhoneNumberViewModel +@{ + ViewBag.Title = "Add Phone Number"; +} + +

@ViewBag.Title.

+
+

Add a phone number.

+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Manage/ChangePassword.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Manage/ChangePassword.cshtml new file mode 100644 index 000000000000..d4fba73fb17e --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Manage/ChangePassword.cshtml @@ -0,0 +1,42 @@ +@model ChangePasswordViewModel +@{ + ViewBag.Title = "Change Password"; +} + +

@ViewBag.Title.

+ +
+

Change Password Form

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Manage/Index.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Manage/Index.cshtml new file mode 100644 index 000000000000..77d0cace4be3 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Manage/Index.cshtml @@ -0,0 +1,80 @@ +@model IndexViewModel +@{ + ViewBag.Title = "Manage your account"; +} + +

@ViewBag.Title.

+

@ViewBag.StatusMessage

+
+

Change your account settings

+
+
+
Password:
+
+ [ + @if (Model.HasPassword) + { + Change + } + else + { + Create + } + ] +
+
External Logins:
+
+ + @Model.Logins.Count [Manage] +
+ + +
Phone Number:
+
+

+ Phone Numbers can used as a second factor of verification in two-factor authentication. + See this article + for details on setting up this ASP.NET application to support two-factor authentication using SMS. +

+ + @*@(Model.PhoneNumber ?? "None") [ + @if (Model.PhoneNumber != null) + { + Change + @:  |  + Remove + } + else + { + Add + } + ]*@ +
+ +
Two-Factor Authentication:
+
+

+ There are no two-factor authentication providers configured. See this article + for setting up this application to support two-factor authentication. +

+ @*@if (Model.TwoFactor) + { +
+ + Enabled + + +
+ } + else + { +
+ + Disabled + + +
+ }*@ +
+
+
diff --git a/samples/WebApplication1/src/WebApplication1/Views/Manage/ManageLogins.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Manage/ManageLogins.cshtml new file mode 100644 index 000000000000..e016ba680367 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Manage/ManageLogins.cshtml @@ -0,0 +1,54 @@ +@model ManageLoginsViewModel +@using Microsoft.AspNet.Http.Authentication +@{ + ViewBag.Title = "Manage your external logins"; +} + +

@ViewBag.Title.

+ +

@ViewBag.StatusMessage

+@if (Model.CurrentLogins.Count > 0) +{ +

Registered Logins

+ + + @foreach (var account in Model.CurrentLogins) + { + + + + + } + +
@account.LoginProvider + @if (ViewBag.ShowRemoveButton) + { +
+
+ + + +
+
+ } + else + { + @:   + } +
+} +@if (Model.OtherLogins.Count > 0) +{ +

Add another service to log in.

+
+
+
+

+ @foreach (AuthenticationDescription p in Model.OtherLogins) + { + + } +

+
+
+} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Manage/RemoveLogin.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Manage/RemoveLogin.cshtml new file mode 100644 index 000000000000..94f66c8eb118 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Manage/RemoveLogin.cshtml @@ -0,0 +1,35 @@ +@model ICollection +@{ + ViewBag.Title = "Remove Login"; +} + +@if (Model.Count > 0) +{ +

Registered Logins

+ + + @foreach (var account in Model) + { + + + + + } + +
@account.LoginProvider + @if (ViewBag.ShowRemoveButton) + { +
+
+ + + +
+
+ } + else + { + @:   + } +
+} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Manage/SetPassword.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Manage/SetPassword.cshtml new file mode 100644 index 000000000000..eb3ca19be680 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Manage/SetPassword.cshtml @@ -0,0 +1,38 @@ +@model SetPasswordViewModel +@{ + ViewBag.Title = "Set Password"; +} + +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+ +
+

Set your password

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Manage/VerifyPhoneNumber.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Manage/VerifyPhoneNumber.cshtml new file mode 100644 index 000000000000..6f71c6e47f95 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Manage/VerifyPhoneNumber.cshtml @@ -0,0 +1,30 @@ +@model VerifyPhoneNumberViewModel +@{ + ViewBag.Title = "Verify Phone Number"; +} + +

@ViewBag.Title.

+ +
+ +

Add a phone number.

+
@ViewBag.Status
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Shared/Error.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Shared/Error.cshtml new file mode 100644 index 000000000000..26ebcf01106c --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Shared/Error.cshtml @@ -0,0 +1,6 @@ +@{ + ViewBag.Title = "Error"; +} + +

Error.

+

An error occurred while processing your request.

diff --git a/samples/WebApplication1/src/WebApplication1/Views/Shared/_Layout.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Shared/_Layout.cshtml new file mode 100644 index 000000000000..1351720cf3f7 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Shared/_Layout.cshtml @@ -0,0 +1,80 @@ +@inject IOptions AppSettings + + + + + + @ViewBag.Title - @AppSettings.Options.SiteTitle + + + + + + + + + + + + + + +
+ @RenderBody() +
+
+

© 2015 - @AppSettings.Options.SiteTitle

+
+
+ + + + + + + + + + + + + + + @RenderSection("scripts", required: false) + + diff --git a/samples/WebApplication1/src/WebApplication1/Views/Shared/_LoginPartial.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Shared/_LoginPartial.cshtml new file mode 100644 index 000000000000..4c10e1f93e43 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Shared/_LoginPartial.cshtml @@ -0,0 +1,20 @@ +@using System.Security.Claims + +@if (User.Identity.IsAuthenticated) +{ + +} +else +{ + +} diff --git a/samples/WebApplication1/src/WebApplication1/Views/Shared/_ValidationScriptsPartial.cshtml b/samples/WebApplication1/src/WebApplication1/Views/Shared/_ValidationScriptsPartial.cshtml new file mode 100644 index 000000000000..a4ada79974c3 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/Shared/_ValidationScriptsPartial.cshtml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/samples/WebApplication1/src/WebApplication1/Views/_GlobalImport.cshtml b/samples/WebApplication1/src/WebApplication1/Views/_GlobalImport.cshtml new file mode 100644 index 000000000000..7f19f5008b24 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/_GlobalImport.cshtml @@ -0,0 +1,5 @@ +@using WebApplication1 +@using WebApplication1.Models +@using Microsoft.Framework.OptionsModel +@using Microsoft.AspNet.Identity +@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" diff --git a/samples/WebApplication1/src/WebApplication1/Views/_ViewStart.cshtml b/samples/WebApplication1/src/WebApplication1/Views/_ViewStart.cshtml new file mode 100644 index 000000000000..a5f10045db97 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/samples/WebApplication1/src/WebApplication1/WebApplication1.xproj b/samples/WebApplication1/src/WebApplication1/WebApplication1.xproj new file mode 100644 index 000000000000..ab990a0f8b9f --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/WebApplication1.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 75ff878b-38ca-457f-8024-4e3791ac469b + WebApplication1 + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 34967 + + + \ No newline at end of file diff --git a/samples/WebApplication1/src/WebApplication1/bower.json b/samples/WebApplication1/src/WebApplication1/bower.json new file mode 100644 index 000000000000..599b015f96c5 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/bower.json @@ -0,0 +1,12 @@ +{ + "name": "ASP.NET", + "private": true, + "dependencies": { + "bootstrap": "3.0.0", + "jquery": "1.10.2", + "jquery-validation": "1.11.1", + "jquery-validation-unobtrusive": "3.2.2", + "hammer.js": "2.0.4", + "bootstrap-touch-carousel": "0.8.0" + } +} diff --git a/samples/WebApplication1/src/WebApplication1/config.json b/samples/WebApplication1/src/WebApplication1/config.json new file mode 100644 index 000000000000..58c57a96c824 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/config.json @@ -0,0 +1,10 @@ +{ + "AppSettings": { + "SiteTitle": "WebApplication1" + }, + "Data": { + "DefaultConnection": { + "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-WebApplication1-095c9ba6-2118-447e-a685-688c1519c20e;Trusted_Connection=True;MultipleActiveResultSets=true" + } + } +} diff --git a/samples/WebApplication1/src/WebApplication1/gulpfile.js b/samples/WebApplication1/src/WebApplication1/gulpfile.js new file mode 100644 index 000000000000..4c1e6c7bc9fc --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/gulpfile.js @@ -0,0 +1,32 @@ +/// + +var gulp = require("gulp"), + rimraf = require("rimraf"), + fs = require("fs"); + +eval("var project = " + fs.readFileSync("./project.json")); + +var paths = { + bower: "./bower_components/", + lib: "./" + project.webroot + "/lib/" +}; + +gulp.task("clean", function (cb) { + rimraf(paths.lib, cb); +}); + +gulp.task("copy", ["clean"], function () { + var bower = { + "bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}", + "bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}", + "hammer.js": "hammer.js/hammer*.{js,map}", + "jquery": "jquery/jquery*.{js,map}", + "jquery-validation": "jquery-validation/jquery.validate.js", + "jquery-validation-unobtrusive": "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js" + } + + for (var destinationDir in bower) { + gulp.src(paths.bower + bower[destinationDir]) + .pipe(gulp.dest(paths.lib + destinationDir)); + } +}); diff --git a/samples/WebApplication1/src/WebApplication1/package.json b/samples/WebApplication1/src/WebApplication1/package.json new file mode 100644 index 000000000000..838ba761987c --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/package.json @@ -0,0 +1,8 @@ +{ + "name": "ASP.NET", + "version": "0.0.0", + "devDependencies": { + "gulp": "3.8.11", + "rimraf": "2.2.8" + } +} diff --git a/samples/WebApplication1/src/WebApplication1/project.json b/samples/WebApplication1/src/WebApplication1/project.json new file mode 100644 index 000000000000..307a553edd38 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/project.json @@ -0,0 +1,58 @@ +{ + "webroot": "wwwroot", + "userSecretsId": "aspnet5-WebApplication1-095c9ba6-2118-447e-a685-688c1519c20e", + "version": "1.0.0-*", + + "dependencies": { + "EntityFramework.SqlServer": "7.0.0-beta4", + "EntityFramework.Commands": "7.0.0-beta4", + "Microsoft.AspNet.Mvc": "6.0.0-beta4", + "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta4", + "Microsoft.AspNet.Authentication.Cookies": "1.0.0-beta4", + "Microsoft.AspNet.Authentication.Facebook": "1.0.0-beta4", + "Microsoft.AspNet.Authentication.Google": "1.0.0-beta4", + "Microsoft.AspNet.Authentication.MicrosoftAccount": "1.0.0-beta4", + "Microsoft.AspNet.Authentication.Twitter": "1.0.0-beta4", + "Microsoft.AspNet.Diagnostics": "1.0.0-beta4", + "Microsoft.AspNet.Diagnostics.Entity": "7.0.0-beta4", + "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-beta4", + "Microsoft.AspNet.Server.IIS": "1.0.0-beta4", + "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4", + "Microsoft.AspNet.StaticFiles": "1.0.0-beta4", + "Microsoft.AspNet.Tooling.Razor": "1.0.0-beta4", + "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4", + "Microsoft.Framework.ConfigurationModel.UserSecrets": "1.0.0-beta4", + "Microsoft.Framework.CodeGenerators.Mvc": "1.0.0-beta4", + "Microsoft.Framework.Logging": "1.0.0-beta4", + "Microsoft.Framework.Logging.Console": "1.0.0-beta4", + "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta4" + }, + + "commands": { + "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000", + "gen": "Microsoft.Framework.CodeGeneration", + "ef": "EntityFramework.Commands" + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + }, + + "exclude": [ + "wwwroot", + "node_modules", + "bower_components" + ], + "publishExclude": [ + "node_modules", + "bower_components", + "**.xproj", + "**.user", + "**.vspscc" + ], + "scripts": { + "postrestore": [ "npm install", "bower install" ], + "prepare": [ "gulp copy" ] + } +} diff --git a/samples/WebApplication1/src/WebApplication1/wwwroot/_references.js b/samples/WebApplication1/src/WebApplication1/wwwroot/_references.js new file mode 100644 index 000000000000..4e6ad34f16de --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/wwwroot/_references.js @@ -0,0 +1,9 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// diff --git a/samples/WebApplication1/src/WebApplication1/wwwroot/css/site.css b/samples/WebApplication1/src/WebApplication1/wwwroot/css/site.css new file mode 100644 index 000000000000..1eaae3d04347 --- /dev/null +++ b/samples/WebApplication1/src/WebApplication1/wwwroot/css/site.css @@ -0,0 +1,34 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +/* Wrapping element */ +/* Set some basic padding to keep content from hitting the edges */ +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +/* Set widths on the form inputs since otherwise they're 100% wide */ +input, +select, +textarea { + max-width: 280px; +} + +/* Carousel */ +.carousel-caption { + z-index: 10 !important; +} + + .carousel-caption p { + font-size: 20px; + line-height: 1.4; + } + +@media (min-width: 768px) { + .carousel-caption { + z-index: 10 !important; + } +} diff --git a/samples/WebApplication1/src/WebApplication1/wwwroot/favicon.ico b/samples/WebApplication1/src/WebApplication1/wwwroot/favicon.ico new file mode 100644 index 000000000000..a3a799985c43 Binary files /dev/null and b/samples/WebApplication1/src/WebApplication1/wwwroot/favicon.ico differ diff --git a/samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-01.png b/samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-01.png new file mode 100644 index 000000000000..ad3c267c7d40 Binary files /dev/null and b/samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-01.png differ diff --git a/samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-02.png b/samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-02.png new file mode 100644 index 000000000000..2f456c0bb828 Binary files /dev/null and b/samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-02.png differ diff --git a/samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-01-Azure.png b/samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-01-Azure.png new file mode 100644 index 000000000000..59fb923e0580 Binary files /dev/null and b/samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-01-Azure.png differ diff --git a/samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-02-VS.png b/samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-02-VS.png new file mode 100644 index 000000000000..c9f461137e80 Binary files /dev/null and b/samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-02-VS.png differ