From 751974c7995e219ac0cb5d964211f710ec510a66 Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Wed, 13 May 2015 12:39:23 -0400 Subject: [PATCH] Adding default web application template project for reference. --- .gitignore | 1 + samples/WebApplication1/WebApplication1.sln | 32 ++ samples/WebApplication1/global.json | 6 + .../Preprocess/RazorPreCompilation.cs | 20 + .../Controllers/AccountController.cs | 449 ++++++++++++++++++ .../Controllers/HomeController.cs | 35 ++ .../Controllers/ManageController.cs | 366 ++++++++++++++ .../src/WebApplication1/MessageServices.cs | 23 + .../00000000000000_CreateIdentitySchema.cs | 304 ++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 160 +++++++ .../Models/AccountViewModels.cs | 102 ++++ .../WebApplication1/Models/IdentityModels.cs | 40 ++ .../Models/ManageViewModels.cs | 91 ++++ .../src/WebApplication1/Project_Readme.html | 203 ++++++++ .../WebApplication1/Properties/AppSettings.cs | 12 + .../src/WebApplication1/Startup.cs | 136 ++++++ .../Views/Account/ConfirmEmail.cshtml | 10 + .../Account/ExternalLoginConfirmation.cshtml | 35 ++ .../Views/Account/ExternalLoginFailure.cshtml | 8 + .../Views/Account/ForgotPassword.cshtml | 31 ++ .../Account/ForgotPasswordConfirmation.cshtml | 8 + .../Views/Account/Login.cshtml | 91 ++++ .../Views/Account/Register.cshtml | 42 ++ .../Views/Account/ResetPassword.cshtml | 43 ++ .../Account/ResetPasswordConfirmation.cshtml | 8 + .../Views/Account/SendCode.cshtml | 21 + .../Views/Account/VerifyCode.cshtml | 38 ++ .../WebApplication1/Views/Home/About.cshtml | 7 + .../WebApplication1/Views/Home/Contact.cshtml | 17 + .../WebApplication1/Views/Home/Index.cshtml | 98 ++++ .../Views/Manage/AddPhoneNumber.cshtml | 27 ++ .../Views/Manage/ChangePassword.cshtml | 42 ++ .../WebApplication1/Views/Manage/Index.cshtml | 80 ++++ .../Views/Manage/ManageLogins.cshtml | 54 +++ .../Views/Manage/RemoveLogin.cshtml | 35 ++ .../Views/Manage/SetPassword.cshtml | 38 ++ .../Views/Manage/VerifyPhoneNumber.cshtml | 30 ++ .../WebApplication1/Views/Shared/Error.cshtml | 6 + .../Views/Shared/_Layout.cshtml | 80 ++++ .../Views/Shared/_LoginPartial.cshtml | 20 + .../Shared/_ValidationScriptsPartial.cshtml | 14 + .../Views/_GlobalImport.cshtml | 5 + .../WebApplication1/Views/_ViewStart.cshtml | 3 + .../src/WebApplication1/WebApplication1.xproj | 19 + .../src/WebApplication1/bower.json | 12 + .../src/WebApplication1/config.json | 10 + .../src/WebApplication1/gulpfile.js | 32 ++ .../src/WebApplication1/package.json | 8 + .../src/WebApplication1/project.json | 58 +++ .../WebApplication1/wwwroot/_references.js | 9 + .../src/WebApplication1/wwwroot/css/site.css | 34 ++ .../src/WebApplication1/wwwroot/favicon.ico | Bin 0 -> 32038 bytes .../wwwroot/images/ASP-NET-Banners-01.png | Bin 0 -> 8314 bytes .../wwwroot/images/ASP-NET-Banners-02.png | Bin 0 -> 8423 bytes .../wwwroot/images/Banner-01-Azure.png | Bin 0 -> 14436 bytes .../wwwroot/images/Banner-02-VS.png | Bin 0 -> 12388 bytes 56 files changed, 3053 insertions(+) create mode 100644 samples/WebApplication1/WebApplication1.sln create mode 100644 samples/WebApplication1/global.json create mode 100644 samples/WebApplication1/src/WebApplication1/Compiler/Preprocess/RazorPreCompilation.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Controllers/AccountController.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Controllers/HomeController.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Controllers/ManageController.cs create mode 100644 samples/WebApplication1/src/WebApplication1/MessageServices.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Migrations/00000000000000_CreateIdentitySchema.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Migrations/ApplicationDbContextModelSnapshot.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Models/AccountViewModels.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Models/IdentityModels.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Models/ManageViewModels.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Project_Readme.html create mode 100644 samples/WebApplication1/src/WebApplication1/Properties/AppSettings.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Startup.cs create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/ConfirmEmail.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginConfirmation.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/ExternalLoginFailure.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPassword.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/ForgotPasswordConfirmation.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/Login.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/Register.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/ResetPassword.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/ResetPasswordConfirmation.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/SendCode.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Account/VerifyCode.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Home/About.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Home/Contact.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Home/Index.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Manage/AddPhoneNumber.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Manage/ChangePassword.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Manage/Index.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Manage/ManageLogins.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Manage/RemoveLogin.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Manage/SetPassword.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Manage/VerifyPhoneNumber.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Shared/Error.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Shared/_Layout.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Shared/_LoginPartial.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/Shared/_ValidationScriptsPartial.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/_GlobalImport.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/Views/_ViewStart.cshtml create mode 100644 samples/WebApplication1/src/WebApplication1/WebApplication1.xproj create mode 100644 samples/WebApplication1/src/WebApplication1/bower.json create mode 100644 samples/WebApplication1/src/WebApplication1/config.json create mode 100644 samples/WebApplication1/src/WebApplication1/gulpfile.js create mode 100644 samples/WebApplication1/src/WebApplication1/package.json create mode 100644 samples/WebApplication1/src/WebApplication1/project.json create mode 100644 samples/WebApplication1/src/WebApplication1/wwwroot/_references.js create mode 100644 samples/WebApplication1/src/WebApplication1/wwwroot/css/site.css create mode 100644 samples/WebApplication1/src/WebApplication1/wwwroot/favicon.ico create mode 100644 samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-01.png create mode 100644 samples/WebApplication1/src/WebApplication1/wwwroot/images/ASP-NET-Banners-02.png create mode 100644 samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-01-Azure.png create mode 100644 samples/WebApplication1/src/WebApplication1/wwwroot/images/Banner-02-VS.png 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 0000000000000000000000000000000000000000..a3a799985c43bc7309d701b2cad129023377dc71 GIT binary patch literal 32038 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 J{Ep+x{{=AZWe)%V literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..59fb923e05804028e395ac4e775fcf696b57eb0c GIT binary patch literal 14436 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 R1gwujQ%z5`RQctb{{l=|@gx8M literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c9f461137e80b7bed7a75d5f2d15d0defcfb2bfa GIT binary patch literal 12388 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 literal 0 HcmV?d00001